Bug 1333799: MinTabSelector for process selection. r=mrbkap a=gchang
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Tue, 07 Mar 2017 20:00:28 +0100
changeset 375147 8ee6ae077b46884e0a623c486dcb4751546522cc
parent 375146 eea804e64a419e5b59157c565d71418a2ff6df33
child 375148 1d8e1baa9f98fdd38142371aa091a3477ef5700c
push id10881
push usercbook@mozilla.com
push dateFri, 10 Mar 2017 11:25:35 +0000
treeherdermozilla-aurora@1d8e1baa9f98 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, gchang
bugs1333799
milestone54.0a2
Bug 1333799: MinTabSelector for process selection. r=mrbkap a=gchang
dom/base/ProcessSelector.js
dom/base/ProcessSelector.manifest
dom/interfaces/base/nsIContentProcess.idl
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/ContentProcessManager.cpp
dom/ipc/ContentProcessManager.h
--- a/dom/base/ProcessSelector.js
+++ b/dom/base/ProcessSelector.js
@@ -53,10 +53,41 @@ RandomSelector.prototype = {
 
       curIdx = (curIdx + 1) % maxContentParents;
     } while (curIdx !== startIdx);
 
     return Ci.nsIContentProcessProvider.NEW_PROCESS;
   },
 };
 
-var components = [RandomSelector];
+// Fills up aProcesses until max and then selects one from the available
+// ones that host the least number of tabs.
+function MinTabSelector() {
+}
+
+MinTabSelector.prototype = {
+  classID:          Components.ID("{2dc08eaf-6eef-4394-b1df-a3a927c1290b}"),
+  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
+
+  provideProcess(aType, aOpener, aProcesses, aCount) {
+    let maxContentParents = getMaxContentParents(aType);
+    if (aCount < maxContentParents) {
+      return Ci.nsIContentProcessProvider.NEW_PROCESS;
+    }
+
+    let min = Number.MAX_VALUE;
+    let candidate = Ci.nsIContentProcessProvider.NEW_PROCESS;
+
+    for (let i = 0; i < maxContentParents; i++) {
+      let process = aProcesses[i];
+      let tabCount = process.tabCount;
+      if (process.opener === aOpener && tabCount < min) {
+        min = tabCount;
+        candidate = i;
+      }
+    }
+
+    return candidate;
+  },
+};
+
+var components = [RandomSelector, MinTabSelector];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
--- a/dom/base/ProcessSelector.manifest
+++ b/dom/base/ProcessSelector.manifest
@@ -1,2 +1,3 @@
 component {c616fcfd-9737-41f1-aa74-cee72a38f91b} ProcessSelector.js
