Bug 1470365: Part 3 - Use shared memory for StringBundles loaded in the content process. r=erahm,smaug
authorKris Maglione <maglione.k@gmail.com>
Fri, 29 Jun 2018 22:53:12 -0700
changeset 424574 cc4d3499cbad3391d6831c48fe3147f39c9f5f3e
parent 424573 86fad941c4d4950935568607e66809bbed364b3d
child 424575 07535136756a8fe7d2671a40e1761d580d26b6bb
push id34217
push userrgurzau@mozilla.com
push dateSun, 01 Jul 2018 21:50:17 +0000
treeherdermozilla-central@3cfc35010196 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm, smaug
bugs1470365
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1470365: Part 3 - Use shared memory for StringBundles loaded in the content process. r=erahm,smaug MozReview-Commit-ID: LunnQyndnBf
dom/base/nsContentUtils.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
intl/strres/nsIStringBundle.idl
intl/strres/nsStringBundle.cpp
intl/strres/nsStringBundleService.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3957,16 +3957,28 @@ nsContentUtils::EnsureStringBundle(Prope
   }
   return NS_OK;
 }
 
 /* static */
 void
 nsContentUtils::AsyncPrecreateStringBundles()
 {
+  // We only ever want to pre-create bundles in the parent process.
+  //
+  // All nsContentUtils bundles are shared between the parent and child
+  // precesses, and the shared memory regions that back them *must* be created
+  // in the parent, and then sent to all children.
+  //
+  // If we attempt to create a bundle in the child before its memory region is
+  // available, we need to create a temporary non-shared bundle, and later
+  // replace that with the shared memory copy. So attempting to pre-load in the
+  // child is wasteful and unnecessary.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
   for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT; ++bundleIndex) {
     nsresult rv = NS_IdleDispatchToCurrentThread(
       NS_NewRunnableFunction("AsyncPrecreateStringBundles",
                              [bundleIndex]() {
                                PropertiesFile file = static_cast<PropertiesFile>(bundleIndex);
                                EnsureStringBundle(file);
                                nsIStringBundle *bundle = sStringBundles[file];
                                bundle->AsyncPreload();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -80,16 +80,17 @@
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "mozilla/HangDetails.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 #include "NullPrincipal.h"
 #include "nsISimpleEnumerator.h"
+#include "nsIStringBundle.h"
 #include "nsIWorkerDebuggerManager.h"
 
 #if !defined(XP_WIN)
 #include "mozilla/Omnijar.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ChildProfilerController.h"
@@ -2257,21 +2258,16 @@ ContentChild::RecvRegisterChrome(Infalli
                                  const nsCString& locale,
                                  const bool& reset)
 {
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryContent* chromeRegistry =
     static_cast<nsChromeRegistryContent*>(registrySvc.get());
   chromeRegistry->RegisterRemoteChrome(packages, resources, overrides,
                                        locale, reset);
-  static bool preloadDone = false;
-  if (!preloadDone) {
-    preloadDone = true;
-    nsContentUtils::AsyncPrecreateStringBundles();
-  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item)
 {
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryContent* chromeRegistry =
@@ -2532,16 +2528,30 @@ ContentChild::RecvAsyncMessage(const nsS
     ipc::UnpackClonedMessageDataForChild(aData, data);
     cpm->ReceiveMessage(cpm, nullptr, aMsg, false, &data, &cpows, aPrincipal, nullptr,
                         IgnoreErrors());
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentChild::RecvRegisterStringBundles(nsTArray<mozilla::dom::StringBundleDescriptor>&& aDescriptors)
+{
+  nsCOMPtr<nsIStringBundleService> stringBundleService =
+    services::GetStringBundleService();
+
+  for (auto& descriptor : aDescriptors) {
+    stringBundleService->RegisterContentBundle(descriptor.bundleURL(), descriptor.mapFile(),
+                                               descriptor.mapSize());
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentChild::RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition)
 {
   nsCOMPtr<nsIGeolocationUpdate> gs =
     do_GetService("@mozilla.org/geolocation/service;1");
   if (!gs) {
     return IPC_OK();
   }
   gs->Update(aPosition);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -389,16 +389,18 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvLoadProcessScript(const nsString& aURL) override;
 
   virtual mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
                                                    InfallibleTArray<CpowEntry>&& aCpows,
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData) override;
 
+  mozilla::ipc::IPCResult RecvRegisterStringBundles(nsTArray<StringBundleDescriptor>&& stringBundles) override;
+
   virtual mozilla::ipc::IPCResult RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition) override;
 
   virtual mozilla::ipc::IPCResult RecvGeolocationError(const uint16_t& errorCode) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateFontList(InfallibleTArray<SystemFontListEntry>&& aFontList) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -141,16 +141,17 @@
 #include "nsIParentChannel.h"
 #include "nsIPresShell.h"
 #include "nsIRemoteWindowContext.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISiteSecurityService.h"
 #include "nsISound.h"
 #include "nsISpellChecker.h"
+#include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
 #include "nsITimer.h"
 #include "nsIURIFixup.h"
 #include "nsIURL.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIWindowWatcher.h"
@@ -1272,16 +1273,27 @@ ContentParent::GetAllEvenIfDead(nsTArray
 {
   aArray.Clear();
 
   for (auto* cp : AllProcesses(eAll)) {
     aArray.AppendElement(cp);
   }
 }
 
+void
+ContentParent::BroadcastStringBundle(const StringBundleDescriptor& aBundle)
+{
+  AutoTArray<StringBundleDescriptor, 1> array;
+  array.AppendElement(aBundle);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    Unused << cp->SendRegisterStringBundles(array);
+  }
+}
+
 const nsAString&
 ContentParent::GetRemoteType() const
 {
   return mRemoteType;
 }
 
 void
 ContentParent::Init()
@@ -2312,16 +2324,20 @@ ContentParent::InitInternal(ProcessPrior
   Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache,
                                           fontList);
 
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryChrome* chromeRegistry =
     static_cast<nsChromeRegistryChrome*>(registrySvc.get());
   chromeRegistry->SendRegisteredChrome(this);
 
+  nsCOMPtr<nsIStringBundleService> stringBundleService =
+    services::GetStringBundleService();
+  stringBundleService->SendContentBundles(this);
+
   if (gAppData) {
     nsCString version(gAppData->version);
     nsCString buildID(gAppData->buildID);
     nsCString name(gAppData->name);
     nsCString UAName(gAppData->UAName);
     nsCString ID(gAppData->ID);
     nsCString vendor(gAppData->vendor);
     nsCString sourceURL(gAppData->sourceURL);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -199,16 +199,18 @@ public:
                 ContentParent* aOpenerContentParent,
                 TabParent* aSameTabGroupAs,
                 uint64_t aNextTabParentId);
 
   static void GetAll(nsTArray<ContentParent*>& aArray);
 
   static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
+  static void BroadcastStringBundle(const StringBundleDescriptor&);
+
   const nsAString& GetRemoteType() const;
 
   virtual void DoGetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const override
   {
     aRemoteType = GetRemoteType();
   }
 
   enum CPIteratorPolicy {
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -284,16 +284,23 @@ struct XPCOMInitData
     ContentDeviceData contentDeviceData;
     GfxInfoFeatureStatus[] gfxFeatureStatus;
     DataStorageEntry[] dataStorage;
     nsCString[] appLocales;
     nsCString[] requestedLocales;
     DynamicScalarDefinition[] dynamicScalarDefs;
 };
 
+struct StringBundleDescriptor
+{
+    nsCString bundleURL;
+    FileDescriptor mapFile;
+    uint32_t mapSize;
+};
+
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
 {
     manages PBrowser;
@@ -452,16 +459,18 @@ child:
 
     async UpdateFontList(SystemFontListEntry[] fontList);
 
     async UpdateAppLocales(nsCString[] appLocales);
     async UpdateRequestedLocales(nsCString[] requestedLocales);
 
     async ClearSiteDataReloadNeeded(nsString origin);
 
+    async RegisterStringBundles(StringBundleDescriptor[] stringBundles);
+
     // nsIPermissionManager messages
     async AddPermission(Permission permission);
 
     async FlushMemory(nsString reason);
 
     async GarbageCollect();
     async CycleCollect();
     async UnlinkGhosts();
--- a/intl/strres/nsIStringBundle.idl
+++ b/intl/strres/nsIStringBundle.idl
@@ -4,16 +4,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsISimpleEnumerator.idl"
 
 %{C++
 #include "mozilla/MemoryReporting.h"
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+namespace ipc {
+class FileDescriptor;
+}
+}
+
 // Define Contractid and CID
 // {D85A17C1-AA7C-11d2-9B8C-00805F8A16D9}
 #define NS_STRINGBUNDLESERVICE_CID \
 { 0xd85a17c1, 0xaa7c, 0x11d2, \
   { 0x9b, 0x8c, 0x0, 0x80, 0x5f, 0x8a, 0x16, 0xd9 } }
 
 #define NS_STRINGBUNDLE_CONTRACTID "@mozilla.org/intl/stringbundle;1"
 
@@ -94,10 +103,16 @@ interface nsIStringBundleService : nsISu
    * because any bundles that are floating around when the locale changes
    * will suddenly contain bad data
    *
    */
   void flushBundles();
 
   %{C++
     virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
+  virtual void SendContentBundles(mozilla::dom::ContentParent* aContentParent) const = 0;
+
+  virtual void RegisterContentBundle(const nsCString& aBundleURL,
+                                     const mozilla::ipc::FileDescriptor& aMapFile,
+                                     size_t aMapSize) = 0;
   %}
 };
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -27,26 +27,29 @@
 #include "nsContentUtils.h"
 #include "nsPersistentProperties.h"
 #include "nsQueryObject.h"
 #include "nsStringStream.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/URLPreloader.h"
 #include "mozilla/ResultExtensions.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ipc/SharedStringMap.h"
 
 // for async loading
 #ifdef ASYNC_LOADING
 #include "nsIBinaryInputStream.h"
 #include "nsIStringStream.h"
 #endif
 
 using namespace mozilla;
 
+using mozilla::dom::ContentParent;
+using mozilla::dom::StringBundleDescriptor;
 using mozilla::dom::ipc::SharedStringMap;
 using mozilla::dom::ipc::SharedStringMapBuilder;
 using mozilla::ipc::FileDescriptor;
 
 static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
 
 /**
  * A set of string bundle URLs which are loaded by content processes, and
@@ -218,16 +221,27 @@ public:
     if (mMapFile.isSome()) {
       return mMapSize;
     }
     return mStringMap->MapSize();
   }
 
   bool Initialized() const { return mStringMap || mMapFile.isSome(); }
 
+  StringBundleDescriptor GetDescriptor() const
+  {
+    MOZ_ASSERT(Initialized());
+
+    StringBundleDescriptor descriptor;
+    descriptor.bundleURL() = BundleURL();
+    descriptor.mapFile() = CloneFileDescriptor();
+    descriptor.mapSize() = MapSize();
+    return descriptor;
+  }
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
   static SharedStringBundle* Cast(nsIStringBundle* aStringBundle)
   {
     return static_cast<SharedStringBundle*>(aStringBundle);
   }
 
 protected:
@@ -457,16 +471,19 @@ SharedStringBundle::LoadProperties()
     nsString value;
     MOZ_TRY(elem->GetKey(key));
     MOZ_TRY(elem->GetValue(value));
 
     builder.Add(key, value);
   }
 
   mStringMap = new SharedStringMap(std::move(builder));
+
+  ContentParent::BroadcastStringBundle(GetDescriptor());
+
   return NS_OK;
 }
 
 void
 SharedStringBundle::SetMapFile(const FileDescriptor& aFile, size_t aSize)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
   mStringMap = nullptr;
@@ -994,16 +1011,62 @@ nsStringBundleService::flushBundleCache(
 NS_IMETHODIMP
 nsStringBundleService::FlushBundles()
 {
   flushBundleCache();
   return NS_OK;
 }
 
 void
+nsStringBundleService::SendContentBundles(ContentParent* aContentParent) const
+{
+  nsTArray<StringBundleDescriptor> bundles;
+
+  for (auto* entry : mSharedBundles) {
+    auto bundle = SharedStringBundle::Cast(entry->mBundle);
+
+    if (bundle->Initialized()) {
+      bundles.AppendElement(bundle->GetDescriptor());
+    }
+  }
+
+  Unused << aContentParent->SendRegisterStringBundles(std::move(bundles));
+}
+
+void
+nsStringBundleService::RegisterContentBundle(const nsCString& aBundleURL,
+                                             const FileDescriptor& aMapFile,
+                                             size_t aMapSize)
+{
+  RefPtr<StringBundleProxy> proxy;
+
+  bundleCacheEntry_t* cacheEntry = mBundleMap.Get(aBundleURL);
+  if (cacheEntry) {
+    if (RefPtr<SharedStringBundle> shared = do_QueryObject(cacheEntry->mBundle)) {
+      return;
+    }
+
+    proxy = do_QueryObject(cacheEntry->mBundle);
+    MOZ_ASSERT(proxy);
+    cacheEntry->remove();
+    delete cacheEntry;
+  }
+
+  auto bundle = MakeRefPtr<SharedStringBundle>(aBundleURL.get(), mOverrideStrings);
+  bundle->SetMapFile(aMapFile, aMapSize);
+
+  if (proxy) {
+    proxy->Retarget(bundle);
+  }
+
+  cacheEntry = insertIntoCache(bundle.forget(), aBundleURL);
+  mSharedBundles.insertBack(cacheEntry);
+}
+
+void
 nsStringBundleService::getStringBundle(const char *aURLSpec,
                                        nsIStringBundle **aResult)
 {
   nsDependentCString key(aURLSpec);
   bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key);
 
   RefPtr<SharedStringBundle> shared;
 
@@ -1058,17 +1121,17 @@ nsStringBundleService::getStringBundle(c
 
   // finally, return the value
   *aResult = cacheEntry->mBundle;
   NS_ADDREF(*aResult);
 }
 
 bundleCacheEntry_t *
 nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                       const nsCString &aHashKey)
+                                       const nsACString& aHashKey)
 {
   bundleCacheEntry_t *cacheEntry;
 
   if (mBundleMap.Count() < MAX_CACHED_BUNDLES ||
       mBundleCache.isEmpty()) {
     // cache not full - create a new entry
     cacheEntry = new bundleCacheEntry_t();
   } else {
--- a/intl/strres/nsStringBundleService.h
+++ b/intl/strres/nsStringBundleService.h
@@ -46,28 +46,34 @@ public:
       "explicit/string-bundle-service", KIND_HEAP, UNITS_BYTES,
       amt,
       "Memory used for StringBundleService bundles");
     return NS_OK;
   };
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
+  void SendContentBundles(mozilla::dom::ContentParent* aContentParent) const override;
+
+  void RegisterContentBundle(const nsCString& aBundleURL,
+                             const mozilla::ipc::FileDescriptor& aMapFile,
+                             size_t aMapSize) override;
+
 private:
   virtual ~nsStringBundleService();
 
   void getStringBundle(const char *aUrl, nsIStringBundle** aResult);
   nsresult FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
                             uint32_t argCount, char16_t** argArray,
                             nsAString& result);
 
   void flushBundleCache();
 
-  bundleCacheEntry_t *insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                      const nsCString &aHashKey);
+  bundleCacheEntry_t* insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
+                                      const nsACString &aHashKey);
 
   nsDataHashtable<nsCStringHashKey, bundleCacheEntry_t*> mBundleMap;
   mozilla::LinkedList<bundleCacheEntry_t> mBundleCache;
   mozilla::AutoCleanLinkedList<bundleCacheEntry_t> mSharedBundles;
 
   nsCOMPtr<nsIErrorService> mErrorService;
   nsCOMPtr<nsIStringBundleOverride> mOverrideStrings;
 };