dom/ipc/PreallocatedProcessManager.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=8 et ft=cpp : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/PreallocatedProcessManager.h"
     8 #include "mozilla/ClearOnShutdown.h"
     9 #include "mozilla/Preferences.h"
    10 #include "mozilla/dom/ContentParent.h"
    11 #include "nsIPropertyBag2.h"
    12 #include "ProcessPriorityManager.h"
    13 #include "nsServiceManagerUtils.h"
    14 #include "nsCxPusher.h"
    16 #ifdef MOZ_NUWA_PROCESS
    17 #include "ipc/Nuwa.h"
    18 #endif
    20 // This number is fairly arbitrary ... the intention is to put off
    21 // launching another app process until the last one has finished
    22 // loading its content, to reduce CPU/memory/IO contention.
    23 #define DEFAULT_ALLOCATE_DELAY 1000
    24 #define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds.
    26 using namespace mozilla;
    27 using namespace mozilla::hal;
    28 using namespace mozilla::dom;
    30 namespace {
    32 /**
    33  * This singleton class implements the static methods on
    34  * PreallocatedProcessManager.
    35  */
    36 class PreallocatedProcessManagerImpl MOZ_FINAL
    37   : public nsIObserver
    38 {
    39 public:
    40   static PreallocatedProcessManagerImpl* Singleton();
    42   NS_DECL_ISUPPORTS
    43   NS_DECL_NSIOBSERVER
    45   // See comments on PreallocatedProcessManager for these methods.
    46   void AllocateAfterDelay();
    47   void AllocateOnIdle();
    48   void AllocateNow();
    49   already_AddRefed<ContentParent> Take();
    51 #ifdef MOZ_NUWA_PROCESS
    52 public:
    53   void ScheduleDelayedNuwaFork();
    54   void DelayedNuwaFork();
    55   void PublishSpareProcess(ContentParent* aContent);
    56   void MaybeForgetSpare(ContentParent* aContent);
    57   void OnNuwaReady();
    58   bool PreallocatedProcessReady();
    59   already_AddRefed<ContentParent> GetSpareProcess();
    60   void RunAfterPreallocatedProcessReady(nsIRunnable* aRunnable);
    62 private:
    63   void NuwaFork();
    65   // initialization off the critical path of app startup.
    66   CancelableTask* mPreallocateAppProcessTask;
    68   // The array containing the preallocated processes. 4 as the inline storage size
    69   // should be enough so we don't need to grow the nsAutoTArray.
    70   nsAutoTArray<nsRefPtr<ContentParent>, 4> mSpareProcesses;
    72   nsTArray<nsCOMPtr<nsIRunnable> > mDelayedContentParentRequests;
    74   // Nuwa process is ready for creating new process.
    75   bool mIsNuwaReady;
    76 #endif
    78 private:
    79   static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
    81   PreallocatedProcessManagerImpl();
    82   DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
    84   void Init();
    86   void RereadPrefs();
    87   void Enable();
    88   void Disable();
    90   void ObserveProcessShutdown(nsISupports* aSubject);
    92   bool mEnabled;
    93   bool mShutdown;
    94   nsRefPtr<ContentParent> mPreallocatedAppProcess;
    95 };
    97 /* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
    98 PreallocatedProcessManagerImpl::sSingleton;
   100 /* static */ PreallocatedProcessManagerImpl*
   101 PreallocatedProcessManagerImpl::Singleton()
   102 {
   103   if (!sSingleton) {
   104     sSingleton = new PreallocatedProcessManagerImpl();
   105     sSingleton->Init();
   106     ClearOnShutdown(&sSingleton);
   107   }
   109   return sSingleton;
   110 }
   112 NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
   114 PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
   115   : mEnabled(false)
   116 #ifdef MOZ_NUWA_PROCESS
   117   , mPreallocateAppProcessTask(nullptr)
   118   , mIsNuwaReady(false)
   119 #endif
   120   , mShutdown(false)
   121 {}
   123 void
   124 PreallocatedProcessManagerImpl::Init()
   125 {
   126   Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
   127   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   128   if (os) {
   129     os->AddObserver(this, "ipc:content-shutdown",
   130                     /* weakRef = */ false);
   131     os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
   132                     /* weakRef = */ false);
   133   }
   134   RereadPrefs();
   135 }
   137 NS_IMETHODIMP
   138 PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
   139                                         const char* aTopic,
   140                                         const char16_t* aData)
   141 {
   142   if (!strcmp("ipc:content-shutdown", aTopic)) {
   143     ObserveProcessShutdown(aSubject);
   144   } else if (!strcmp("nsPref:changed", aTopic)) {
   145     // The only other observer we registered was for our prefs.
   146     RereadPrefs();
   147   } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
   148     mShutdown = true;
   149   } else {
   150     MOZ_ASSERT(false);
   151   }
   153   return NS_OK;
   154 }
   156 void
   157 PreallocatedProcessManagerImpl::RereadPrefs()
   158 {
   159   if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
   160     Enable();
   161   } else {
   162     Disable();
   163   }
   164 }
   166 already_AddRefed<ContentParent>
   167 PreallocatedProcessManagerImpl::Take()
   168 {
   169   return mPreallocatedAppProcess.forget();
   170 }
   172 void
   173 PreallocatedProcessManagerImpl::Enable()
   174 {
   175   if (mEnabled) {
   176     return;
   177   }
   179   mEnabled = true;
   180 #ifdef MOZ_NUWA_PROCESS
   181   ScheduleDelayedNuwaFork();
   182 #else
   183   AllocateAfterDelay();
   184 #endif
   185 }
   187 void
   188 PreallocatedProcessManagerImpl::AllocateAfterDelay()
   189 {
   190   if (!mEnabled || mPreallocatedAppProcess) {
   191     return;
   192   }
   194   MessageLoop::current()->PostDelayedTask(
   195     FROM_HERE,
   196     NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
   197     Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
   198                          DEFAULT_ALLOCATE_DELAY));
   199 }
   201 void
   202 PreallocatedProcessManagerImpl::AllocateOnIdle()
   203 {
   204   if (!mEnabled || mPreallocatedAppProcess) {
   205     return;
   206   }
   208   MessageLoop::current()->PostIdleTask(
   209     FROM_HERE,
   210     NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
   211 }
   213 void
   214 PreallocatedProcessManagerImpl::AllocateNow()
   215 {
   216   if (!mEnabled || mPreallocatedAppProcess) {
   217     return;
   218   }
   220   mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
   221 }
   223 #ifdef MOZ_NUWA_PROCESS
   225 void
   226 PreallocatedProcessManagerImpl::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
   227 {
   228   MOZ_ASSERT(NS_IsMainThread());
   229   mDelayedContentParentRequests.AppendElement(aRequest);
   231   // This is an urgent NuwaFork() request. Request to fork at once.
   232   DelayedNuwaFork();
   233 }
   235 void
   236 PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork()
   237 {
   238   MOZ_ASSERT(NS_IsMainThread());
   240   if (mPreallocateAppProcessTask) {
   241     // Make sure there is only one request running.
   242     return;
   243   }
   245   mPreallocateAppProcessTask = NewRunnableMethod(
   246     this, &PreallocatedProcessManagerImpl::DelayedNuwaFork);
   247   MessageLoop::current()->PostDelayedTask(
   248     FROM_HERE, mPreallocateAppProcessTask,
   249     Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
   250                          DEFAULT_ALLOCATE_DELAY));
   251 }
   253 void
   254 PreallocatedProcessManagerImpl::DelayedNuwaFork()
   255 {
   256   MOZ_ASSERT(NS_IsMainThread());
   258   mPreallocateAppProcessTask = nullptr;
   260   if (!mIsNuwaReady) {
   261     if (!mPreallocatedAppProcess && !mShutdown && mEnabled) {
   262       mPreallocatedAppProcess = ContentParent::RunNuwaProcess();
   263     }
   264     // else mPreallocatedAppProcess is starting. It will NuwaFork() when ready.
   265   } else if (mSpareProcesses.IsEmpty()) {
   266     NuwaFork();
   267   }
   268 }
   270 /**
   271  * Get a spare ContentParent from mSpareProcesses list.
   272  */
   273 already_AddRefed<ContentParent>
   274 PreallocatedProcessManagerImpl::GetSpareProcess()
   275 {
   276   MOZ_ASSERT(NS_IsMainThread());
   278   if (mSpareProcesses.IsEmpty()) {
   279     return nullptr;
   280   }
   282   nsRefPtr<ContentParent> process = mSpareProcesses.LastElement();
   283   mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1);
   285   if (mSpareProcesses.IsEmpty() && mIsNuwaReady) {
   286     NS_ASSERTION(mPreallocatedAppProcess != nullptr,
   287                  "Nuwa process is not present!");
   288     ScheduleDelayedNuwaFork();
   289   }
   291   return process.forget();
   292 }
   294 /**
   295  * Publish a ContentParent to spare process list.
   296  */
   297 void
   298 PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent)
   299 {
   300   MOZ_ASSERT(NS_IsMainThread());
   302   if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
   303     AutoJSContext cx;
   304     nsCOMPtr<nsIMessageBroadcaster> ppmm =
   305       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
   306     nsresult rv = ppmm->BroadcastAsyncMessage(
   307       NS_LITERAL_STRING("TEST-ONLY:nuwa-add-new-process"),
   308       JS::NullHandleValue, JS::NullHandleValue, cx, 1);
   309   }
   311   mSpareProcesses.AppendElement(aContent);
   313   if (!mDelayedContentParentRequests.IsEmpty()) {
   314     nsCOMPtr<nsIRunnable> runnable = mDelayedContentParentRequests[0];
   315     mDelayedContentParentRequests.RemoveElementAt(0);
   316     NS_DispatchToMainThread(runnable);
   317   }
   318 }
   320 void
   321 PreallocatedProcessManagerImpl::MaybeForgetSpare(ContentParent* aContent)
   322 {
   323   MOZ_ASSERT(NS_IsMainThread());
   325   if (!mDelayedContentParentRequests.IsEmpty()) {
   326     if (!mPreallocateAppProcessTask) {
   327       // This NuwaFork request is urgent. Don't delay it.
   328       DelayedNuwaFork();
   329     }
   330   }
   332   if (mSpareProcesses.RemoveElement(aContent)) {
   333     return;
   334   }
   336   if (aContent == mPreallocatedAppProcess) {
   337     mPreallocatedAppProcess = nullptr;
   338     mIsNuwaReady = false;
   339     ScheduleDelayedNuwaFork();
   340   }
   341 }
   343 void
   344 PreallocatedProcessManagerImpl::OnNuwaReady()
   345 {
   346   NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!");
   347   ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess,
   348                                              hal::PROCESS_PRIORITY_MASTER);
   349   mIsNuwaReady = true;
   350   if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
   351     AutoJSContext cx;
   352     nsCOMPtr<nsIMessageBroadcaster> ppmm =
   353       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
   354     nsresult rv = ppmm->BroadcastAsyncMessage(
   355       NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"),
   356       JS::NullHandleValue, JS::NullHandleValue, cx, 1);
   357   }
   358   NuwaFork();
   359 }
   361 bool
   362 PreallocatedProcessManagerImpl::PreallocatedProcessReady()
   363 {
   364   return !mSpareProcesses.IsEmpty();
   365 }
   368 void
   369 PreallocatedProcessManagerImpl::NuwaFork()
   370 {
   371   mPreallocatedAppProcess->SendNuwaFork();
   372 }
   373 #endif
   375 void
   376 PreallocatedProcessManagerImpl::Disable()
   377 {
   378   if (!mEnabled) {
   379     return;
   380   }
   382   mEnabled = false;
   384 #ifdef MOZ_NUWA_PROCESS
   385   // Cancel pending fork.
   386   if (mPreallocateAppProcessTask) {
   387     mPreallocateAppProcessTask->Cancel();
   388     mPreallocateAppProcessTask = nullptr;
   389   }
   390 #endif
   392   if (mPreallocatedAppProcess) {
   393 #ifdef MOZ_NUWA_PROCESS
   394     while (mSpareProcesses.Length() > 0){
   395       nsRefPtr<ContentParent> process = mSpareProcesses[0];
   396       process->Close();
   397       mSpareProcesses.RemoveElementAt(0);
   398     }
   399     mIsNuwaReady = false;
   400 #endif
   401     mPreallocatedAppProcess->Close();
   402     mPreallocatedAppProcess = nullptr;
   403   }
   404 }
   406 void
   407 PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
   408 {
   409   if (!mPreallocatedAppProcess) {
   410     return;
   411   }
   413   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   414   NS_ENSURE_TRUE_VOID(props);
   416   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   417   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   418   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
   420   if (childID == mPreallocatedAppProcess->ChildID()) {
   421     mPreallocatedAppProcess = nullptr;
   422   }
   423 }
   425 inline PreallocatedProcessManagerImpl* GetPPMImpl()
   426 {
   427   return PreallocatedProcessManagerImpl::Singleton();
   428 }
   430 } // anonymous namespace
   432 namespace mozilla {
   434 /* static */ void
   435 PreallocatedProcessManager::AllocateAfterDelay()
   436 {
   437 #ifdef MOZ_NUWA_PROCESS
   438   GetPPMImpl()->ScheduleDelayedNuwaFork();
   439 #else
   440   GetPPMImpl()->AllocateAfterDelay();
   441 #endif
   442 }
   444 /* static */ void
   445 PreallocatedProcessManager::AllocateOnIdle()
   446 {
   447   GetPPMImpl()->AllocateOnIdle();
   448 }
   450 /* static */ void
   451 PreallocatedProcessManager::AllocateNow()
   452 {
   453   GetPPMImpl()->AllocateNow();
   454 }
   456 /* static */ already_AddRefed<ContentParent>
   457 PreallocatedProcessManager::Take()
   458 {
   459 #ifdef MOZ_NUWA_PROCESS
   460   return GetPPMImpl()->GetSpareProcess();
   461 #else
   462   return GetPPMImpl()->Take();
   463 #endif
   464 }
   466 #ifdef MOZ_NUWA_PROCESS
   467 /* static */ void
   468 PreallocatedProcessManager::PublishSpareProcess(ContentParent* aContent)
   469 {
   470   GetPPMImpl()->PublishSpareProcess(aContent);
   471 }
   473 /* static */ void
   474 PreallocatedProcessManager::MaybeForgetSpare(ContentParent* aContent)
   475 {
   476   GetPPMImpl()->MaybeForgetSpare(aContent);
   477 }
   479 /* static */ void
   480 PreallocatedProcessManager::OnNuwaReady()
   481 {
   482   GetPPMImpl()->OnNuwaReady();
   483 }
   485 /*static */ bool
   486 PreallocatedProcessManager::PreallocatedProcessReady()
   487 {
   488   return GetPPMImpl()->PreallocatedProcessReady();
   489 }
   491 /* static */ void
   492 PreallocatedProcessManager::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
   493 {
   494   GetPPMImpl()->RunAfterPreallocatedProcessReady(aRequest);
   495 }
   497 #endif
   499 } // namespace mozilla

mercurial