LCOV - code coverage report
Current view: top level - dom/ipc - ProcessPriorityManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 62 359 17.3 %
Date: 2017-07-14 16:53:18 Functions: 12 64 18.8 %
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 "ProcessPriorityManager.h"
       8             : #include "mozilla/ClearOnShutdown.h"
       9             : #include "mozilla/dom/ContentParent.h"
      10             : #include "mozilla/dom/Element.h"
      11             : #include "mozilla/dom/TabParent.h"
      12             : #include "mozilla/Hal.h"
      13             : #include "mozilla/IntegerPrintfMacros.h"
      14             : #include "mozilla/Preferences.h"
      15             : #include "mozilla/Services.h"
      16             : #include "mozilla/Unused.h"
      17             : #include "mozilla/Logging.h"
      18             : #include "nsPrintfCString.h"
      19             : #include "nsXULAppAPI.h"
      20             : #include "nsIFrameLoader.h"
      21             : #include "nsIObserverService.h"
      22             : #include "StaticPtr.h"
      23             : #include "nsIMozBrowserFrame.h"
      24             : #include "nsIObserver.h"
      25             : #include "nsITimer.h"
      26             : #include "nsIPropertyBag2.h"
      27             : #include "nsComponentManagerUtils.h"
      28             : #include "nsCRT.h"
      29             : #include "nsTHashtable.h"
      30             : 
      31             : using namespace mozilla;
      32             : using namespace mozilla::dom;
      33             : using namespace mozilla::hal;
      34             : 
      35             : #ifdef XP_WIN
      36             : #include <process.h>
      37             : #define getpid _getpid
      38             : #else
      39             : #include <unistd.h>
      40             : #endif
      41             : 
      42             : #ifdef LOG
      43             : #undef LOG
      44             : #endif
      45             : 
      46             : // Use LOGP inside a ParticularProcessPriorityManager method; use LOG
      47             : // everywhere else.  LOGP prints out information about the particular process
      48             : // priority manager.
      49             : //
      50             : // (Wow, our logging story is a huge mess.)
      51             : 
      52             : // #define ENABLE_LOGGING 1
      53             : 
      54             : #if defined(ANDROID) && defined(ENABLE_LOGGING)
      55             : #  include <android/log.h>
      56             : #  define LOG(fmt, ...) \
      57             :      __android_log_print(ANDROID_LOG_INFO, \
      58             :        "Gecko:ProcessPriorityManager", \
      59             :        fmt, ## __VA_ARGS__)
      60             : #  define LOGP(fmt, ...) \
      61             :     __android_log_print(ANDROID_LOG_INFO, \
      62             :       "Gecko:ProcessPriorityManager", \
      63             :       "[%schild-id=%" PRIu64 ", pid=%d] " fmt, \
      64             :       NameWithComma().get(), \
      65             :       static_cast<uint64_t>(ChildID()), Pid(), ## __VA_ARGS__)
      66             : 
      67             : #elif defined(ENABLE_LOGGING)
      68             : #  define LOG(fmt, ...) \
      69             :      printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__)
      70             : #  define LOGP(fmt, ...) \
      71             :      printf("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " \
      72             :        fmt "\n", \
      73             :        NameWithComma().get(), \
      74             :        static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__)
      75             : #else
      76             :   static LogModule*
      77           5 :   GetPPMLog()
      78             :   {
      79             :     static LazyLogModule sLog("ProcessPriorityManager");
      80           5 :     return sLog;
      81             :   }
      82             : #  define LOG(fmt, ...) \
      83             :      MOZ_LOG(GetPPMLog(), LogLevel::Debug, \
      84             :             ("ProcessPriorityManager - " fmt, ##__VA_ARGS__))
      85             : #  define LOGP(fmt, ...) \
      86             :      MOZ_LOG(GetPPMLog(), LogLevel::Debug, \
      87             :             ("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt, \
      88             :             NameWithComma().get(), \
      89             :             static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__))
      90             : #endif
      91             : 
      92             : namespace {
      93             : 
      94             : class ParticularProcessPriorityManager;
      95             : 
      96             : /**
      97             :  * This singleton class does the work to implement the process priority manager
      98             :  * in the main process.  This class may not be used in child processes.  (You
      99             :  * can call StaticInit, but it won't do anything, and GetSingleton() will
     100             :  * return null.)
     101             :  *
     102             :  * ProcessPriorityManager::CurrentProcessIsForeground() and
     103             :  * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
     104             :  * any process, are handled separately, by the ProcessPriorityManagerChild
     105             :  * class.
     106             :  */
     107             : class ProcessPriorityManagerImpl final
     108             :   : public nsIObserver
     109             :   , public WakeLockObserver
     110             :   , public nsSupportsWeakReference
     111             : {
     112             : public:
     113             :   /**
     114             :    * If we're in the main process, get the ProcessPriorityManagerImpl
     115             :    * singleton.  If we're in a child process, return null.
     116             :    */
     117             :   static ProcessPriorityManagerImpl* GetSingleton();
     118             : 
     119             :   static void StaticInit();
     120             :   static bool PrefsEnabled();
     121             :   static bool TestMode();
     122             : 
     123             :   NS_DECL_ISUPPORTS
     124             :   NS_DECL_NSIOBSERVER
     125             : 
     126             :   /**
     127             :    * This function implements ProcessPriorityManager::SetProcessPriority.
     128             :    */
     129             :   void SetProcessPriority(ContentParent* aContentParent,
     130             :                           ProcessPriority aPriority);
     131             : 
     132             :   /**
     133             :    * If a magic testing-only pref is set, notify the observer service on the
     134             :    * given topic with the given data.  This is used for testing
     135             :    */
     136             :   void FireTestOnlyObserverNotification(const char* aTopic,
     137             :                                         const nsACString& aData = EmptyCString());
     138             : 
     139             :   /**
     140             :    * This must be called by a ParticularProcessPriorityManager when it changes
     141             :    * its priority.
     142             :    */
     143             :   void NotifyProcessPriorityChanged(
     144             :     ParticularProcessPriorityManager* aParticularManager,
     145             :     hal::ProcessPriority aOldPriority);
     146             : 
     147             :   /**
     148             :    * Implements WakeLockObserver, used to monitor wake lock changes in the
     149             :    * main process.
     150             :    */
     151             :   virtual void Notify(const WakeLockInformation& aInfo) override;
     152             : 
     153             :   void TabActivityChanged(TabParent* aTabParent, bool aIsActive);
     154             : 
     155             :   /**
     156             :    * Call ShutDown before destroying the ProcessPriorityManager because
     157             :    * WakeLockObserver hols a strong reference to it.
     158             :    */
     159             :   void ShutDown();
     160             : 
     161             : private:
     162             :   static bool sPrefsEnabled;
     163             :   static bool sRemoteTabsDisabled;
     164             :   static bool sTestMode;
     165             :   static bool sPrefListenersRegistered;
     166             :   static bool sInitialized;
     167             :   static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
     168             : 
     169             :   static void PrefChangedCallback(const char* aPref, void* aClosure);
     170             : 
     171             :   ProcessPriorityManagerImpl();
     172             :   ~ProcessPriorityManagerImpl();
     173             :   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl);
     174             : 
     175             :   void Init();
     176             : 
     177             :   already_AddRefed<ParticularProcessPriorityManager>
     178             :   GetParticularProcessPriorityManager(ContentParent* aContentParent);
     179             : 
     180             :   void ObserveContentParentCreated(nsISupports* aContentParent);
     181             :   void ObserveContentParentDestroyed(nsISupports* aSubject);
     182             : 
     183             :   nsDataHashtable<nsUint64HashKey, RefPtr<ParticularProcessPriorityManager> >
     184             :     mParticularManagers;
     185             : 
     186             :   /** True if the main process is holding a high-priority wakelock */
     187             :   bool mHighPriority;
     188             : 
     189             :   /** Contains the PIDs of child processes holding high-priority wakelocks */
     190             :   nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
     191             : };
     192             : 
     193             : /**
     194             :  * This singleton class implements the parts of the process priority manager
     195             :  * that are available from all processes.
     196             :  */
     197             : class ProcessPriorityManagerChild final
     198             :   : public nsIObserver
     199             : {
     200             : public:
     201             :   static void StaticInit();
     202             :   static ProcessPriorityManagerChild* Singleton();
     203             : 
     204             :   NS_DECL_ISUPPORTS
     205             :   NS_DECL_NSIOBSERVER
     206             : 
     207             :   bool CurrentProcessIsForeground();
     208             : 
     209             : private:
     210             :   static StaticRefPtr<ProcessPriorityManagerChild> sSingleton;
     211             : 
     212             :   ProcessPriorityManagerChild();
     213           0 :   ~ProcessPriorityManagerChild() {}
     214             :   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild);
     215             : 
     216             :   void Init();
     217             : 
     218             :   hal::ProcessPriority mCachedPriority;
     219             : };
     220             : 
     221             : /**
     222             :  * This class manages the priority of one particular process.  It is
     223             :  * main-process only.
     224             :  */
     225             : class ParticularProcessPriorityManager final
     226             :   : public WakeLockObserver
     227             :   , public nsIObserver
     228             :   , public nsITimerCallback
     229             :   , public nsSupportsWeakReference
     230             : {
     231             :   ~ParticularProcessPriorityManager();
     232             : public:
     233             :   explicit ParticularProcessPriorityManager(ContentParent* aContentParent);
     234             : 
     235             :   NS_DECL_ISUPPORTS
     236             :   NS_DECL_NSIOBSERVER
     237             :   NS_DECL_NSITIMERCALLBACK
     238             : 
     239             :   virtual void Notify(const WakeLockInformation& aInfo) override;
     240             :   static void StaticInit();
     241             :   void Init();
     242             : 
     243             :   int32_t Pid() const;
     244             :   uint64_t ChildID() const;
     245             : 
     246             :   /**
     247             :    * Used in logging, this method returns the ContentParent's name followed by
     248             :    * ", ".  If we can't get the ContentParent's name for some reason, it
     249             :    * returns an empty string.
     250             :    *
     251             :    * The reference returned here is guaranteed to be live until the next call
     252             :    * to NameWithComma() or until the ParticularProcessPriorityManager is
     253             :    * destroyed, whichever comes first.
     254             :    */
     255             :   const nsAutoCString& NameWithComma();
     256             : 
     257             :   void OnRemoteBrowserFrameShown(nsISupports* aSubject);
     258             :   void OnTabParentDestroyed(nsISupports* aSubject);
     259             : 
     260             :   ProcessPriority CurrentPriority();
     261             :   ProcessPriority ComputePriority();
     262             : 
     263             :   enum TimeoutPref {
     264             :     BACKGROUND_PERCEIVABLE_GRACE_PERIOD,
     265             :     BACKGROUND_GRACE_PERIOD,
     266             :   };
     267             : 
     268             :   void ScheduleResetPriority(TimeoutPref aTimeoutPref);
     269             :   void ResetPriority();
     270             :   void ResetPriorityNow();
     271             :   void SetPriorityNow(ProcessPriority aPriority);
     272             : 
     273             :   void TabActivityChanged(TabParent* aTabParent, bool aIsActive);
     274             : 
     275             :   void ShutDown();
     276             : 
     277             : private:
     278             :   static uint32_t sBackgroundPerceivableGracePeriodMS;
     279             :   static uint32_t sBackgroundGracePeriodMS;
     280             : 
     281             :   void FireTestOnlyObserverNotification(
     282             :     const char* aTopic,
     283             :     const nsACString& aData = EmptyCString());
     284             : 
     285             :   void FireTestOnlyObserverNotification(
     286             :     const char* aTopic,
     287             :     const char* aData = nullptr);
     288             : 
     289             :   ContentParent* mContentParent;
     290             :   uint64_t mChildID;
     291             :   ProcessPriority mPriority;
     292             :   bool mHoldsCPUWakeLock;
     293             :   bool mHoldsHighPriorityWakeLock;
     294             : 
     295             :   /**
     296             :    * Used to implement NameWithComma().
     297             :    */
     298             :   nsAutoCString mNameWithComma;
     299             : 
     300             :   nsCOMPtr<nsITimer> mResetPriorityTimer;
     301             : 
     302             :   // This hashtable contains the list of active TabId for this process.
     303             :   nsTHashtable<nsUint64HashKey> mActiveTabParents;
     304             : };
     305             : 
     306             : /* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
     307             : /* static */ bool ProcessPriorityManagerImpl::sPrefsEnabled = false;
     308             : /* static */ bool ProcessPriorityManagerImpl::sRemoteTabsDisabled = true;
     309             : /* static */ bool ProcessPriorityManagerImpl::sTestMode = false;
     310             : /* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
     311             : /* static */ StaticRefPtr<ProcessPriorityManagerImpl>
     312           3 :   ProcessPriorityManagerImpl::sSingleton;
     313             : /* static */ uint32_t ParticularProcessPriorityManager::sBackgroundPerceivableGracePeriodMS = 0;
     314             : /* static */ uint32_t ParticularProcessPriorityManager::sBackgroundGracePeriodMS = 0;
     315             : 
     316           0 : NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl,
     317             :                   nsIObserver,
     318             :                   nsISupportsWeakReference);
     319             : 
     320             : /* static */ void
     321           0 : ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref,
     322             :                                                 void* aClosure)
     323             : {
     324           0 :   StaticInit();
     325           0 :   if (!PrefsEnabled() && sSingleton) {
     326           0 :     sSingleton->ShutDown();
     327           0 :     sSingleton = nullptr;
     328           0 :     sInitialized = false;
     329             :   }
     330           0 : }
     331             : 
     332             : /* static */ bool
     333           5 : ProcessPriorityManagerImpl::PrefsEnabled()
     334             : {
     335           5 :   return sPrefsEnabled && hal::SetProcessPrioritySupported() && !sRemoteTabsDisabled;
     336             : }
     337             : 
     338             : /* static */ bool
     339           0 : ProcessPriorityManagerImpl::TestMode()
     340             : {
     341           0 :   return sTestMode;
     342             : }
     343             : 
     344             : /* static */ void
     345           7 : ProcessPriorityManagerImpl::StaticInit()
     346             : {
     347           7 :   if (sInitialized) {
     348           0 :     return;
     349             :   }
     350             : 
     351             :   // The process priority manager is main-process only.
     352           7 :   if (!XRE_IsParentProcess()) {
     353           2 :     sInitialized = true;
     354           2 :     return;
     355             :   }
     356             : 
     357           5 :   if (!sPrefListenersRegistered) {
     358             :     Preferences::AddBoolVarCache(&sPrefsEnabled,
     359           1 :                                  "dom.ipc.processPriorityManager.enabled");
     360             :     Preferences::AddBoolVarCache(&sRemoteTabsDisabled,
     361           1 :                                  "dom.ipc.tabs.disabled");
     362             :     Preferences::AddBoolVarCache(&sTestMode,
     363           1 :                                  "dom.ipc.processPriorityManager.testMode");
     364             :   }
     365             : 
     366             :   // If IPC tabs aren't enabled at startup, don't bother with any of this.
     367           5 :   if (!PrefsEnabled()) {
     368           5 :     LOG("InitProcessPriorityManager bailing due to prefs.");
     369             : 
     370             :     // Run StaticInit() again if the prefs change.  We don't expect this to
     371             :     // happen in normal operation, but it happens during testing.
     372           5 :     if (!sPrefListenersRegistered) {
     373           1 :       sPrefListenersRegistered = true;
     374             :       Preferences::RegisterCallback(PrefChangedCallback,
     375           1 :                                     "dom.ipc.processPriorityManager.enabled");
     376             :       Preferences::RegisterCallback(PrefChangedCallback,
     377           1 :                                     "dom.ipc.tabs.disabled");
     378             :     }
     379           5 :     return;
     380             :   }
     381             : 
     382           0 :   sInitialized = true;
     383             : 
     384           0 :   sSingleton = new ProcessPriorityManagerImpl();
     385           0 :   sSingleton->Init();
     386           0 :   ClearOnShutdown(&sSingleton);
     387             : }
     388             : 
     389             : /* static */ ProcessPriorityManagerImpl*
     390           4 : ProcessPriorityManagerImpl::GetSingleton()
     391             : {
     392           4 :   if (!sSingleton) {
     393           4 :     StaticInit();
     394             :   }
     395             : 
     396           4 :   return sSingleton;
     397             : }
     398             : 
     399           0 : ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
     400           0 :   : mHighPriority(false)
     401             : {
     402           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     403           0 :   RegisterWakeLockObserver(this);
     404           0 : }
     405             : 
     406           0 : ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
     407             : {
     408           0 :   ShutDown();
     409           0 : }
     410             : 
     411             : void
     412           0 : ProcessPriorityManagerImpl::ShutDown()
     413             : {
     414           0 :   UnregisterWakeLockObserver(this);
     415           0 : }
     416             : 
     417             : void
     418           0 : ProcessPriorityManagerImpl::Init()
     419             : {
     420           0 :   LOG("Starting up.  This is the master process.");
     421             : 
     422             :   // The master process's priority never changes; set it here and then forget
     423             :   // about it.  We'll manage only subprocesses' priorities using the process
     424             :   // priority manager.
     425           0 :   hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER);
     426             : 
     427           0 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     428           0 :   if (os) {
     429           0 :     os->AddObserver(this, "ipc:content-created", /* ownsWeak */ true);
     430           0 :     os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
     431             :   }
     432           0 : }
     433             : 
     434             : NS_IMETHODIMP
     435           0 : ProcessPriorityManagerImpl::Observe(
     436             :   nsISupports* aSubject,
     437             :   const char* aTopic,
     438             :   const char16_t* aData)
     439             : {
     440           0 :   nsDependentCString topic(aTopic);
     441           0 :   if (topic.EqualsLiteral("ipc:content-created")) {
     442           0 :     ObserveContentParentCreated(aSubject);
     443           0 :   } else if (topic.EqualsLiteral("ipc:content-shutdown")) {
     444           0 :     ObserveContentParentDestroyed(aSubject);
     445             :   } else {
     446           0 :     MOZ_ASSERT(false);
     447             :   }
     448             : 
     449           0 :   return NS_OK;
     450             : }
     451             : 
     452             : already_AddRefed<ParticularProcessPriorityManager>
     453           0 : ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
     454             :   ContentParent* aContentParent)
     455             : {
     456           0 :   uint64_t cpId = aContentParent->ChildID();
     457           0 :   auto entry = mParticularManagers.LookupForAdd(cpId);
     458             :   RefPtr<ParticularProcessPriorityManager> pppm = entry.OrInsert(
     459           0 :     [aContentParent]() {
     460           0 :       return new ParticularProcessPriorityManager(aContentParent);
     461           0 :     });
     462             : 
     463           0 :   if (!entry) {
     464             :     // We created a new entry.
     465           0 :     pppm->Init();
     466             :     FireTestOnlyObserverNotification("process-created",
     467           0 :       nsPrintfCString("%" PRIu64, cpId));
     468             :   }
     469             : 
     470           0 :   return pppm.forget();
     471             : }
     472             : 
     473             : void
     474           0 : ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
     475             :                                                ProcessPriority aPriority)
     476             : {
     477           0 :   MOZ_ASSERT(aContentParent);
     478             :   RefPtr<ParticularProcessPriorityManager> pppm =
     479           0 :     GetParticularProcessPriorityManager(aContentParent);
     480           0 :   if (pppm) {
     481           0 :     pppm->SetPriorityNow(aPriority);
     482             :   }
     483           0 : }
     484             : 
     485             : void
     486           0 : ProcessPriorityManagerImpl::ObserveContentParentCreated(
     487             :   nsISupports* aContentParent)
     488             : {
     489             :   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
     490             :   // don't leak the already_AddRefed object.
     491           0 :   nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
     492             :   RefPtr<ParticularProcessPriorityManager> pppm =
     493           0 :     GetParticularProcessPriorityManager(cp->AsContentParent());
     494           0 : }
     495             : 
     496             : void
     497           0 : ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
     498             : {
     499           0 :   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
     500           0 :   NS_ENSURE_TRUE_VOID(props);
     501             : 
     502           0 :   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
     503           0 :   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
     504           0 :   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
     505             : 
     506           0 :   if (auto entry = mParticularManagers.Lookup(childID)) {
     507           0 :     entry.Data()->ShutDown();
     508           0 :     mHighPriorityChildIDs.RemoveEntry(childID);
     509           0 :     entry.Remove();
     510             :   }
     511             : }
     512             : 
     513             : void
     514           0 : ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
     515             :   ParticularProcessPriorityManager* aParticularManager,
     516             :   ProcessPriority aOldPriority)
     517             : {
     518           0 :   ProcessPriority newPriority = aParticularManager->CurrentPriority();
     519             : 
     520           0 :   if (newPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH &&
     521             :     aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH) {
     522           0 :     mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID());
     523           0 :   } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
     524             :     aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
     525           0 :     mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
     526             :   }
     527           0 : }
     528             : 
     529             : /* virtual */ void
     530           0 : ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
     531             : {
     532             :   /* The main process always has an ID of 0, if it is present in the wake-lock
     533             :    * information then we explicitly requested a high-priority wake-lock for the
     534             :    * main process. */
     535           0 :   if (aInfo.topic().EqualsLiteral("high-priority")) {
     536           0 :     if (aInfo.lockingProcesses().Contains((uint64_t)0)) {
     537           0 :       mHighPriority = true;
     538             :     } else {
     539           0 :       mHighPriority = false;
     540             :     }
     541             : 
     542           0 :     LOG("Got wake lock changed event. "
     543             :         "Now mHighPriorityParent = %d\n", mHighPriority);
     544             :   }
     545           0 : }
     546             : 
     547             : void
     548           0 : ProcessPriorityManagerImpl::TabActivityChanged(TabParent* aTabParent,
     549             :                                                bool aIsActive)
     550             : {
     551           0 :   ContentParent* cp = aTabParent->Manager()->AsContentParent();
     552             :   RefPtr<ParticularProcessPriorityManager> pppm =
     553           0 :     GetParticularProcessPriorityManager(cp);
     554           0 :   if (!pppm) {
     555           0 :     return;
     556             :   }
     557             : 
     558           0 :   pppm->TabActivityChanged(aTabParent, aIsActive);
     559             : }
     560             : 
     561           0 : NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
     562             :                   nsIObserver,
     563             :                   nsITimerCallback,
     564             :                   nsISupportsWeakReference);
     565             : 
     566           0 : ParticularProcessPriorityManager::ParticularProcessPriorityManager(
     567           0 :   ContentParent* aContentParent)
     568             :   : mContentParent(aContentParent)
     569           0 :   , mChildID(aContentParent->ChildID())
     570             :   , mPriority(PROCESS_PRIORITY_UNKNOWN)
     571             :   , mHoldsCPUWakeLock(false)
     572           0 :   , mHoldsHighPriorityWakeLock(false)
     573             : {
     574           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     575           0 :   LOGP("Creating ParticularProcessPriorityManager.");
     576           0 : }
     577             : 
     578             : void
     579           3 : ParticularProcessPriorityManager::StaticInit()
     580             : {
     581             :   Preferences::AddUintVarCache(&sBackgroundPerceivableGracePeriodMS,
     582           3 :                                "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS");
     583             :   Preferences::AddUintVarCache(&sBackgroundGracePeriodMS,
     584           3 :                                "dom.ipc.processPriorityManager.backgroundGracePeriodMS");
     585           3 : }
     586             : 
     587             : void
     588           0 : ParticularProcessPriorityManager::Init()
     589             : {
     590           0 :   RegisterWakeLockObserver(this);
     591             : 
     592           0 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     593           0 :   if (os) {
     594           0 :     os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
     595           0 :     os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
     596             :   }
     597             : 
     598             :   // This process may already hold the CPU lock; for example, our parent may
     599             :   // have acquired it on our behalf.
     600           0 :   WakeLockInformation info1, info2;
     601           0 :   GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
     602           0 :   mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID());
     603             : 
     604           0 :   GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2);
     605           0 :   mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID());
     606           0 :   LOGP("Done starting up.  mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
     607             :        mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
     608           0 : }
     609             : 
     610           0 : ParticularProcessPriorityManager::~ParticularProcessPriorityManager()
     611             : {
     612           0 :   LOGP("Destroying ParticularProcessPriorityManager.");
     613             : 
     614             :   // Unregister our wake lock observer if ShutDown hasn't been called.  (The
     615             :   // wake lock observer takes raw refs, so we don't want to take chances here!)
     616             :   // We don't call UnregisterWakeLockObserver unconditionally because the code
     617             :   // will print a warning if it's called unnecessarily.
     618             : 
     619           0 :   if (mContentParent) {
     620           0 :     UnregisterWakeLockObserver(this);
     621             :   }
     622           0 : }
     623             : 
     624             : /* virtual */ void
     625           0 : ParticularProcessPriorityManager::Notify(const WakeLockInformation& aInfo)
     626             : {
     627           0 :   if (!mContentParent) {
     628             :     // We've been shut down.
     629           0 :     return;
     630             :   }
     631             : 
     632           0 :   bool* dest = nullptr;
     633           0 :   if (aInfo.topic().EqualsLiteral("cpu")) {
     634           0 :     dest = &mHoldsCPUWakeLock;
     635           0 :   } else if (aInfo.topic().EqualsLiteral("high-priority")) {
     636           0 :     dest = &mHoldsHighPriorityWakeLock;
     637             :   }
     638             : 
     639           0 :   if (dest) {
     640           0 :     bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID());
     641           0 :     if (thisProcessLocks != *dest) {
     642           0 :       *dest = thisProcessLocks;
     643           0 :       LOGP("Got wake lock changed event. "
     644             :            "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
     645             :            mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
     646           0 :       ResetPriority();
     647             :     }
     648             :   }
     649             : }
     650             : 
     651             : NS_IMETHODIMP
     652           0 : ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
     653             :                                           const char* aTopic,
     654             :                                           const char16_t* aData)
     655             : {
     656           0 :   if (!mContentParent) {
     657             :     // We've been shut down.
     658           0 :     return NS_OK;
     659             :   }
     660             : 
     661           0 :   nsDependentCString topic(aTopic);
     662             : 
     663           0 :   if (topic.EqualsLiteral("remote-browser-shown")) {
     664           0 :     OnRemoteBrowserFrameShown(aSubject);
     665           0 :   } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
     666           0 :     OnTabParentDestroyed(aSubject);
     667             :   } else {
     668           0 :     MOZ_ASSERT(false);
     669             :   }
     670             : 
     671           0 :   return NS_OK;
     672             : }
     673             : 
     674             : uint64_t
     675           0 : ParticularProcessPriorityManager::ChildID() const
     676             : {
     677             :   // We have to cache mContentParent->ChildID() instead of getting it from the
     678             :   // ContentParent each time because after ShutDown() is called, mContentParent
     679             :   // is null.  If we didn't cache ChildID(), then we wouldn't be able to run
     680             :   // LOGP() after ShutDown().
     681           0 :   return mChildID;
     682             : }
     683             : 
     684             : int32_t
     685           0 : ParticularProcessPriorityManager::Pid() const
     686             : {
     687           0 :   return mContentParent ? mContentParent->Pid() : -1;
     688             : }
     689             : 
     690             : const nsAutoCString&
     691           0 : ParticularProcessPriorityManager::NameWithComma()
     692             : {
     693           0 :   mNameWithComma.Truncate();
     694           0 :   if (!mContentParent) {
     695           0 :     return mNameWithComma; // empty string
     696             :   }
     697             : 
     698           0 :   nsAutoString name;
     699           0 :   mContentParent->FriendlyName(name);
     700           0 :   if (name.IsEmpty()) {
     701           0 :     return mNameWithComma; // empty string
     702             :   }
     703             : 
     704           0 :   mNameWithComma = NS_ConvertUTF16toUTF8(name);
     705           0 :   mNameWithComma.AppendLiteral(", ");
     706           0 :   return mNameWithComma;
     707             : }
     708             : 
     709             : void
     710           0 : ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
     711             : {
     712           0 :   nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
     713           0 :   NS_ENSURE_TRUE_VOID(fl);
     714             : 
     715           0 :   TabParent* tp = TabParent::GetFrom(fl);
     716           0 :   NS_ENSURE_TRUE_VOID(tp);
     717             : 
     718           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     719           0 :   if (tp->Manager() != mContentParent) {
     720           0 :     return;
     721             :   }
     722             : 
     723             :   // Ignore notifications that aren't from a Browser
     724             :   bool isMozBrowser;
     725           0 :   fl->GetOwnerIsMozBrowserFrame(&isMozBrowser);
     726           0 :   if (isMozBrowser) {
     727           0 :     ResetPriority();
     728             :   }
     729             : 
     730           0 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     731           0 :   if (os) {
     732           0 :     os->RemoveObserver(this, "remote-browser-shown");
     733             :   }
     734             : }
     735             : 
     736             : void
     737           0 : ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
     738             : {
     739           0 :   nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
     740           0 :   NS_ENSURE_TRUE_VOID(tp);
     741             : 
     742           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     743           0 :   if (TabParent::GetFrom(tp)->Manager() != mContentParent) {
     744           0 :     return;
     745             :   }
     746             : 
     747             :   uint64_t tabId;
     748           0 :   if (NS_WARN_IF(NS_FAILED(tp->GetTabId(&tabId)))) {
     749           0 :     return;
     750             :   }
     751             : 
     752           0 :   mActiveTabParents.RemoveEntry(tabId);
     753             : 
     754           0 :   ResetPriority();
     755             : }
     756             : 
     757             : void
     758           0 : ParticularProcessPriorityManager::ResetPriority()
     759             : {
     760           0 :   ProcessPriority processPriority = ComputePriority();
     761           0 :   if (mPriority == PROCESS_PRIORITY_UNKNOWN ||
     762           0 :       mPriority > processPriority) {
     763             :     // Apps set at a perceivable background priority are often playing media.
     764             :     // Most media will have short gaps while changing tracks between songs,
     765             :     // switching videos, etc.  Give these apps a longer grace period so they
     766             :     // can get their next track started, if there is one, before getting
     767             :     // downgraded.
     768           0 :     if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
     769           0 :       ScheduleResetPriority(BACKGROUND_PERCEIVABLE_GRACE_PERIOD);
     770             :     } else {
     771           0 :       ScheduleResetPriority(BACKGROUND_GRACE_PERIOD);
     772             :     }
     773           0 :     return;
     774             :   }
     775             : 
     776           0 :   SetPriorityNow(processPriority);
     777             : }
     778             : 
     779             : void
     780           0 : ParticularProcessPriorityManager::ResetPriorityNow()
     781             : {
     782           0 :   SetPriorityNow(ComputePriority());
     783           0 : }
     784             : 
     785             : void
     786           0 : ParticularProcessPriorityManager::ScheduleResetPriority(TimeoutPref aTimeoutPref)
     787             : {
     788           0 :   if (mResetPriorityTimer) {
     789           0 :     LOGP("ScheduleResetPriority bailing; the timer is already running.");
     790           0 :     return;
     791             :   }
     792             : 
     793           0 :   uint32_t timeout = 0;
     794           0 :   switch (aTimeoutPref) {
     795             :     case BACKGROUND_PERCEIVABLE_GRACE_PERIOD:
     796           0 :       timeout = sBackgroundPerceivableGracePeriodMS;
     797           0 :       break;
     798             :     case BACKGROUND_GRACE_PERIOD:
     799           0 :       timeout = sBackgroundGracePeriodMS;
     800           0 :       break;
     801             :     default:
     802           0 :       MOZ_ASSERT(false, "Unrecognized timeout pref");
     803             :       break;
     804             :   }
     805             : 
     806           0 :   LOGP("Scheduling reset timer to fire in %dms.", timeout);
     807           0 :   mResetPriorityTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     808           0 :   mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
     809             : }
     810             : 
     811             : NS_IMETHODIMP
     812           0 : ParticularProcessPriorityManager::Notify(nsITimer* aTimer)
     813             : {
     814           0 :   LOGP("Reset priority timer callback; about to ResetPriorityNow.");
     815           0 :   ResetPriorityNow();
     816           0 :   mResetPriorityTimer = nullptr;
     817           0 :   return NS_OK;
     818             : }
     819             : 
     820             : ProcessPriority
     821           0 : ParticularProcessPriorityManager::CurrentPriority()
     822             : {
     823           0 :   return mPriority;
     824             : }
     825             : 
     826             : ProcessPriority
     827           0 : ParticularProcessPriorityManager::ComputePriority()
     828             : {
     829           0 :   if (!mActiveTabParents.IsEmpty()) {
     830           0 :     return PROCESS_PRIORITY_FOREGROUND;
     831             :   }
     832             : 
     833           0 :   if (mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) {
     834           0 :     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
     835             :   }
     836             : 
     837           0 :   return PROCESS_PRIORITY_BACKGROUND;
     838             : }
     839             : 
     840             : void
     841           0 : ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority)
     842             : {
     843           0 :   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     844           0 :     MOZ_ASSERT(false);
     845             :     return;
     846             :   }
     847             : 
     848           0 :   if (!ProcessPriorityManagerImpl::PrefsEnabled() ||
     849           0 :       !mContentParent ||
     850           0 :       mPriority == aPriority) {
     851           0 :     return;
     852             :   }
     853             : 
     854           0 :   if (mPriority == aPriority) {
     855           0 :     hal::SetProcessPriority(Pid(), mPriority);
     856           0 :     return;
     857             :   }
     858             : 
     859           0 :   LOGP("Changing priority from %s to %s.",
     860             :        ProcessPriorityToString(mPriority),
     861             :        ProcessPriorityToString(aPriority));
     862             : 
     863           0 :   ProcessPriority oldPriority = mPriority;
     864             : 
     865           0 :   mPriority = aPriority;
     866           0 :   hal::SetProcessPriority(Pid(), mPriority);
     867             : 
     868           0 :   if (oldPriority != mPriority) {
     869             :     ProcessPriorityManagerImpl::GetSingleton()->
     870           0 :       NotifyProcessPriorityChanged(this, oldPriority);
     871             : 
     872           0 :     Unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
     873             :   }
     874             : 
     875           0 :   FireTestOnlyObserverNotification("process-priority-set",
     876           0 :     ProcessPriorityToString(mPriority));
     877             : }
     878             : 
     879             : void
     880           0 : ParticularProcessPriorityManager::TabActivityChanged(TabParent* aTabParent,
     881             :                                                      bool aIsActive)
     882             : {
     883           0 :   MOZ_ASSERT(aTabParent);
     884             : 
     885           0 :   if (!aIsActive) {
     886           0 :     mActiveTabParents.RemoveEntry(aTabParent->GetTabId());
     887             :   } else {
     888           0 :     mActiveTabParents.PutEntry(aTabParent->GetTabId());
     889             :   }
     890             : 
     891           0 :   ResetPriority();
     892           0 : }
     893             : 
     894             : void
     895           0 : ParticularProcessPriorityManager::ShutDown()
     896             : {
     897           0 :   MOZ_ASSERT(mContentParent);
     898             : 
     899           0 :   UnregisterWakeLockObserver(this);
     900             : 
     901           0 :   if (mResetPriorityTimer) {
     902           0 :     mResetPriorityTimer->Cancel();
     903           0 :     mResetPriorityTimer = nullptr;
     904             :   }
     905             : 
     906           0 :   mContentParent = nullptr;
     907           0 : }
     908             : 
     909             : void
     910           0 : ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
     911             :   const char* aTopic,
     912             :   const nsACString& aData /* = EmptyCString() */)
     913             : {
     914           0 :   if (!TestMode()) {
     915           0 :     return;
     916             :   }
     917             : 
     918           0 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     919           0 :   NS_ENSURE_TRUE_VOID(os);
     920             : 
     921           0 :   nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic);
     922             : 
     923           0 :   LOG("Notifying observer %s, data %s",
     924             :       topic.get(), PromiseFlatCString(aData).get());
     925           0 :   os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get());
     926             : }
     927             : 
     928             : void
     929           0 : ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
     930             :   const char* aTopic,
     931             :   const char* aData /* = nullptr */ )
     932             : {
     933           0 :   if (!ProcessPriorityManagerImpl::TestMode()) {
     934           0 :     return;
     935             :   }
     936             : 
     937           0 :   nsAutoCString data;
     938           0 :   if (aData) {
     939           0 :     data.AppendASCII(aData);
     940             :   }
     941             : 
     942           0 :   FireTestOnlyObserverNotification(aTopic, data);
     943             : }
     944             : 
     945             : void
     946           0 : ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
     947             :   const char* aTopic,
     948             :   const nsACString& aData /* = EmptyCString() */)
     949             : {
     950           0 :   if (!ProcessPriorityManagerImpl::TestMode()) {
     951           0 :     return;
     952             :   }
     953             : 
     954           0 :   nsAutoCString data(nsPrintfCString("%" PRIu64, ChildID()));
     955           0 :   if (!aData.IsEmpty()) {
     956           0 :     data.Append(':');
     957           0 :     data.Append(aData);
     958             :   }
     959             : 
     960             :   // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return
     961             :   // null, since ProcessPriorityManagerImpl is the only class which creates
     962             :   // ParticularProcessPriorityManagers.
     963             : 
     964             :   ProcessPriorityManagerImpl::GetSingleton()->
     965           0 :     FireTestOnlyObserverNotification(aTopic, data);
     966             : }
     967             : 
     968             : StaticRefPtr<ProcessPriorityManagerChild>
     969           3 : ProcessPriorityManagerChild::sSingleton;
     970             : 
     971             : /* static */ void
     972           3 : ProcessPriorityManagerChild::StaticInit()
     973             : {
     974           3 :   if (!sSingleton) {
     975           3 :     sSingleton = new ProcessPriorityManagerChild();
     976           3 :     sSingleton->Init();
     977           3 :     ClearOnShutdown(&sSingleton);
     978             :   }
     979           3 : }
     980             : 
     981             : /* static */ ProcessPriorityManagerChild*
     982           0 : ProcessPriorityManagerChild::Singleton()
     983             : {
     984           0 :   StaticInit();
     985           0 :   return sSingleton;
     986             : }
     987             : 
     988           5 : NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild, nsIObserver)
     989             : 
     990           3 : ProcessPriorityManagerChild::ProcessPriorityManagerChild()
     991             : {
     992           3 :   if (XRE_IsParentProcess()) {
     993           1 :     mCachedPriority = PROCESS_PRIORITY_MASTER;
     994             :   } else {
     995           2 :     mCachedPriority = PROCESS_PRIORITY_UNKNOWN;
     996             :   }
     997           3 : }
     998             : 
     999             : void
    1000           3 : ProcessPriorityManagerChild::Init()
    1001             : {
    1002             :   // The process priority should only be changed in child processes; don't even
    1003             :   // bother listening for changes if we're in the main process.
    1004           3 :   if (!XRE_IsParentProcess()) {
    1005           4 :     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    1006           2 :     NS_ENSURE_TRUE_VOID(os);
    1007           2 :     os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false);
    1008             :   }
    1009             : }
    1010             : 
    1011             : NS_IMETHODIMP
    1012           0 : ProcessPriorityManagerChild::Observe(nsISupports* aSubject,
    1013             :                                      const char* aTopic,
    1014             :                                      const char16_t* aData)
    1015             : {
    1016           0 :   MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed"));
    1017             : 
    1018           0 :   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
    1019           0 :   NS_ENSURE_TRUE(props, NS_OK);
    1020             : 
    1021           0 :   int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN);
    1022           0 :   props->GetPropertyAsInt32(NS_LITERAL_STRING("priority"), &priority);
    1023           0 :   NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK);
    1024             : 
    1025           0 :   mCachedPriority = static_cast<ProcessPriority>(priority);
    1026             : 
    1027           0 :   return NS_OK;
    1028             : }
    1029             : 
    1030             : bool
    1031           0 : ProcessPriorityManagerChild::CurrentProcessIsForeground()
    1032             : {
    1033           0 :   return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
    1034           0 :          mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
    1035             : }
    1036             : 
    1037             : } // namespace
    1038             : 
    1039             : namespace mozilla {
    1040             : 
    1041             : /* static */ void
    1042           3 : ProcessPriorityManager::Init()
    1043             : {
    1044           3 :   ProcessPriorityManagerImpl::StaticInit();
    1045           3 :   ProcessPriorityManagerChild::StaticInit();
    1046           3 :   ParticularProcessPriorityManager::StaticInit();
    1047           3 : }
    1048             : 
    1049             : /* static */ void
    1050           2 : ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
    1051             :                                            ProcessPriority aPriority)
    1052             : {
    1053           2 :   MOZ_ASSERT(aContentParent);
    1054             : 
    1055             :   ProcessPriorityManagerImpl* singleton =
    1056           2 :     ProcessPriorityManagerImpl::GetSingleton();
    1057           2 :   if (singleton) {
    1058           0 :     singleton->SetProcessPriority(aContentParent, aPriority);
    1059             :   }
    1060           2 : }
    1061             : 
    1062             : /* static */ bool
    1063           0 : ProcessPriorityManager::CurrentProcessIsForeground()
    1064             : {
    1065             :   return ProcessPriorityManagerChild::Singleton()->
    1066           0 :     CurrentProcessIsForeground();
    1067             : }
    1068             : 
    1069             : /* static */ void
    1070           2 : ProcessPriorityManager::TabActivityChanged(TabParent* aTabParent,
    1071             :                                            bool aIsActive)
    1072             : {
    1073           2 :   MOZ_ASSERT(aTabParent);
    1074             : 
    1075             :   ProcessPriorityManagerImpl* singleton =
    1076           2 :     ProcessPriorityManagerImpl::GetSingleton();
    1077           2 :   if (!singleton) {
    1078           2 :     return;
    1079             :   }
    1080             : 
    1081           0 :   singleton->TabActivityChanged(aTabParent, aIsActive);
    1082             : }
    1083             : 
    1084             : } // namespace mozilla

Generated by: LCOV version 1.13