LCOV - code coverage report
Current view: top level - dom/ipc - PreallocatedProcessManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 80 134 59.7 %
Date: 2017-07-14 16:53:18 Functions: 19 26 73.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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/. */
       6             : 
       7             : #include "mozilla/PreallocatedProcessManager.h"
       8             : #include "mozilla/ClearOnShutdown.h"
       9             : #include "mozilla/Preferences.h"
      10             : #include "mozilla/Unused.h"
      11             : #include "mozilla/dom/ContentParent.h"
      12             : #include "mozilla/dom/ScriptSettings.h"
      13             : #include "nsIPropertyBag2.h"
      14             : #include "ProcessPriorityManager.h"
      15             : #include "nsServiceManagerUtils.h"
      16             : 
      17             : // This number is fairly arbitrary ... the intention is to put off
      18             : // launching another app process until the last one has finished
      19             : // loading its content, to reduce CPU/memory/IO contention.
      20             : #define DEFAULT_ALLOCATE_DELAY 1000
      21             : 
      22             : using namespace mozilla;
      23             : using namespace mozilla::hal;
      24             : using namespace mozilla::dom;
      25             : 
      26             : namespace mozilla {
      27             : 
      28             : /**
      29             :  * This singleton class implements the static methods on
      30             :  * PreallocatedProcessManager.
      31             :  */
      32             : class PreallocatedProcessManagerImpl final
      33             :   : public nsIObserver
      34             : {
      35             : public:
      36             :   static PreallocatedProcessManagerImpl* Singleton();
      37             : 
      38             :   NS_DECL_ISUPPORTS
      39             :   NS_DECL_NSIOBSERVER
      40             : 
      41             :   // See comments on PreallocatedProcessManager for these methods.
      42             :   void AddBlocker(ContentParent* aParent);
      43             :   void RemoveBlocker(ContentParent* aParent);
      44             :   already_AddRefed<ContentParent> Take();
      45             :   bool Provide(ContentParent* aParent);
      46             : 
      47             : private:
      48             :   static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
      49             : 
      50             :   PreallocatedProcessManagerImpl();
      51           0 :   ~PreallocatedProcessManagerImpl() {}
      52             :   DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
      53             : 
      54             :   void Init();
      55             : 
      56             :   bool CanAllocate();
      57             :   void AllocateAfterDelay();
      58             :   void AllocateOnIdle();
      59             :   void AllocateNow();
      60             : 
      61             :   void RereadPrefs();
      62             :   void Enable();
      63             :   void Disable();
      64             :   void CloseProcess();
      65             : 
      66             :   void ObserveProcessShutdown(nsISupports* aSubject);
      67             : 
      68             :   bool mEnabled;
      69             :   bool mShutdown;
      70             :   RefPtr<ContentParent> mPreallocatedProcess;
      71             :   nsTHashtable<nsUint64HashKey> mBlockers;
      72             : };
      73             : 
      74             : /* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
      75           3 : PreallocatedProcessManagerImpl::sSingleton;
      76             : 
      77             : /* static */ PreallocatedProcessManagerImpl*
      78           4 : PreallocatedProcessManagerImpl::Singleton()
      79             : {
      80           4 :   MOZ_ASSERT(NS_IsMainThread());
      81           4 :   if (!sSingleton) {
      82           1 :     sSingleton = new PreallocatedProcessManagerImpl();
      83           1 :     sSingleton->Init();
      84           1 :     ClearOnShutdown(&sSingleton);
      85             :   }
      86             : 
      87           4 :   return sSingleton;
      88             : }
      89             : 
      90          27 : NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
      91             : 
      92           1 : PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
      93             :   : mEnabled(false)
      94           1 :   , mShutdown(false)
      95           1 : {}
      96             : 
      97             : void
      98           1 : PreallocatedProcessManagerImpl::Init()
      99             : {
     100           1 :   Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
     101             :   // We have to respect processCount at all time. This is especially important
     102             :   // for testing.
     103           1 :   Preferences::AddStrongObserver(this, "dom.ipc.processCount");
     104           2 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     105           1 :   if (os) {
     106           2 :     os->AddObserver(this, "ipc:content-shutdown",
     107           2 :                     /* weakRef = */ false);
     108           2 :     os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
     109           2 :                     /* weakRef = */ false);
     110           2 :     os->AddObserver(this, "profile-change-teardown",
     111           2 :                     /* weakRef = */ false);
     112             :   }
     113           1 :   RereadPrefs();
     114           1 : }
     115             : 
     116             : NS_IMETHODIMP
     117           0 : PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
     118             :                                         const char* aTopic,
     119             :                                         const char16_t* aData)
     120             : {
     121           0 :   if (!strcmp("ipc:content-shutdown", aTopic)) {
     122           0 :     ObserveProcessShutdown(aSubject);
     123           0 :   } else if (!strcmp("nsPref:changed", aTopic)) {
     124             :     // The only other observer we registered was for our prefs.
     125           0 :     RereadPrefs();
     126           0 :   } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic) ||
     127           0 :              !strcmp("profile-change-teardown", aTopic)) {
     128           0 :     Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled");
     129           0 :     Preferences::RemoveObserver(this, "dom.ipc.processCount");
     130           0 :     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     131           0 :     if (os) {
     132           0 :       os->RemoveObserver(this, "ipc:content-shutdown");
     133           0 :       os->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     134           0 :       os->RemoveObserver(this, "profile-change-teardown");
     135             :     }
     136           0 :     mShutdown = true;
     137           0 :     CloseProcess();
     138             :   } else {
     139           0 :     MOZ_ASSERT(false);
     140             :   }
     141             : 
     142           0 :   return NS_OK;
     143             : }
     144             : 
     145             : void
     146           1 : PreallocatedProcessManagerImpl::RereadPrefs()
     147             : {
     148           2 :   if (mozilla::BrowserTabsRemoteAutostart() &&
     149           1 :       Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
     150           1 :     Enable();
     151             :   } else {
     152           0 :     Disable();
     153             :   }
     154             : 
     155           1 :   if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
     156           0 :     CloseProcess();
     157             :   }
     158           1 : }
     159             : 
     160             : already_AddRefed<ContentParent>
     161           1 : PreallocatedProcessManagerImpl::Take()
     162             : {
     163           1 :   if (!mEnabled || mShutdown) {
     164           0 :     return nullptr;
     165             :   }
     166             : 
     167           1 :   if (mPreallocatedProcess) {
     168             :     // The preallocated process is taken. Let's try to start up a new one soon.
     169           0 :     AllocateOnIdle();
     170             :   }
     171             : 
     172           1 :   return mPreallocatedProcess.forget();
     173             : }
     174             : 
     175             : bool
     176           0 : PreallocatedProcessManagerImpl::Provide(ContentParent* aParent)
     177             : {
     178           0 :   if (mEnabled && !mShutdown && !mPreallocatedProcess) {
     179           0 :     mPreallocatedProcess = aParent;
     180             :   }
     181             : 
     182             :   // We might get a call from both NotifyTabDestroying and NotifyTabDestroyed with the same
     183             :   // ContentParent. Returning true here for both calls is important to avoid the cached process
     184             :   // to be destroyed.
     185           0 :   return aParent == mPreallocatedProcess;
     186             : }
     187             : 
     188             : void
     189           1 : PreallocatedProcessManagerImpl::Enable()
     190             : {
     191           1 :   if (mEnabled) {
     192           0 :     return;
     193             :   }
     194             : 
     195           1 :   mEnabled = true;
     196           1 :   AllocateAfterDelay();
     197             : }
     198             : 
     199             : void
     200           2 : PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent)
     201             : {
     202           2 :   uint64_t childID = aParent->ChildID();
     203           2 :   MOZ_ASSERT(!mBlockers.Contains(childID));
     204           2 :   mBlockers.PutEntry(childID);
     205           2 : }
     206             : 
     207             : void
     208           1 : PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent)
     209             : {
     210           1 :   uint64_t childID = aParent->ChildID();
     211           1 :   MOZ_ASSERT(mBlockers.Contains(childID));
     212           1 :   mBlockers.RemoveEntry(childID);
     213           1 :   if (!mPreallocatedProcess && mBlockers.IsEmpty()) {
     214           1 :     AllocateAfterDelay();
     215             :   }
     216           1 : }
     217             : 
     218             : bool
     219           1 : PreallocatedProcessManagerImpl::CanAllocate()
     220             : {
     221           2 :   return mEnabled &&
     222           2 :          mBlockers.IsEmpty() &&
     223           2 :          !mPreallocatedProcess &&
     224           5 :          !mShutdown &&
     225           3 :          !ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
     226             : }
     227             : 
     228             : void
     229           2 : PreallocatedProcessManagerImpl::AllocateAfterDelay()
     230             : {
     231           2 :   if (!mEnabled) {
     232           0 :     return;
     233             :   }
     234             : 
     235           6 :   NS_DelayedDispatchToCurrentThread(
     236           4 :     NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle",
     237             :                       this,
     238             :                       &PreallocatedProcessManagerImpl::AllocateOnIdle),
     239             :     Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
     240           2 :                          DEFAULT_ALLOCATE_DELAY));
     241             : }
     242             : 
     243             : void
     244           2 : PreallocatedProcessManagerImpl::AllocateOnIdle()
     245             : {
     246           2 :   if (!mEnabled) {
     247           0 :     return;
     248             :   }
     249             : 
     250           4 :   NS_IdleDispatchToCurrentThread(
     251           4 :     NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow",
     252             :                       this,
     253           2 :                       &PreallocatedProcessManagerImpl::AllocateNow));
     254             : }
     255             : 
     256             : void
     257           1 : PreallocatedProcessManagerImpl::AllocateNow()
     258             : {
     259           1 :   if (!CanAllocate()) {
     260           0 :     if (mEnabled && !mShutdown && !mPreallocatedProcess && !mBlockers.IsEmpty()) {
     261             :       // If it's too early to allocate a process let's retry later.
     262           0 :       AllocateAfterDelay();
     263             :     }
     264           0 :     return;
     265             :   }
     266             : 
     267           1 :   mPreallocatedProcess = ContentParent::PreallocateProcess();
     268             : }
     269             : 
     270             : void
     271           0 : PreallocatedProcessManagerImpl::Disable()
     272             : {
     273           0 :   if (!mEnabled) {
     274           0 :     return;
     275             :   }
     276             : 
     277           0 :   mEnabled = false;
     278           0 :   CloseProcess();
     279             : }
     280             : 
     281             : void
     282           0 : PreallocatedProcessManagerImpl::CloseProcess()
     283             : {
     284           0 :   if (mPreallocatedProcess) {
     285           0 :     mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
     286           0 :     mPreallocatedProcess = nullptr;
     287             :   }
     288           0 : }
     289             : 
     290             : void
     291           0 : PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
     292             : {
     293           0 :   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
     294           0 :   NS_ENSURE_TRUE_VOID(props);
     295             : 
     296           0 :   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
     297           0 :   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
     298           0 :   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
     299             : 
     300           0 :   if (mPreallocatedProcess && childID == mPreallocatedProcess->ChildID()) {
     301           0 :     mPreallocatedProcess = nullptr;
     302             :   }
     303             : 
     304           0 :   mBlockers.RemoveEntry(childID);
     305             : }
     306             : 
     307           4 : inline PreallocatedProcessManagerImpl* GetPPMImpl()
     308             : {
     309           4 :   return PreallocatedProcessManagerImpl::Singleton();
     310             : }
     311             : 
     312             : /* static */ void
     313           2 : PreallocatedProcessManager::AddBlocker(ContentParent* aParent)
     314             : {
     315           2 :   GetPPMImpl()->AddBlocker(aParent);
     316           2 : }
     317             : 
     318             : /* static */ void
     319           1 : PreallocatedProcessManager::RemoveBlocker(ContentParent* aParent)
     320             : {
     321           1 :   GetPPMImpl()->RemoveBlocker(aParent);
     322           1 : }
     323             : 
     324             : /* static */ already_AddRefed<ContentParent>
     325           1 : PreallocatedProcessManager::Take()
     326             : {
     327           1 :   return GetPPMImpl()->Take();
     328             : }
     329             : 
     330             : /* static */ bool
     331           0 : PreallocatedProcessManager::Provide(ContentParent* aParent)
     332             : {
     333           0 :   return GetPPMImpl()->Provide(aParent);
     334             : }
     335             : 
     336             : } // namespace mozilla

Generated by: LCOV version 1.13