|
@@ -6,6 +6,7 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
+#include <AzCore/Interface/Interface.h>
|
|
|
#include <AzCore/Name/NameDictionary.h>
|
|
|
#include <AzCore/Name/Internal/NameData.h>
|
|
|
#include <AzCore/std/hash.h>
|
|
@@ -21,99 +22,65 @@ namespace AZ
|
|
|
|
|
|
namespace NameDictionaryInternal
|
|
|
{
|
|
|
- static AZ::EnvironmentVariable<NameDictionary> s_instance = nullptr;
|
|
|
+ // Pointer which indicated that the NameDictonary associated with the AZ::Interface
|
|
|
+ // was created by the Create function below
|
|
|
+ static AZ::EnvironmentVariable<AZStd::unique_ptr<AZ::NameDictionary>> s_staticNameDictionary;
|
|
|
}
|
|
|
|
|
|
void NameDictionary::Create()
|
|
|
{
|
|
|
using namespace NameDictionaryInternal;
|
|
|
|
|
|
- AZ_Assert(!s_instance, "NameDictionary already created!");
|
|
|
-
|
|
|
- if (!s_instance)
|
|
|
+ if (AZ::Interface<AZ::NameDictionary>::Get() != nullptr)
|
|
|
{
|
|
|
- // Because the NameDictionary allocates memory using the AZ::Allocator and it is created
|
|
|
- // in the executable memory space, it's ownership cannot be transferred to other module memory spaces
|
|
|
- // Otherwise this could cause the the NameDictionary to be destroyed in static de-init
|
|
|
- // after the AZ::Allocators have been destroyed
|
|
|
- // Therefore we supply the isTransferOwnership value of false using CreateVariableEx
|
|
|
- s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
|
|
|
-
|
|
|
- // Load any deferred names stored in our module's deferred name list, if it isn't already pointing at the dictionary's list
|
|
|
- // If Name::s_staticNameBegin is equal to m_deferredHead we can skip this check, as that would make the freshly created name the only
|
|
|
- // entry in the list.
|
|
|
- if (Name::s_staticNameBegin != nullptr && &s_instance->m_deferredHead != Name::s_staticNameBegin)
|
|
|
- {
|
|
|
- s_instance->m_deferredHead.m_nextName = Name::s_staticNameBegin;
|
|
|
- Name::s_staticNameBegin->m_previousName = &s_instance->m_deferredHead;
|
|
|
- }
|
|
|
- Name* current = s_instance->m_deferredHead.m_nextName;
|
|
|
- while (current != nullptr)
|
|
|
- {
|
|
|
- current->m_linkedToDictionary = true;
|
|
|
- current = current->m_nextName;
|
|
|
- }
|
|
|
- s_instance->LoadDeferredNames(&s_instance->m_deferredHead);
|
|
|
+ AZ_Warning("NameDictionary", false, "NameDictionary already registered! Must unregister existing instance to register another one");
|
|
|
+ return;
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- void NameDictionary::Destroy()
|
|
|
- {
|
|
|
- using namespace NameDictionaryInternal;
|
|
|
|
|
|
- AZ_Assert(s_instance, "NameDictionary not created!");
|
|
|
+ auto nameDictionary = aznew NameDictionary();
|
|
|
+ AZ::Interface<AZ::NameDictionary>::Register(nameDictionary);
|
|
|
+ // Store pointer to the newly created NameDictionary
|
|
|
+ s_staticNameDictionary = AZ::Environment::CreateVariable<AZStd::unique_ptr<AZ::NameDictionary>>(NameDictionaryInstanceName, nameDictionary);
|
|
|
|
|
|
- // Unload deferred names before destroying the name dictionary
|
|
|
- // We need to do this because they may release their NameRefs, which require s_instance to be set
|
|
|
- s_instance->UnloadDeferredNames();
|
|
|
- s_instance.Reset();
|
|
|
+ // Load any deferred names stored in our module's deferred name list, if it isn't already pointing at the dictionary's list
|
|
|
+ // If Name::s_staticNameBegin is equal to m_deferredHead we can skip this check, as that would make the freshly created name the only
|
|
|
+ // entry in the list.
|
|
|
+ nameDictionary->LoadDeferredNames(AZ::Name::GetDeferredHead());
|
|
|
}
|
|
|
|
|
|
- bool NameDictionary::IsReady(bool tryCreate)
|
|
|
+ void NameDictionary::Destroy()
|
|
|
{
|
|
|
using namespace NameDictionaryInternal;
|
|
|
|
|
|
- if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
|
|
|
+ if (s_staticNameDictionary && *s_staticNameDictionary)
|
|
|
{
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!s_instance)
|
|
|
- {
|
|
|
- if (tryCreate)
|
|
|
- {
|
|
|
- // Because the NameDictionary allocates memory using the AZ::Allocator and it is created
|
|
|
- // in the executable memory space, it's ownership cannot be transferred to other module memory spaces
|
|
|
- // Otherwise this could cause the the NameDictionary to be destroyed in static de-init
|
|
|
- // after the AZ::Allocators have been destroyed
|
|
|
- // Therefore we supply the isTransferOwnership value of false using CreateVariableEx
|
|
|
- s_instance = AZ::Environment::CreateVariableEx<NameDictionary>(NameDictionaryInstanceName, true, false);
|
|
|
- }
|
|
|
- else
|
|
|
+ // If the static name dictionary is registered with the AZ::Interface<AZ::NameDictionary> unregister it
|
|
|
+ if (AZ::Interface<AZ::NameDictionary>::Get() == s_staticNameDictionary->get())
|
|
|
{
|
|
|
- return false;
|
|
|
+ AZ::Interface<AZ::NameDictionary>::Unregister(s_staticNameDictionary->get());
|
|
|
}
|
|
|
+
|
|
|
+ s_staticNameDictionary = {};
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- return s_instance.IsConstructed();
|
|
|
+ bool NameDictionary::IsReady()
|
|
|
+ {
|
|
|
+ return AZ::Interface<AZ::NameDictionary>::Get();
|
|
|
}
|
|
|
|
|
|
NameDictionary& NameDictionary::Instance()
|
|
|
{
|
|
|
- using namespace NameDictionaryInternal;
|
|
|
-
|
|
|
- if (!s_instance)
|
|
|
- {
|
|
|
- s_instance = Environment::FindVariable<NameDictionary>(NameDictionaryInstanceName);
|
|
|
- }
|
|
|
-
|
|
|
- AZ_Assert(s_instance.IsConstructed(), "NameDictionary has not been initialized yet.");
|
|
|
+ auto nameDictionary = AZ::Interface<AZ::NameDictionary>::Get();
|
|
|
+ AZ_Assert(nameDictionary != nullptr, "A NameDictionary has not been globally registered with the AZ::Interface<AZ::NameDictionary>.");
|
|
|
|
|
|
- return *s_instance;
|
|
|
+ return *nameDictionary;
|
|
|
}
|
|
|
|
|
|
+ // The deferred head passes in a nullptr NameDictionary as the nameDictionary isn't available
|
|
|
+ // until the constructor completes
|
|
|
NameDictionary::NameDictionary()
|
|
|
- : m_deferredHead(Name::FromStringLiteral("-fixed name dictionary deferred head-"))
|
|
|
+ : m_deferredHead(Name::FromStringLiteral("-fixed name dictionary deferred head-", nullptr))
|
|
|
{
|
|
|
// Ensure a Name that is valid for the life-cycle of this dictionary is the head of our literal linked list
|
|
|
// This prevents our list head from being destroyed from a module that has shut down its AZ::Environment and
|
|
@@ -123,6 +90,9 @@ namespace AZ
|
|
|
|
|
|
NameDictionary::~NameDictionary()
|
|
|
{
|
|
|
+ // Unload deferred names
|
|
|
+ UnloadDeferredNames();
|
|
|
+
|
|
|
// Ensure our module's static name list is up-to-date with what's in our deferred list.
|
|
|
// This allows the NameDictionary to be recreated and restore and name literals that still exist
|
|
|
// in e.g. unit tests.
|
|
@@ -132,9 +102,8 @@ namespace AZ
|
|
|
|
|
|
for (const auto& keyValue : m_dictionary)
|
|
|
{
|
|
|
- Internal::NameData* nameData = keyValue.second;
|
|
|
- const int useCount = keyValue.second->m_useCount;
|
|
|
- [[maybe_unused]] const bool hadCollision = keyValue.second->m_hashCollision;
|
|
|
+ Internal::NameData* nameData = keyValue.second.m_nameData;
|
|
|
+ const int useCount = nameData->m_useCount;
|
|
|
|
|
|
if (useCount == 0)
|
|
|
{
|
|
@@ -143,7 +112,7 @@ namespace AZ
|
|
|
else
|
|
|
{
|
|
|
leaksDetected = true;
|
|
|
- AZ_TracePrintf("NameDictionary", "\tLeaked Name [%3d reference(s)]: hash 0x%08X, '%.*s'\n", useCount, keyValue.first, AZ_STRING_ARG(keyValue.second->GetName()));
|
|
|
+ AZ_TracePrintf("NameDictionary", "\tLeaked Name [%3d reference(s)]: hash 0x%08X, '%.*s'\n", useCount, keyValue.first, AZ_STRING_ARG(nameData->GetName()));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -156,7 +125,7 @@ namespace AZ
|
|
|
auto iter = m_dictionary.find(hash);
|
|
|
if (iter != m_dictionary.end())
|
|
|
{
|
|
|
- return Name(iter->second);
|
|
|
+ return Name(iter->second.m_nameData);
|
|
|
}
|
|
|
return Name();
|
|
|
}
|
|
@@ -193,7 +162,6 @@ namespace AZ
|
|
|
{
|
|
|
m_deferredHead.m_nextName = &deferredName;
|
|
|
deferredName.m_previousName = &m_deferredHead;
|
|
|
- deferredName.m_linkedToDictionary = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -263,19 +231,20 @@ namespace AZ
|
|
|
{
|
|
|
Internal::NameData* nameData = aznew Internal::NameData(nameString, hash);
|
|
|
nameData->m_hashCollision = collisionDetected;
|
|
|
- m_dictionary.emplace(hash, nameData);
|
|
|
+ // Piecewise construct to prevent creating a temporary ScopedNameDataWrapper that destructs
|
|
|
+ m_dictionary.emplace(AZStd::piecewise_construct, AZStd::forward_as_tuple(hash), AZStd::forward_as_tuple(*this, nameData));
|
|
|
return Name(nameData);
|
|
|
}
|
|
|
// Found the desired entry, return it
|
|
|
- else if (iter->second->GetName() == nameString)
|
|
|
+ else if (iter->second.m_nameData->GetName() == nameString)
|
|
|
{
|
|
|
- return Name(iter->second);
|
|
|
+ return Name(iter->second.m_nameData);
|
|
|
}
|
|
|
// Hash collision, try a new hash
|
|
|
else
|
|
|
{
|
|
|
collisionDetected = true;
|
|
|
- iter->second->m_hashCollision = true; // Make sure the existing entry is flagged as colliding too
|
|
|
+ iter->second.m_nameData->m_hashCollision = true; // Make sure the existing entry is flagged as colliding too
|
|
|
++hash;
|
|
|
iter = m_dictionary.find(hash);
|
|
|
}
|
|
@@ -310,7 +279,7 @@ namespace AZ
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Internal::NameData* nameData = dictIt->second;
|
|
|
+ Internal::NameData* nameData = dictIt->second.m_nameData;
|
|
|
|
|
|
// Check m_hashCollision inside the m_sharedMutex because a new collision could have happened
|
|
|
// on another thread before taking the lock.
|
|
@@ -350,26 +319,27 @@ namespace AZ
|
|
|
|
|
|
for (auto& iter : m_dictionary)
|
|
|
{
|
|
|
- const size_t nameLength = iter.second->m_name.size();
|
|
|
+ Internal::NameData* nameData = iter.second.m_nameData;
|
|
|
+ const size_t nameLength = nameData->m_name.size();
|
|
|
actualStringMemoryUsed += nameLength;
|
|
|
- potentialStringMemoryUsed += (nameLength * iter.second->m_useCount);
|
|
|
+ potentialStringMemoryUsed += (nameLength * nameData->m_useCount);
|
|
|
|
|
|
if (!longestName || longestName->m_name.size() < nameLength)
|
|
|
{
|
|
|
- longestName = iter.second;
|
|
|
+ longestName = nameData;
|
|
|
}
|
|
|
|
|
|
if (!mostRepeatedName)
|
|
|
{
|
|
|
- mostRepeatedName = iter.second;
|
|
|
+ mostRepeatedName = nameData;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
const size_t mostIndividualSavings = mostRepeatedName->m_name.size() * (mostRepeatedName->m_useCount - 1);
|
|
|
- const size_t currentIndividualSavings = nameLength * (iter.second->m_useCount - 1);
|
|
|
+ const size_t currentIndividualSavings = nameLength * (nameData->m_useCount - 1);
|
|
|
if (currentIndividualSavings > mostIndividualSavings)
|
|
|
{
|
|
|
- mostRepeatedName = iter.second;
|
|
|
+ mostRepeatedName = nameData;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -405,4 +375,32 @@ namespace AZ
|
|
|
const uint32_t hash = AZStd::hash<AZStd::string_view>()(name) & 0xFFFFFFFF;
|
|
|
return hash;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ // NameDictionary::ScopedNameDataWrapper RAII implementation
|
|
|
+ NameDictionary::ScopedNameDataWrapper::ScopedNameDataWrapper(NameDictionary& nameDictionary, Internal::NameData* nameData)
|
|
|
+ : m_nameData(nameData)
|
|
|
+ , m_nameDictionary(nameDictionary)
|
|
|
+ {
|
|
|
+ if (m_nameData->m_nameDictionary == nullptr)
|
|
|
+ {
|
|
|
+ m_nameData->m_nameDictionary = &m_nameDictionary;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ NameDictionary::ScopedNameDataWrapper::ScopedNameDataWrapper(ScopedNameDataWrapper&& other)
|
|
|
+ : m_nameData(other.m_nameData)
|
|
|
+ , m_nameDictionary(other.m_nameDictionary)
|
|
|
+ {
|
|
|
+ // Reset to nullptr
|
|
|
+ other.m_nameData = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ NameDictionary::ScopedNameDataWrapper::~ScopedNameDataWrapper()
|
|
|
+ {
|
|
|
+ if (m_nameData->m_nameDictionary == &m_nameDictionary)
|
|
|
+ {
|
|
|
+ m_nameData->m_nameDictionary = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|