Bug 1471025: Part 7c - Clear the dynamic hashtable in the parent process after snapshotting. r=njn draft
authorKris Maglione <maglione.k@gmail.com>
Mon, 02 Jul 2018 22:48:40 -0700
changeset 815382 93e3ae5221e046a021c56ab30588b26efa1aafa1
parent 815381 b255da769062497e19724fdcb316f5131e6b8e89
child 815383 e975dd106e05d1a8ed5ad1a5c30f5e6ee5df6525
push id115503
push usermaglione.k@gmail.com
push dateSat, 07 Jul 2018 19:50:37 +0000
reviewersnjn
bugs1471025
milestone63.0a1
Bug 1471025: Part 7c - Clear the dynamic hashtable in the parent process after snapshotting. r=njn The preference storage in the shared memory snapshot is much more compact than the dynamic hashtable, and is already mapped in memory, so there's no need to keep the full hashtable in memory in the parent process after the snapshot is created. This patch empties the hashtable and the name string arena after the snapshot. It also removes the accounting for preferences which have changed after init, since those are, by definition, exactly the set of entries in the dynamic hashtable. MozReview-Commit-ID: L6VGq2z4foH
modules/libpref/Preferences.cpp
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -463,17 +463,16 @@ class Pref
 public:
   explicit Pref(const char* aName)
     : mName(ArenaStrdup(aName, gPrefNameArena))
     , mType(static_cast<uint32_t>(PrefType::None))
     , mIsSticky(false)
     , mIsLocked(false)
     , mHasDefaultValue(false)
     , mHasUserValue(false)
-    , mHasChangedSinceInit(false)
     , mDefaultValue()
     , mUserValue()
   {
   }
 
   ~Pref()
   {
     // There's no need to free mName because it's allocated in memory owned by
@@ -495,28 +494,17 @@ public:
   bool IsTypeNone() const { return IsType(PrefType::None); }
   bool IsTypeString() const { return IsType(PrefType::String); }
   bool IsTypeInt() const { return IsType(PrefType::Int); }
   bool IsTypeBool() const { return IsType(PrefType::Bool); }
 
   // Other properties.
 
   bool IsLocked() const { return mIsLocked; }
-  void SetIsLocked(bool aValue)
-  {
-    mIsLocked = aValue;
-    OnChanged();
-  }
-
-  void OnChanged()
-  {
-    if (gSharedMap) {
-      mHasChangedSinceInit = true;
-    }
-  }
+  void SetIsLocked(bool aValue) { mIsLocked = aValue; }
 
   bool IsSticky() const { return mIsSticky; }
 
   bool HasDefaultValue() const { return mHasDefaultValue; }
   bool HasUserValue() const { return mHasUserValue; }
 
   template<typename T>
   void AddToMap(SharedPrefMapBuilder& aMap)
@@ -535,42 +523,16 @@ public:
       AddToMap<int32_t>(aMap);
     } else if (IsTypeString()) {
       AddToMap<nsDependentCString>(aMap);
     } else {
       MOZ_ASSERT_UNREACHABLE("Unexpected preference type");
     }
   }
 
-  // When a content process is created we could tell it about every pref. But
-  // the content process also initializes prefs from file, so we save a lot of
-  // IPC if we only tell it about prefs that have changed since initialization.
-  //
-  // Specifically, we send a pref if any of the following conditions are met.
-  //
-  // - If the pref has changed in any way (default value, user value, or other
-  //   attribute, such as whether it is locked) since being initialized from
-  //   file.
-  //
-  // - If the pref has a user value. (User values are more complicated than
-  //   default values, because they can be loaded from file after
-  //   initialization with Preferences::ReadUserPrefsFromFile(), so we are
-  //   conservative with them.)
-  //
-  // In other words, prefs that only have a default value and haven't changed
-  // need not be sent. One could do better with effort, but it's ok to be
-  // conservative and this still greatly reduces the number of prefs sent.
-  //
-  // Note: This function is only useful in the parent process.
-  bool MustSendToContentProcesses() const
-  {
-    MOZ_ASSERT(XRE_IsParentProcess());
-    return mHasChangedSinceInit;
-  }
-
   // Other operations.
 
   bool MatchEntry(const char* aPrefName)
   {
     if (!mName || !aPrefName) {
       return false;
     }
 
@@ -678,18 +640,16 @@ public:
         mHasUserValue = true;
         userValueChanged = true;
       }
     } else if (mHasUserValue) {
       ClearUserValue();
       userValueChanged = true;
     }
 
-    OnChanged();
-
     if (userValueChanged || (defaultValueChanged && !mHasUserValue)) {
       *aValueChanged = true;
     }
   }
 
   void FromWrapper(PrefWrapper& aWrapper);
 
   bool HasAdvisablySizedValues()
@@ -727,17 +687,16 @@ private:
               : mHasUserValue && mUserValue.Equals(aType, aValue));
   }
 
 public:
   void ClearUserValue()
   {
     mUserValue.Clear(Type());
     mHasUserValue = false;
-    OnChanged();
   }
 
   nsresult SetDefaultValue(PrefType aType,
                            PrefValue aValue,
                            bool aIsSticky,
                            bool aIsLocked,
                            bool* aValueChanged)
   {
@@ -750,17 +709,16 @@ public:
     // doing so would change the default value.
     if (!IsLocked()) {
       if (aIsLocked) {
         SetIsLocked(true);
       }
       if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
         mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
         mHasDefaultValue = true;
-        OnChanged();
         if (aIsSticky) {
           mIsSticky = true;
         }
         if (!mHasUserValue) {
           *aValueChanged = true;
         }
         // What if we change the default to be the same as the user value?
         // Should we clear the user value? Currently we don't.
@@ -793,48 +751,23 @@ public:
       }
 
       // Otherwise, should we set the user value? Only if doing so would
       // change the user value.
     } else if (!ValueMatches(PrefValueKind::User, aType, aValue)) {
       mUserValue.Replace(mHasUserValue, Type(), aType, aValue);
       SetType(aType); // needed because we may have changed the type
       mHasUserValue = true;
-      OnChanged();
       if (!IsLocked()) {
         *aValueChanged = true;
       }
     }
     return NS_OK;
   }
 
