diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index c7e9a234c778..914fc8d513e4 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -19,6 +19,7 @@ #include "mozilla/Attributes.h" #include "mozilla/dom/PContent.h" #include "mozilla/HashFunctions.h" +#include "mozilla/HashTable.h" #include "mozilla/Logging.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" @@ -538,15 +539,6 @@ public: // Other operations. - bool MatchEntry(const char* aPrefName) - { - if (!mName || !aPrefName) { - return false; - } - - return strcmp(mName, aPrefName) == 0; - } - bool GetBoolValue(PrefValueKind aKind = PrefValueKind::User) const { MOZ_ASSERT(IsTypeBool()); @@ -955,33 +947,20 @@ private: PrefValue mUserValue; }; -class PrefEntry : public PLDHashEntryHdr +struct PrefHasher { -public: - Pref* mPref; // Note: this is never null in a live entry. + using Key = mozilla::UniquePtr; + using Lookup = const char*; - static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey) + static HashNumber hash(const Lookup& aLookup) { return HashString(aLookup); } + + static bool match(const Key& aKey, const Lookup& aLookup) { - auto entry = static_cast(aEntry); - auto prefName = static_cast(aKey); + if (!aLookup || !aKey->Name()) { + return false; + } - return entry->mPref->MatchEntry(prefName); - } - - static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey) - { - auto entry = static_cast(aEntry); - auto prefName = static_cast(aKey); - - entry->mPref = new Pref(prefName); - } - - static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry) - { - auto entry = static_cast(aEntry); - - delete entry->mPref; - entry->mPref = nullptr; + return strcmp(aLookup, aKey->Name()) == 0; } }; @@ -1301,7 +1280,9 @@ private: uintptr_t mNextAndMatchKind; }; -static PLDHashTable* gHashTable; +using PrefsHashTable = mozilla::HashSet, PrefHasher>; + +static PrefsHashTable* gHashTable; // The callback list contains all the priority callbacks followed by the // non-priority callbacks. gLastPriorityNode records where the first part ends. @@ -1349,22 +1330,16 @@ AddAccessCount(const char* aPrefName) static bool gCallbacksInProgress = false; static bool gShouldCleanupDeadNodes = false; -static PLDHashTableOps pref_HashTableOps = { - PLDHashTable::HashStringKey, PrefEntry::MatchEntry, - PLDHashTable::MoveEntryStub, PrefEntry::ClearEntry, - PrefEntry::InitEntry, -}; - class PrefsHashIter { - using Iterator = decltype(gHashTable->Iter()); + using Iterator = decltype(gHashTable->modIter()); using ElemType = Pref*; Iterator mIter; public: - explicit PrefsHashIter(PLDHashTable* aTable) - : mIter(aTable->Iter()) + explicit PrefsHashIter(PrefsHashTable* aTable) + : mIter(aTable->modIter()) { } @@ -1391,7 +1366,7 @@ public: if (mDone) { return nullptr; } - return static_cast(Iter().Get())->mPref; + return Iter().get().get(); } ElemType get() const { return const_cast(this)->get(); } @@ -1400,13 +1375,13 @@ public: operator ElemType() { return get(); } - void Remove() { Iter().Remove(); } + void Remove() { Iter().remove(); } Elem& operator++() { MOZ_ASSERT(!mDone); - Iter().Next(); - mDone = Iter().Done(); + Iter().next(); + mDone = Iter().done(); return *this; } @@ -1416,14 +1391,14 @@ public: } }; - Elem begin() { return Elem(*this, mIter.Done()); } + Elem begin() { return Elem(*this, mIter.done()); } Elem end() { return Elem(*this, true); } }; class PrefsIter { - using Iterator = decltype(gHashTable->Iter()); + using Iterator = decltype(gHashTable->iter()); using ElemType = PrefWrapper; using HashElem = PrefsHashIter::Elem; @@ -1432,7 +1407,7 @@ class PrefsIter using ElemTypeVariant = Variant; SharedPrefMap* mSharedMap; - PLDHashTable* mHashTable; + PrefsHashTable* mHashTable; PrefsHashIter mIter; ElemTypeVariant mPos; @@ -1441,7 +1416,7 @@ class PrefsIter Maybe mEntry; public: - PrefsIter(PLDHashTable* aHashTable, SharedPrefMap* aSharedMap) + PrefsIter(PrefsHashTable* aHashTable, SharedPrefMap* aSharedMap) : mSharedMap(aSharedMap) , mHashTable(aHashTable) , mIter(aHashTable) @@ -1531,9 +1506,9 @@ public: void SkipDuplicates() { - while (!mDone && (mParent.IteratingBase() - ? !!mParent.mHashTable->Search(ref().Name()) - : ref().IsTypeNone())) { + while (!mDone && + (mParent.IteratingBase() ? mParent.mHashTable->has(ref().Name()) + : ref().IsTypeNone())) { Next(); } } @@ -1621,7 +1596,7 @@ pref_savePrefs() { MOZ_ASSERT(NS_IsMainThread()); - PrefSaveData savedPrefs(gHashTable->EntryCount()); + PrefSaveData savedPrefs(gHashTable->count()); for (auto& pref : PrefsIter(gHashTable, gSharedMap)) { nsAutoCString prefValueStr; @@ -1649,25 +1624,19 @@ static bool gContentProcessPrefsAreInited = false; #endif // DEBUG -static PrefEntry* -pref_HashTableLookupInner(const char* aPrefName) +static Pref* +pref_HashTableLookup(const char* aPrefName) { MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal()); MOZ_ASSERT_IF(!XRE_IsParentProcess(), gContentProcessPrefsAreInited); - return static_cast(gHashTable->Search(aPrefName)); -} - -static Pref* -pref_HashTableLookup(const char* aPrefName) -{ - PrefEntry* entry = pref_HashTableLookupInner(aPrefName); - if (!entry) { - return nullptr; - } - - return entry->mPref; + // We use readonlyThreadsafeLookup() because we often have concurrent lookups + // from multiple Stylo threads. This is safe because those threads cannot + // modify gHashTable, and the main thread is blocked while Stylo threads are + // doing these lookups. + auto p = gHashTable->readonlyThreadsafeLookup(aPrefName); + return p ? p->get() : nullptr; } // While notifying preference callbacks, this holds the wrapper for the @@ -1718,11 +1687,11 @@ pref_LookupForModify(const char* aPrefName, return wrapper->as(); } - auto entry = static_cast(gHashTable->Add(aPrefName, fallible)); - if (!entry) { + Pref* pref = new Pref(aPrefName); + if (!gHashTable->putNew(aPrefName, pref)) { + delete pref; return Err(NS_ERROR_OUT_OF_MEMORY); } - Pref* pref = entry->mPref; pref->FromWrapper(*wrapper); return pref; } @@ -1755,15 +1724,16 @@ pref_SetPref(const char* aPrefName, } if (!pref) { - auto entry = static_cast(gHashTable->Add(aPrefName, fallible)); - if (!entry) { - return NS_ERROR_OUT_OF_MEMORY; - } - pref = entry->mPref; - - if (pref->IsTypeNone()) { - // New entry. Set the type. + auto p = gHashTable->lookupForAdd(aPrefName); + if (!p) { + pref = new Pref(aPrefName); pref->SetType(aType); + if (!gHashTable->add(p, pref)) { + delete pref; + return NS_ERROR_OUT_OF_MEMORY; + } + } else { + pref = p->get(); } } @@ -2882,16 +2852,14 @@ nsPrefBranch::DeleteBranch(const char* aStartingAt) const nsACString& branchNameNoDot = Substring(branchName, 0, branchName.Length() - 1); - for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = static_cast(iter.Get())->mPref; - + for (auto iter = gHashTable->modIter(); !iter.done(); iter.next()) { // The first disjunct matches branches: e.g. a branch name "foo.bar." // matches a name "foo.bar.baz" (but it won't match "foo.barrel.baz"). // The second disjunct matches leaf nodes: e.g. a branch name "foo.bar." // matches a name "foo.bar" (by ignoring the trailing '.'). - nsDependentCString name(pref->Name()); + nsDependentCString name(iter.get()->Name()); if (StringBeginsWith(name, branchName) || name.Equals(branchNameNoDot)) { - iter.Remove(); + iter.remove(); // The saved callback pref may be invalid now. gCallbackPref = nullptr; } @@ -2943,8 +2911,8 @@ nsPrefBranch::GetChildList(const char* aStartingAt, // back to us because if they do we are going to add mPrefRoot again. const nsCString& element = prefArray[dwIndex]; outArray[dwIndex] = - (char*) moz_xmemdup(element.get() + mPrefRoot.Length(), - element.Length() - mPrefRoot.Length() + 1); + (char*)moz_xmemdup(element.get() + mPrefRoot.Length(), + element.Length() - mPrefRoot.Length() + 1); } *aChildArray = outArray; } @@ -3535,10 +3503,9 @@ PreferenceServiceReporter::CollectReports( Preferences::AddSizeOfIncludingThis(mallocSizeOf, sizes); if (gHashTable) { - sizes.mHashTable += gHashTable->ShallowSizeOfIncludingThis(mallocSizeOf); - for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = static_cast(iter.Get())->mPref; - pref->AddSizeOfIncludingThis(mallocSizeOf, sizes); + sizes.mHashTable += gHashTable->shallowSizeOfIncludingThis(mallocSizeOf); + for (auto iter = gHashTable->iter(); !iter.done(); iter.next()) { + iter.get()->AddSizeOfIncludingThis(mallocSizeOf, sizes); } } @@ -3850,10 +3817,8 @@ Preferences::GetInstanceForService() MOZ_ASSERT(!gHashTable); gHashTable = - new PLDHashTable(&pref_HashTableOps, - sizeof(PrefEntry), - (XRE_IsParentProcess() ? kHashTableInitialLengthParent - : kHashTableInitialLengthContent)); + new PrefsHashTable(XRE_IsParentProcess() ? kHashTableInitialLengthParent + : kHashTableInitialLengthContent); gTelemetryLoadData = new nsDataHashtable(); @@ -4018,8 +3983,8 @@ Preferences::SerializePreferences(nsCString& aStr) aStr.Truncate(); - for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = static_cast(iter.Get())->mPref; + for (auto iter = gHashTable->iter(); !iter.done(); iter.next()) { + Pref* pref = iter.get().get(); if (!pref->IsTypeNone() && pref->HasAdvisablySizedValues()) { pref->SerializeAndAppend(aStr); } @@ -4061,10 +4026,8 @@ Preferences::EnsureSnapshot(size_t* aSize) if (!gSharedMap) { SharedPrefMapBuilder builder; - for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = static_cast(iter.Get())->mPref; - - pref->AddToMap(builder); + for (auto iter = gHashTable->iter(); !iter.done(); iter.next()) { + iter.get()->AddToMap(builder); } gSharedMap = new SharedPrefMap(std::move(builder)); @@ -4078,7 +4041,9 @@ Preferences::EnsureSnapshot(size_t* aSize) // we can initialize the hashtable with the expected number of per-session // changed preferences, rather than the expected total number of // preferences. - gHashTable->ClearAndPrepareForLength(kHashTableInitialLengthContent); + gHashTable->clearAndCompact(); + Unused << gHashTable->reserve(kHashTableInitialLengthContent); + gPrefNameArena.Clear(); } @@ -4197,7 +4162,9 @@ Preferences::ResetPrefs() return NS_ERROR_NOT_AVAILABLE; } - gHashTable->ClearAndPrepareForLength(kHashTableInitialLengthParent); + gHashTable->clearAndCompact(); + Unused << gHashTable->reserve(kHashTableInitialLengthParent); + gPrefNameArena.Clear(); return InitInitialObjects(/* isStartup */ false).isOk() ? NS_OK @@ -4212,8 +4179,8 @@ Preferences::ResetUserPrefs() MOZ_ASSERT(NS_IsMainThread()); Vector prefNames; - for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) { - Pref* pref = static_cast(iter.Get())->mPref; + for (auto iter = gHashTable->modIter(); !iter.done(); iter.next()) { + Pref* pref = iter.get().get(); if (pref->HasUserValue()) { if (!prefNames.append(pref->Name())) { @@ -4222,7 +4189,7 @@ Preferences::ResetUserPrefs() pref->ClearUserValue(); if (!pref->HasDefaultValue()) { - iter.Remove(); + iter.remove(); } } } @@ -4290,13 +4257,18 @@ Preferences::SetPreference(const dom::Pref& aDomPref) const char* prefName = aDomPref.name().get(); - auto entry = static_cast(gHashTable->Add(prefName, fallible)); - if (!entry) { - return; + Pref* pref; + auto p = gHashTable->lookupForAdd(prefName); + if (!p) { + pref = new Pref(prefName); + if (!gHashTable->add(p, pref)) { + delete pref; + return; + } + } else { + pref = p->get(); } - Pref* pref = entry->mPref; - bool valueChanged = false; pref->FromDomPref(aDomPref, &valueChanged); @@ -4316,7 +4288,7 @@ Preferences::SetPreference(const dom::Pref& aDomPref) if (gSharedMap->Has(pref->Name())) { pref->SetType(PrefType::None); } else { - gHashTable->RemoveEntry(entry); + gHashTable->remove(prefName); } pref = nullptr; } @@ -5269,7 +5241,7 @@ Preferences::ClearUser(const char* aPrefName) if (!pref->HasDefaultValue()) { if (!gSharedMap || !gSharedMap->Has(pref->Name())) { - gHashTable->Remove(aPrefName); + gHashTable->remove(aPrefName); } else { pref->SetType(PrefType::None); } diff --git a/modules/libpref/SharedPrefMap.h b/modules/libpref/SharedPrefMap.h index d976c72a3f14..2846a8432bcc 100644 --- a/modules/libpref/SharedPrefMap.h +++ b/modules/libpref/SharedPrefMap.h @@ -318,7 +318,8 @@ class SharedPrefMap uint8_t mIsSticky : 1; // True if the preference is locked, as defined by the preference service. uint8_t mIsLocked : 1; - // True if the preference's default value has changed since it was first set. + // True if the preference's default value has changed since it was first + // set. uint8_t mDefaultChanged : 1; };