Bug 797239 - Defer scheduling prelaunch app until child goes idle. r=cjones, a=blocking-basecamp
authorDave Hylands <dhylands@mozilla.com>
Mon, 08 Oct 2012 22:46:19 -0600
changeset 116037 90a5685dc1be98a1314cc6a4129083b22eed2142
parent 116036 85aa1352aba7896605f2e46fdd28e0ffd4edb925
child 116038 adfd438b56ffd83ce7af0c1eff560ee495b4cdd2
push idunknown
push userunknown
push dateunknown
reviewerscjones, blocking-basecamp
bugs797239
milestone18.0a2
Bug 797239 - Defer scheduling prelaunch app until child goes idle. r=cjones, a=blocking-basecamp
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -465,20 +465,31 @@ ContentChild::AllocPCompositor(mozilla::
 
 PImageBridgeChild*
 ContentChild::AllocPImageBridge(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
+static void FirstIdle(void)
+{
+    ContentChild::GetSingleton()->SendFirstIdle();
+}
+
 PBrowserChild*
 ContentChild::AllocPBrowser(const uint32_t& aChromeFlags,
                             const bool& aIsBrowserElement, const AppId& aApp)
 {
+    static bool firstIdleTaskPosted = false;
+    if (!firstIdleTaskPosted) {
+        MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableFunction(FirstIdle));
+        firstIdleTaskPosted = true;
+    }
+
     nsRefPtr<TabChild> child =
         TabChild::Create(aChromeFlags, aIsBrowserElement, aApp.get_uint32_t());
     // The ref here is released below.
     return child.forget().get();
 }
 
 bool
 ContentChild::DeallocPBrowser(PBrowserChild* iframe)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -222,37 +222,47 @@ ContentParent::ScheduleDelayedPreallocat
         FROM_HERE, sPreallocateAppProcessTask, sPreallocateDelayMs);
 }
 
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::MaybeTakePreallocatedAppProcess()
 {
     nsRefPtr<ContentParent> process = sPreallocatedAppProcess.get();
     sPreallocatedAppProcess = nullptr;
-    ScheduleDelayedPreallocateAppProcess();
     return process.forget();
 }
 
 /*static*/ void
+ContentParent::FirstIdle(void)
+{
+  // The parent has gone idle for the first time. This would be a good
+  // time to preallocate an app process.
+  ScheduleDelayedPreallocateAppProcess();
+}
+
+/*static*/ void
 ContentParent::StartUp()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         return;
     }
 
     sKeepAppProcessPreallocated =
         Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false);
     if (sKeepAppProcessPreallocated) {
         ClearOnShutdown(&sPreallocatedAppProcess);
 
         sPreallocateDelayMs = Preferences::GetUint(
             "dom.ipc.processPrelaunch.delayMs", 1000);
 
         MOZ_ASSERT(!sPreallocateAppProcessTask);
-        ScheduleDelayedPreallocateAppProcess();
+
+        // Let's not slow down the main process initialization. Wait until
+        // the main process goes idle before we preallocate a process
+        MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableFunction(FirstIdle));
     }
 }
 
 /*static*/ void
 ContentParent::ShutDown()
 {
     // No-op for now.  We rely on normal process shutdown and
     // ClearOnShutdown() to clean up our state.
@@ -998,16 +1008,27 @@ ContentParent::RecvGetShowPasswordSettin
 #ifdef MOZ_WIDGET_ANDROID
     NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
     if (AndroidBridge::Bridge() != nullptr)
         *showPassword = AndroidBridge::Bridge()->GetShowPasswordSetting();
 #endif
     return true;
 }
 
+bool
+ContentParent::RecvFirstIdle()
+{
+    // When the ContentChild goes idle, it sends us a FirstIdle message
+    // which we use as a good time to prelaunch another process. If we
+    // prelaunch any sooner than this, then we'll be competing with the
+    // child process and slowing it down.
+    ScheduleDelayedPreallocateAppProcess();
+    return true;
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS3(ContentParent,
                               nsIObserver,
                               nsIThreadObserver,
                               nsIDOMGeoPositionCallback)
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -139,16 +139,17 @@ private:
     static nsDataHashtable<nsStringHashKey, ContentParent*> *gAppContentParents;
     static nsTArray<ContentParent*>* gNonAppContentParents;
     static nsTArray<ContentParent*>* gPrivateContent;
 
     static void PreallocateAppProcess();
     static void DelayedPreallocateAppProcess();
     static void ScheduleDelayedPreallocateAppProcess();
     static already_AddRefed<ContentParent> MaybeTakePreallocatedAppProcess();
+    static void FirstIdle();
 
     // Hide the raw constructor methods since we don't want client code
     // using them.
     using PContentParent::SendPBrowserConstructor;
     using PContentParent::SendPTestShellConstructor;
 
     ContentParent(const nsAString& aAppManifestURL, bool aIsForBrowser,
                   ChildOSPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT);
@@ -303,16 +304,18 @@ private:
                                  const nsString& aSourceLine,
                                  const uint32_t& aLineNumber,
                                  const uint32_t& aColNumber,
                                  const uint32_t& aFlags,
                                  const nsCString& aCategory);
 
     virtual bool RecvPrivateDocShellsExist(const bool& aExist);
 
+    virtual bool RecvFirstIdle();
+
     virtual void ProcessingError(Result what) MOZ_OVERRIDE;
 
     GeckoChildProcessHost* mSubprocess;
     ChildOSPrivileges mOSPrivileges;
 
     int32_t mGeolocationWatchID;
     int mRunToCompletionDepth;
     bool mShouldCallUnblockChild;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -341,14 +341,17 @@ parent:
         returns (uint8_t[] bits);
 
     sync GetShowPasswordSetting()
         returns (bool showPassword);
 
     // Notify the parent of the presence or absence of private docshells
     PrivateDocShellsExist(bool aExist);
 
+    // Tell the parent that the child has gone idle for the first time
+    async FirstIdle();
+
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData);
 };
 
 }
 }