-  // Returns false if this pref doesn't have a user value worth saving.
-  bool UserValueToStringForSaving(nsCString& aStr)
-  {
-    // Should we save the user value, if present? Only if it does not match the
-    // default value, or it is sticky.
-    if (mHasUserValue &&
-        (!ValueMatches(PrefValueKind::Default, Type(), mUserValue) ||
-         mIsSticky)) {
-      if (IsTypeString()) {
-        StrEscape(mUserValue.mStringVal, aStr);
-
-      } else if (IsTypeInt()) {
-        aStr.AppendInt(mUserValue.mIntVal);
-
-      } else if (IsTypeBool()) {
-        aStr = mUserValue.mBoolVal ? "true" : "false";
-      }
-      return true;
-    }
-
-    // Do not save default prefs that haven't changed.
-    return false;
-  }
-
   // Prefs are serialized in a manner that mirrors dom::Pref. The two should be
   // kept in sync. E.g. if something is added to one it should also be added to
   // the other. (It would be nice to be able to use the code generated from
   // IPDL for serializing dom::Pref here instead of writing by hand this
   // serialization/deserialization. Unfortunately, that generated code is
   // difficult to use directly, outside of the IPDL IPC code.)
   //
   // The grammar for the serialized prefs has the following form.
@@ -1000,17 +933,16 @@ public:
 private:
   const char* mName; // allocated in gPrefNameArena
 
   uint32_t mType : 2;
   uint32_t mIsSticky : 1;
   uint32_t mIsLocked : 1;
   uint32_t mHasDefaultValue : 1;
   uint32_t mHasUserValue : 1;
-  uint32_t mHasChangedSinceInit : 1;
 
   PrefValue mDefaultValue;
   PrefValue mUserValue;
 };
 
 class PrefEntry : public PLDHashEntryHdr
 {
 public:
@@ -1157,16 +1089,40 @@ public:
   {
     PrefValueKind kind;
     MOZ_TRY_VAR(kind, WantValueKind(PrefType::String, aKind));
 
     aResult = GetStringValue(kind);
     return NS_OK;
   }
 
+  // Returns false if this pref doesn't have a user value worth saving.
+  bool UserValueToStringForSaving(nsCString& aStr)
+  {
+    // Should we save the user value, if present? Only if it does not match the
+    // default value, or it is sticky.
+    if (HasUserValue() &&
+        (!ValueMatches(PrefValueKind::Default, Type(), GetValue()) ||
+         IsSticky())) {
+      if (IsTypeString()) {
+        StrEscape(GetStringValue().get(), aStr);
+
+      } else if (IsTypeInt()) {
+        aStr.AppendInt(GetIntValue());
+
+      } else if (IsTypeBool()) {
+        aStr = GetBoolValue() ? "true" : "false";
+      }
+      return true;
+    }
+
+    // Do not save default prefs that haven't changed.
+    return false;
+  }
+
   bool Matches(PrefType aType,
                PrefValueKind aKind,
                PrefValue& aValue,
                bool aIsSticky,
                bool aIsLocked) const
   {
     return (ValueMatches(aKind, aType, aValue) && aIsSticky == IsSticky() &&
             aIsLocked == IsLocked());
@@ -1602,19 +1558,17 @@ constexpr size_t kHashTableInitialLength
 
 static PrefSaveData
 pref_savePrefs()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   PrefSaveData savedPrefs(gHashTable->EntryCount());
 
-  for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
-    Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
-
+  for (auto& pref : PrefsIter(gHashTable, gSharedMap)) {
     nsAutoCString prefValueStr;
     if (!pref->UserValueToStringForSaving(prefValueStr)) {
       continue;
     }
 
     nsAutoCString prefNameStr;
     StrEscape(pref->Name(), prefNameStr);
 
@@ -4022,17 +3976,17 @@ NS_IMPL_ISUPPORTS(Preferences,
 Preferences::SerializePreferences(nsCString& aStr)
 {
   MOZ_RELEASE_ASSERT(InitStaticMembers());
 
   aStr.Truncate();
 
   for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
     Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
-    if (pref->MustSendToContentProcesses() && pref->HasAdvisablySizedValues()) {
+    if (pref->HasAdvisablySizedValues()) {
       pref->SerializeAndAppend(aStr);
     }
   }
 
   aStr.Append('\0');
 }
 
 /* static */ void
@@ -4070,16 +4024,28 @@ Preferences::EnsureSnapshot(size_t* aSiz
 
     for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
       Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
 
       pref->AddToMap(builder);
     }
 
     gSharedMap = new SharedPrefMap(std::move(builder));
+
+    // Once we've built a snapshot of the database, there's no need to continue
+    // storing dynamic copies of the preferences it contains. Once we reset the
+    // hashtable, preference lookups will fall back to the snapshot for any
+    // preferences not in the dynamic hashtable.
+    //
+    // And since the majority of the database is now contained in the snapshot,
+    // 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);
+    gPrefNameArena.Clear();
   }
 
   *aSize = gSharedMap->MapSize();
   return gSharedMap->CloneFileDescriptor();
 }
 
 /* static */ void
 Preferences::InitSnapshot(const FileDescriptor& aHandle, size_t aSize)