-contract @mozilla.org/ipc/processselector;1 {c616fcfd-9737-41f1-aa74-cee72a38f91b}
+component {2dc08eaf-6eef-4394-b1df-a3a927c1290b} ProcessSelector.js
+contract @mozilla.org/ipc/processselector;1 {2dc08eaf-6eef-4394-b1df-a3a927c1290b}
--- a/dom/interfaces/base/nsIContentProcess.idl
+++ b/dom/interfaces/base/nsIContentProcess.idl
@@ -23,16 +23,21 @@ interface nsIContentProcessInfo : nsISup
   readonly attribute int32_t processId;
 
   /**
    * This content process's opener.
    */
   readonly attribute nsIContentProcessInfo opener;
 
   /**
+   * Number of opened tabs living in this content process.
+   */
+  readonly attribute int32_t tabCount;
+
+  /**
    * The process manager for this ContentParent (so a process message manager
    * as opposed to a frame message manager.
    */
   readonly attribute nsIMessageSender messageManager;
 };
 
 [scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)]
 interface nsIContentProcessProvider : nsISupports
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -501,16 +501,29 @@ ScriptableCPInfo::GetOpener(nsIContentPr
   if (ContentParent* opener = mContentParent->Opener()) {
     nsCOMPtr<nsIContentProcessInfo> info = opener->ScriptableHelper();
     info.forget(aInfo);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+ScriptableCPInfo::GetTabCount(int32_t* aTabCount)
+{
+  if (!mContentParent) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  *aTabCount = cpm->GetTabParentCountByProcessId(mContentParent->ChildID());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger)
 {
   *aMessenger = nullptr;
   if (!mContentParent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   nsCOMPtr<nsIMessageSender> manager = mContentParent->GetMessageManager();
@@ -752,33 +765,38 @@ ContentParent::ReleaseCachedProcesses()
     // Make sure we don't select this process for new tabs.
     cp->MarkAsDead();
     // Make sure that this process is no longer accessible from JS by its message manager.
     cp->ShutDownMessageManager();
   }
 }
 
 /*static*/ already_AddRefed<ContentParent>
-ContentParent::RandomSelect(const nsTArray<ContentParent*>& aContentParents,
+ContentParent::MinTabSelect(const nsTArray<ContentParent*>& aContentParents,
                             ContentParent* aOpener, int32_t aMaxContentParents)
 {
   uint32_t maxSelectable = std::min(static_cast<uint32_t>(aContentParents.Length()),
                                     static_cast<uint32_t>(aMaxContentParents));
-  uint32_t startIdx = rand() % maxSelectable;
-  uint32_t currIdx = startIdx;
-  do {
-    RefPtr<ContentParent> p = aContentParents[currIdx];
+  uint32_t min = INT_MAX;
+  RefPtr<ContentParent> candidate;
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+  for (uint32_t i = 0; i < maxSelectable; i++) {
+    ContentParent* p = aContentParents[i];
     NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
     if (p->mOpener == aOpener) {
-      return p.forget();
+      uint32_t tabCount = cpm->GetTabParentCountByProcessId(p->ChildID());
+      if (tabCount < min) {
+        candidate = p;
+        min = tabCount;
+      }
     }
-    currIdx = (currIdx + 1) % maxSelectable;
-  } while (currIdx != startIdx);
-
-  return nullptr;
+  }
+
+  return candidate.forget();
 }
 
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
                                           ProcessPriority aPriority,
                                           ContentParent* aOpener)
 {
   nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
@@ -811,17 +829,17 @@ ContentParent::GetNewOrUsedBrowserProces
         return retval.forget();
       }
     } else {
       // If there was a problem with the JS chooser, fall back to a random
       // selection.
       NS_WARNING("nsIContentProcessProvider failed to return a process");
       RefPtr<ContentParent> random;
       if (contentParents.Length() >= maxContentParents &&
-          (random = RandomSelect(contentParents, aOpener, maxContentParents))) {
+          (random = MinTabSelect(contentParents, aOpener, maxContentParents))) {
         return random.forget();
       }
     }
 
     // Try to take the preallocated process only for the default process type.
     RefPtr<ContentParent> p;
     if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) &&
         (p = PreallocatedProcessManager::Take())) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -154,17 +154,17 @@ public:
   static void ReleaseCachedProcesses();
 
   /**
    * Picks a random content parent from |aContentParents| with a given |aOpener|
    * respecting the index limit set by |aMaxContentParents|.
    * Returns null if non available.
    */
   static already_AddRefed<ContentParent>
-  RandomSelect(const nsTArray<ContentParent*>& aContentParents,
+  MinTabSelect(const nsTArray<ContentParent*>& aContentParents,
                ContentParent* aOpener,
                int32_t maxContentParents);
 
   /**
    * Get or create a content process for:
    * 1. browser iframe
    * 2. remote xul <browser>
    * 3. normal iframe
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -349,10 +349,23 @@ ContentProcessManager::GetTabParentsByPr
       remoteFrameIter != iter->second.mRemoteFrames.end();
       ++remoteFrameIter) {
     tabIdList.AppendElement(remoteFrameIter->first);
   }
 
   return Move(tabIdList);
 }
 
+uint32_t
+ContentProcessManager::GetTabParentCountByProcessId(const ContentParentId& aChildCpId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto iter = mContentParentMap.find(aChildCpId);
+  if (NS_WARN_IF(iter == mContentParentMap.end())) {
+    return 0;
+  }
+
+  return iter->second.mRemoteFrames.size();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -113,16 +113,23 @@ public:
   /**
    * Get all TabParents' Ids managed by the givent content process.
    * Return empty array when TabParent couldn't be found via aChildCpId
    */
   nsTArray<TabId>
   GetTabParentsByProcessId(const ContentParentId& aChildCpId);
 
   /**
+   * Get the number of TabParents managed by the givent content process.
+   * Return 0 when TabParent couldn't be found via aChildCpId.
+   */
+  uint32_t
+  GetTabParentCountByProcessId(const ContentParentId& aChildCpId);
+
+  /**
    * Get the TabParent by the given content process and tab id.
    * Return nullptr when TabParent couldn't be found via aChildCpId
    * and aChildTabId.
    * (or probably because the TabParent is not in the chrome process)
    */
   already_AddRefed<TabParent>
   GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
                                 const TabId& aChildTabId);