dom/ipc/ProcessPriorityManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 "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/Preferences.h"
    14 #include "mozilla/Services.h"
    15 #include "mozilla/unused.h"
    16 #include "AudioChannelService.h"
    17 #include "prlog.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"
    29 #ifdef XP_WIN
    30 #include <process.h>
    31 #define getpid _getpid
    32 #else
    33 #include <unistd.h>
    34 #endif
    36 #ifdef LOG
    37 #undef LOG
    38 #endif
    40 // Use LOGP inside a ParticularProcessPriorityManager method; use LOG
    41 // everywhere else.  LOGP prints out information about the particular process
    42 // priority manager.
    43 //
    44 // (Wow, our logging story is a huge mess.)
    46 // #define ENABLE_LOGGING 1
    48 #if defined(ANDROID) && defined(ENABLE_LOGGING)
    49 #  include <android/log.h>
    50 #  define LOG(fmt, ...) \
    51      __android_log_print(ANDROID_LOG_INFO, \
    52        "Gecko:ProcessPriorityManager", \
    53        fmt, ## __VA_ARGS__)
    54 #  define LOGP(fmt, ...) \
    55     __android_log_print(ANDROID_LOG_INFO, \
    56       "Gecko:ProcessPriorityManager", \
    57       "[%schild-id=%llu, pid=%d] " fmt, \
    58       NameWithComma().get(), \
    59       (long long unsigned) ChildID(), Pid(), ## __VA_ARGS__)
    61 #elif defined(ENABLE_LOGGING)
    62 #  define LOG(fmt, ...) \
    63      printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__)
    64 #  define LOGP(fmt, ...) \
    65      printf("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt "\n", \
    66        NameWithComma().get(), \
    67        (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__)
    69 #elif defined(PR_LOGGING)
    70   static PRLogModuleInfo*
    71   GetPPMLog()
    72   {
    73     static PRLogModuleInfo *sLog;
    74     if (!sLog)
    75       sLog = PR_NewLogModule("ProcessPriorityManager");
    76     return sLog;
    77   }
    78 #  define LOG(fmt, ...) \
    79      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
    80             ("ProcessPriorityManager - " fmt, ##__VA_ARGS__))
    81 #  define LOGP(fmt, ...) \
    82      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
    83             ("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt, \
    84             NameWithComma().get(), \
    85             (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__))
    86 #else
    87 #define LOG(fmt, ...)
    88 #define LOGP(fmt, ...)
    89 #endif
    91 using namespace mozilla;
    92 using namespace mozilla::dom;
    93 using namespace mozilla::hal;
    95 namespace {
    97 class ParticularProcessPriorityManager;
    99 /**
   100  * This singleton class does the work to implement the process priority manager
   101  * in the main process.  This class may not be used in child processes.  (You
   102  * can call StaticInit, but it won't do anything, and GetSingleton() will
   103  * return null.)
   104  *
   105  * ProcessPriorityManager::CurrentProcessIsForeground() and
   106  * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
   107  * any process, are handled separately, by the ProcessPriorityManagerChild
   108  * class.
   109  */
   110 class ProcessPriorityManagerImpl MOZ_FINAL
   111   : public nsIObserver
   112 {
   113 public:
   114   /**
   115    * If we're in the main process, get the ProcessPriorityManagerImpl
   116    * singleton.  If we're in a child process, return null.
   117    */
   118   static ProcessPriorityManagerImpl* GetSingleton();
   120   static void StaticInit();
   121   static bool PrefsEnabled();
   123   NS_DECL_ISUPPORTS
   124   NS_DECL_NSIOBSERVER
   126   /**
   127    * This function implements ProcessPriorityManager::SetProcessPriority.
   128    */
   129   void SetProcessPriority(ContentParent* aContentParent,
   130                           ProcessPriority aPriority,
   131                           uint32_t aBackgroundLRU = 0);
   133   /**
   134    * If a magic testing-only pref is set, notify the observer service on the
   135    * given topic with the given data.  This is used for testing
   136    */
   137   void FireTestOnlyObserverNotification(const char* aTopic,
   138                                         const nsACString& aData = EmptyCString());
   140   /**
   141    * Does some process, other than the one handled by aParticularManager, have
   142    * priority FOREGROUND_HIGH?
   143    */
   144   bool OtherProcessHasHighPriority(
   145     ParticularProcessPriorityManager* aParticularManager);
   147   /**
   148    * Does one of the child processes have priority FOREGROUND_HIGH?
   149    */
   150   bool ChildProcessHasHighPriority();
   152   /**
   153    * This must be called by a ParticularProcessPriorityManager when it changes
   154    * its priority.
   155    */
   156   void NotifyProcessPriorityChanged(
   157     ParticularProcessPriorityManager* aParticularManager,
   158     hal::ProcessPriority aOldPriority);
   160 private:
   161   static bool sPrefListenersRegistered;
   162   static bool sInitialized;
   163   static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
   165   static void PrefChangedCallback(const char* aPref, void* aClosure);
   167   ProcessPriorityManagerImpl();
   168   ~ProcessPriorityManagerImpl() {}
   169   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl);
   171   void Init();
   173   already_AddRefed<ParticularProcessPriorityManager>
   174   GetParticularProcessPriorityManager(ContentParent* aContentParent);
   176   void ObserveContentParentCreated(nsISupports* aContentParent);
   177   void ObserveContentParentDestroyed(nsISupports* aSubject);
   179   nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
   180     mParticularManagers;
   182   nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
   183 };
   185 /**
   186  * This singleton class implements the parts of the process priority manager
   187  * that are available from all processes.
   188  */
   189 class ProcessPriorityManagerChild MOZ_FINAL
   190   : public nsIObserver
   191 {
   192 public:
   193   static void StaticInit();
   194   static ProcessPriorityManagerChild* Singleton();
   196   NS_DECL_ISUPPORTS
   197   NS_DECL_NSIOBSERVER
   199   bool CurrentProcessIsForeground();
   200   bool CurrentProcessIsHighPriority();
   202 private:
   203   static StaticRefPtr<ProcessPriorityManagerChild> sSingleton;
   205   ProcessPriorityManagerChild();
   206   ~ProcessPriorityManagerChild() {}
   207   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild);
   209   void Init();
   211   hal::ProcessPriority mCachedPriority;
   212 };
   214 /**
   215  * This class manages the priority of one particular process.  It is
   216  * main-process only.
   217  */
   218 class ParticularProcessPriorityManager MOZ_FINAL
   219   : public WakeLockObserver
   220   , public nsIObserver
   221   , public nsITimerCallback
   222   , public nsSupportsWeakReference
   223 {
   224 public:
   225   ParticularProcessPriorityManager(ContentParent* aContentParent);
   226   ~ParticularProcessPriorityManager();
   228   NS_DECL_ISUPPORTS
   229   NS_DECL_NSIOBSERVER
   230   NS_DECL_NSITIMERCALLBACK
   232   virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE;
   233   void Init();
   235   int32_t Pid() const;
   236   uint64_t ChildID() const;
   237   bool IsPreallocated() const;
   239   /**
   240    * Used in logging, this method returns the ContentParent's name followed by
   241    * ", ".  If we can't get the ContentParent's name for some reason, it
   242    * returns an empty string.
   243    *
   244    * The reference returned here is guaranteed to be live until the next call
   245    * to NameWithComma() or until the ParticularProcessPriorityManager is
   246    * destroyed, whichever comes first.
   247    */
   248   const nsAutoCString& NameWithComma();
   250   bool HasAppType(const char* aAppType);
   251   bool IsExpectingSystemMessage();
   253   void OnAudioChannelProcessChanged(nsISupports* aSubject);
   254   void OnRemoteBrowserFrameShown(nsISupports* aSubject);
   255   void OnTabParentDestroyed(nsISupports* aSubject);
   256   void OnFrameloaderVisibleChanged(nsISupports* aSubject);
   257   void OnChannelConnected(nsISupports* aSubject);
   259   ProcessPriority CurrentPriority();
   260   ProcessPriority ComputePriority();
   261   ProcessCPUPriority ComputeCPUPriority(ProcessPriority aPriority);
   263   void ScheduleResetPriority(const char* aTimeoutPref);
   264   void ResetPriority();
   265   void ResetPriorityNow();
   266   void ResetCPUPriorityNow();
   268   /**
   269    * This overload is equivalent to SetPriorityNow(aPriority,
   270    * ComputeCPUPriority()).
   271    */
   272   void SetPriorityNow(ProcessPriority aPriority,
   273                       uint32_t aBackgroundLRU = 0);
   275   void SetPriorityNow(ProcessPriority aPriority,
   276                       ProcessCPUPriority aCPUPriority,
   277                       uint32_t aBackgroundLRU = 0);
   279   void ShutDown();
   281 private:
   282   void FireTestOnlyObserverNotification(
   283     const char* aTopic,
   284     const nsACString& aData = EmptyCString());
   286   void FireTestOnlyObserverNotification(
   287     const char* aTopic,
   288     const char* aData = nullptr);
   290   ContentParent* mContentParent;
   291   uint64_t mChildID;
   292   ProcessPriority mPriority;
   293   ProcessCPUPriority mCPUPriority;
   294   bool mHoldsCPUWakeLock;
   295   bool mHoldsHighPriorityWakeLock;
   297   /**
   298    * Used to implement NameWithComma().
   299    */
   300   nsAutoCString mNameWithComma;
   302   nsCOMPtr<nsITimer> mResetPriorityTimer;
   303 };
   305 class BackgroundProcessLRUPool MOZ_FINAL
   306 {
   307 public:
   308   static BackgroundProcessLRUPool* Singleton();
   310   /**
   311    * Used to remove a ContentParent from background LRU pool when
   312    * it is destroyed or its priority changed from BACKGROUND to others.
   313    */
   314   void RemoveFromBackgroundLRUPool(ContentParent* aContentParent);
   316   /**
   317    * Used to add a ContentParent into background LRU pool when
   318    * its priority changed to BACKGROUND from others.
   319    */
   320   void AddIntoBackgroundLRUPool(ContentParent* aContentParent);
   322 private:
   323   static StaticAutoPtr<BackgroundProcessLRUPool> sSingleton;
   325   int32_t mLRUPoolLevels;
   326   int32_t mLRUPoolSize;
   327   int32_t mLRUPoolAvailableIndex;
   328   nsTArray<ContentParent*> mLRUPool;
   330   uint32_t CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex);
   332   nsresult UpdateAvailableIndexInLRUPool(
   333       ContentParent* aContentParent,
   334       int32_t aTargetIndex = -1);
   336   void ShiftLRUPool();
   338   void EnsureLRUPool();
   340   BackgroundProcessLRUPool();
   341   DISALLOW_EVIL_CONSTRUCTORS(BackgroundProcessLRUPool);
   343 };
   345 /* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
   346 /* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
   347 /* static */ StaticRefPtr<ProcessPriorityManagerImpl>
   348   ProcessPriorityManagerImpl::sSingleton;
   350 NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl,
   351                   nsIObserver);
   353 /* static */ void
   354 ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref,
   355                                                 void* aClosure)
   356 {
   357   StaticInit();
   358 }
   360 /* static */ bool
   361 ProcessPriorityManagerImpl::PrefsEnabled()
   362 {
   363   return Preferences::GetBool("dom.ipc.processPriorityManager.enabled") &&
   364          !Preferences::GetBool("dom.ipc.tabs.disabled");
   365 }
   367 /* static */ void
   368 ProcessPriorityManagerImpl::StaticInit()
   369 {
   370   if (sInitialized) {
   371     return;
   372   }
   374   // The process priority manager is main-process only.
   375   if (XRE_GetProcessType() != GeckoProcessType_Default) {
   376     sInitialized = true;
   377     return;
   378   }
   380   // If IPC tabs aren't enabled at startup, don't bother with any of this.
   381   if (!PrefsEnabled()) {
   382     LOG("InitProcessPriorityManager bailing due to prefs.");
   384     // Run StaticInit() again if the prefs change.  We don't expect this to
   385     // happen in normal operation, but it happens during testing.
   386     if (!sPrefListenersRegistered) {
   387       sPrefListenersRegistered = true;
   388       Preferences::RegisterCallback(PrefChangedCallback,
   389                                     "dom.ipc.processPriorityManager.enabled");
   390       Preferences::RegisterCallback(PrefChangedCallback,
   391                                     "dom.ipc.tabs.disabled");
   392     }
   393     return;
   394   }
   396   sInitialized = true;
   398   sSingleton = new ProcessPriorityManagerImpl();
   399   sSingleton->Init();
   400   ClearOnShutdown(&sSingleton);
   401 }
   403 /* static */ ProcessPriorityManagerImpl*
   404 ProcessPriorityManagerImpl::GetSingleton()
   405 {
   406   if (!sSingleton) {
   407     StaticInit();
   408   }
   410   return sSingleton;
   411 }
   413 ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
   414 {
   415   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   416 }
   418 void
   419 ProcessPriorityManagerImpl::Init()
   420 {
   421   LOG("Starting up.  This is the master process.");
   423   // The master process's priority never changes; set it here and then forget
   424   // about it.  We'll manage only subprocesses' priorities using the process
   425   // priority manager.
   426   hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER,
   427                           PROCESS_CPU_PRIORITY_NORMAL);
   429   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   430   if (os) {
   431     os->AddObserver(this, "ipc:content-created", /* ownsWeak */ false);
   432     os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ false);
   433   }
   434 }
   436 NS_IMETHODIMP
   437 ProcessPriorityManagerImpl::Observe(
   438   nsISupports* aSubject,
   439   const char* aTopic,
   440   const char16_t* aData)
   441 {
   442   nsDependentCString topic(aTopic);
   443   if (topic.EqualsLiteral("ipc:content-created")) {
   444     ObserveContentParentCreated(aSubject);
   445   } else if (topic.EqualsLiteral("ipc:content-shutdown")) {
   446     ObserveContentParentDestroyed(aSubject);
   447   } else {
   448     MOZ_ASSERT(false);
   449   }
   451   return NS_OK;
   452 }
   454 already_AddRefed<ParticularProcessPriorityManager>
   455 ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
   456   ContentParent* aContentParent)
   457 {
   458   nsRefPtr<ParticularProcessPriorityManager> pppm;
   459   mParticularManagers.Get(aContentParent->ChildID(), &pppm);
   460   if (!pppm) {
   461     pppm = new ParticularProcessPriorityManager(aContentParent);
   462     pppm->Init();
   463     mParticularManagers.Put(aContentParent->ChildID(), pppm);
   465     FireTestOnlyObserverNotification("process-created",
   466       nsPrintfCString("%lld", aContentParent->ChildID()));
   467   }
   469   return pppm.forget();
   470 }
   472 void
   473 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
   474                                                ProcessPriority aPriority,
   475                                                uint32_t aBackgroundLRU)
   476 {
   477   MOZ_ASSERT(aContentParent);
   478   nsRefPtr<ParticularProcessPriorityManager> pppm =
   479     GetParticularProcessPriorityManager(aContentParent);
   480   pppm->SetPriorityNow(aPriority, aBackgroundLRU);
   481 }
   483 void
   484 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   485   nsISupports* aContentParent)
   486 {
   487   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   488   // don't leak the already_AddRefed object.
   489   nsCOMPtr<nsIObserver> cp = do_QueryInterface(aContentParent);
   490   nsRefPtr<ParticularProcessPriorityManager> pppm =
   491     GetParticularProcessPriorityManager(static_cast<ContentParent*>(cp.get()));
   492 }
   494 static PLDHashOperator
   495 EnumerateParticularProcessPriorityManagers(
   496   const uint64_t& aKey,
   497   nsRefPtr<ParticularProcessPriorityManager> aValue,
   498   void* aUserData)
   499 {
   500   nsTArray<nsRefPtr<ParticularProcessPriorityManager> >* aArray =
   501     static_cast<nsTArray<nsRefPtr<ParticularProcessPriorityManager> >*>(aUserData);
   502   aArray->AppendElement(aValue);
   503   return PL_DHASH_NEXT;
   504 }
   506 void
   507 ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
   508 {
   509   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   510   NS_ENSURE_TRUE_VOID(props);
   512   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   513   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   514   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
   516   nsRefPtr<ParticularProcessPriorityManager> pppm;
   517   mParticularManagers.Get(childID, &pppm);
   518   MOZ_ASSERT(pppm);
   519   if (pppm) {
   520     pppm->ShutDown();
   521   }
   523   mParticularManagers.Remove(childID);
   525   if (mHighPriorityChildIDs.Contains(childID)) {
   526     mHighPriorityChildIDs.RemoveEntry(childID);
   528     // We just lost a high-priority process; reset everyone's CPU priorities.
   529     nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
   530     mParticularManagers.EnumerateRead(
   531       &EnumerateParticularProcessPriorityManagers,
   532       &pppms);
   534     for (uint32_t i = 0; i < pppms.Length(); i++) {
   535       pppms[i]->ResetCPUPriorityNow();
   536     }
   537   }
   538 }
   540 bool
   541 ProcessPriorityManagerImpl::OtherProcessHasHighPriority(
   542   ParticularProcessPriorityManager* aParticularManager)
   543 {
   544   if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) {
   545     return mHighPriorityChildIDs.Count() > 1;
   546   }
   547   return mHighPriorityChildIDs.Count() > 0;
   548 }
   550 bool
   551 ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void )
   552 {
   553   return mHighPriorityChildIDs.Count() > 0;
   554 }
   556 void
   557 ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
   558   ParticularProcessPriorityManager* aParticularManager,
   559   ProcessPriority aOldPriority)
   560 {
   561   // This priority change can only affect other processes' priorities if we're
   562   // changing to/from FOREGROUND_HIGH.
   564   if (aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
   565       aParticularManager->CurrentPriority() <
   566         PROCESS_PRIORITY_FOREGROUND_HIGH) {
   568     return;
   569   }
   571   if (aParticularManager->CurrentPriority() >=
   572       PROCESS_PRIORITY_FOREGROUND_HIGH) {
   573     mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID());
   574   } else {
   575     mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
   576   }
   578   nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
   579   mParticularManagers.EnumerateRead(
   580     &EnumerateParticularProcessPriorityManagers,
   581     &pppms);
   583   for (uint32_t i = 0; i < pppms.Length(); i++) {
   584     if (pppms[i] != aParticularManager) {
   585       pppms[i]->ResetCPUPriorityNow();
   586     }
   587   }
   588 }
   590 NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
   591                   nsIObserver,
   592                   nsITimerCallback,
   593                   nsISupportsWeakReference);
   595 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   596   ContentParent* aContentParent)
   597   : mContentParent(aContentParent)
   598   , mChildID(aContentParent->ChildID())
   599   , mPriority(PROCESS_PRIORITY_UNKNOWN)
   600   , mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
   601   , mHoldsCPUWakeLock(false)
   602   , mHoldsHighPriorityWakeLock(false)
   603 {
   604   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   605   LOGP("Creating ParticularProcessPriorityManager.");
   606 }
   608 void
   609 ParticularProcessPriorityManager::Init()
   610 {
   611   RegisterWakeLockObserver(this);
   613   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   614   if (os) {
   615     os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true);
   616     os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
   617     os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
   618     os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
   619   }
   621   // This process may already hold the CPU lock; for example, our parent may
   622   // have acquired it on our behalf.
   623   WakeLockInformation info1, info2;
   624   GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
   625   mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID());
   627   GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2);
   628   mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID());
   629   LOGP("Done starting up.  mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
   630        mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
   631 }
   633 ParticularProcessPriorityManager::~ParticularProcessPriorityManager()
   634 {
   635   LOGP("Destroying ParticularProcessPriorityManager.");
   637   // Unregister our wake lock observer if ShutDown hasn't been called.  (The
   638   // wake lock observer takes raw refs, so we don't want to take chances here!)
   639   // We don't call UnregisterWakeLockObserver unconditionally because the code
   640   // will print a warning if it's called unnecessarily.
   642   if (mContentParent) {
   643     UnregisterWakeLockObserver(this);
   644   }
   645 }
   647 /* virtual */ void
   648 ParticularProcessPriorityManager::Notify(const WakeLockInformation& aInfo)
   649 {
   650   if (!mContentParent) {
   651     // We've been shut down.
   652     return;
   653   }
   655   bool* dest = nullptr;
   656   if (aInfo.topic().EqualsLiteral("cpu")) {
   657     dest = &mHoldsCPUWakeLock;
   658   } else if (aInfo.topic().EqualsLiteral("high-priority")) {
   659     dest = &mHoldsHighPriorityWakeLock;
   660   }
   662   if (dest) {
   663     bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID());
   664     if (thisProcessLocks != *dest) {
   665       *dest = thisProcessLocks;
   666       LOGP("Got wake lock changed event. "
   667            "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
   668            mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
   669       ResetPriority();
   670     }
   671   }
   672 }
   674 NS_IMETHODIMP
   675 ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
   676                                           const char* aTopic,
   677                                           const char16_t* aData)
   678 {
   679   if (!mContentParent) {
   680     // We've been shut down.
   681     return NS_OK;
   682   }
   684   nsDependentCString topic(aTopic);
   686   if (topic.EqualsLiteral("audio-channel-process-changed")) {
   687     OnAudioChannelProcessChanged(aSubject);
   688   } else if (topic.EqualsLiteral("remote-browser-shown")) {
   689     OnRemoteBrowserFrameShown(aSubject);
   690   } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
   691     OnTabParentDestroyed(aSubject);
   692   } else if (topic.EqualsLiteral("frameloader-visible-changed")) {
   693     OnFrameloaderVisibleChanged(aSubject);
   694   } else {
   695     MOZ_ASSERT(false);
   696   }
   698   return NS_OK;
   699 }
   701 uint64_t
   702 ParticularProcessPriorityManager::ChildID() const
   703 {
   704   // We have to cache mContentParent->ChildID() instead of getting it from the
   705   // ContentParent each time because after ShutDown() is called, mContentParent
   706   // is null.  If we didn't cache ChildID(), then we wouldn't be able to run
   707   // LOGP() after ShutDown().
   708   return mChildID;
   709 }
   711 int32_t
   712 ParticularProcessPriorityManager::Pid() const
   713 {
   714   return mContentParent ? mContentParent->Pid() : -1;
   715 }
   717 bool
   718 ParticularProcessPriorityManager::IsPreallocated() const
   719 {
   720   return mContentParent ? mContentParent->IsPreallocated() : false;
   721 }
   723 const nsAutoCString&
   724 ParticularProcessPriorityManager::NameWithComma()
   725 {
   726   mNameWithComma.Truncate();
   727   if (!mContentParent) {
   728     return mNameWithComma; // empty string
   729   }
   731   nsAutoString name;
   732   mContentParent->FriendlyName(name);
   733   if (name.IsEmpty()) {
   734     return mNameWithComma; // empty string
   735   }
   737   mNameWithComma = NS_ConvertUTF16toUTF8(name);
   738   mNameWithComma.AppendLiteral(", ");
   739   return mNameWithComma;
   740 }
   742 void
   743 ParticularProcessPriorityManager::OnAudioChannelProcessChanged(nsISupports* aSubject)
   744 {
   745   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   746   NS_ENSURE_TRUE_VOID(props);
   748   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   749   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   750   if (childID == ChildID()) {
   751     ResetPriority();
   752   }
   753 }
   755 void
   756 ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
   757 {
   758   nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
   759   NS_ENSURE_TRUE_VOID(fl);
   761   // Ignore notifications that aren't from a BrowserOrApp
   762   bool isBrowserOrApp;
   763   fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
   764   if (!isBrowserOrApp) {
   765     return;
   766   }
   768   nsCOMPtr<nsITabParent> tp;
   769   fl->GetTabParent(getter_AddRefs(tp));
   770   NS_ENSURE_TRUE_VOID(tp);
   772   if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
   773     return;
   774   }
   776   ResetPriority();
   777 }
   779 void
   780 ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
   781 {
   782   nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
   783   NS_ENSURE_TRUE_VOID(tp);
   785   if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
   786     return;
   787   }
   789   ResetPriority();
   790 }
   792 void
   793 ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubject)
   794 {
   795   nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
   796   NS_ENSURE_TRUE_VOID(fl);
   798   nsCOMPtr<nsITabParent> tp;
   799   fl->GetTabParent(getter_AddRefs(tp));
   800   if (!tp) {
   801     return;
   802   }
   804   if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
   805     return;
   806   }
   808   // Most of the time when something changes in a process we call
   809   // ResetPriority(), giving a grace period before downgrading its priority.
   810   // But notice that here don't give a grace period: We call ResetPriorityNow()
   811   // instead.
   812   //
   813   // We do this because we're reacting here to a setVisibility() call, which is
   814   // an explicit signal from the process embedder that we should re-prioritize
   815   // a process.  If we gave a grace period in response to setVisibility()
   816   // calls, it would be impossible for the embedder to explicitly prioritize
   817   // processes and prevent e.g. the case where we switch which process is in
   818   // the foreground and, during the old fg processs's grace period, it OOMs the
   819   // new fg process.
   821   ResetPriorityNow();
   822 }
   824 void
   825 ParticularProcessPriorityManager::ResetPriority()
   826 {
   827   ProcessPriority processPriority = ComputePriority();
   828   if (mPriority == PROCESS_PRIORITY_UNKNOWN ||
   829       mPriority > processPriority) {
   830     // Apps set at a perceivable background priority are often playing media.
   831     // Most media will have short gaps while changing tracks between songs,
   832     // switching videos, etc.  Give these apps a longer grace period so they
   833     // can get their next track started, if there is one, before getting
   834     // downgraded.
   835     if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
   836       ScheduleResetPriority("backgroundPerceivableGracePeriodMS");
   837     } else {
   838       ScheduleResetPriority("backgroundGracePeriodMS");
   839     }
   840     return;
   841   }
   843   SetPriorityNow(processPriority);
   844 }
   846 void
   847 ParticularProcessPriorityManager::ResetPriorityNow()
   848 {
   849   SetPriorityNow(ComputePriority());
   850 }
   852 void
   853 ParticularProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref)
   854 {
   855   if (mResetPriorityTimer) {
   856     LOGP("ScheduleResetPriority bailing; the timer is already running.");
   857     return;
   858   }
   860   uint32_t timeout = Preferences::GetUint(
   861     nsPrintfCString("dom.ipc.processPriorityManager.%s", aTimeoutPref).get());
   862   LOGP("Scheduling reset timer to fire in %dms.", timeout);
   863   mResetPriorityTimer = do_CreateInstance("@mozilla.org/timer;1");
   864   mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
   865 }
   868 NS_IMETHODIMP
   869 ParticularProcessPriorityManager::Notify(nsITimer* aTimer)
   870 {
   871   LOGP("Reset priority timer callback; about to ResetPriorityNow.");
   872   ResetPriorityNow();
   873   mResetPriorityTimer = nullptr;
   874   return NS_OK;
   875 }
   877 bool
   878 ParticularProcessPriorityManager::HasAppType(const char* aAppType)
   879 {
   880   const InfallibleTArray<PBrowserParent*>& browsers =
   881     mContentParent->ManagedPBrowserParent();
   882   for (uint32_t i = 0; i < browsers.Length(); i++) {
   883     nsAutoString appType;
   884     static_cast<TabParent*>(browsers[i])->GetAppType(appType);
   885     if (appType.EqualsASCII(aAppType)) {
   886       return true;
   887     }
   888   }
   890   return false;
   891 }
   893 bool
   894 ParticularProcessPriorityManager::IsExpectingSystemMessage()
   895 {
   896   const InfallibleTArray<PBrowserParent*>& browsers =
   897     mContentParent->ManagedPBrowserParent();
   898   for (uint32_t i = 0; i < browsers.Length(); i++) {
   899     TabParent* tp = static_cast<TabParent*>(browsers[i]);
   900     nsCOMPtr<nsIMozBrowserFrame> bf = do_QueryInterface(tp->GetOwnerElement());
   901     if (!bf) {
   902       continue;
   903     }
   905     if (bf->GetIsExpectingSystemMessage()) {
   906       return true;
   907     }
   908   }
   910   return false;
   911 }
   913 ProcessPriority
   914 ParticularProcessPriorityManager::CurrentPriority()
   915 {
   916   return mPriority;
   917 }
   919 ProcessPriority
   920 ParticularProcessPriorityManager::ComputePriority()
   921 {
   922   if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
   923       HasAppType("critical")) {
   924     return PROCESS_PRIORITY_FOREGROUND_HIGH;
   925   }
   927   bool isVisible = false;
   928   const InfallibleTArray<PBrowserParent*>& browsers =
   929     mContentParent->ManagedPBrowserParent();
   930   for (uint32_t i = 0; i < browsers.Length(); i++) {
   931     if (static_cast<TabParent*>(browsers[i])->IsVisible()) {
   932       isVisible = true;
   933       break;
   934     }
   935   }
   937   if (isVisible) {
   938     return HasAppType("keyboard") ?
   939       PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
   940       PROCESS_PRIORITY_FOREGROUND;
   941   }
   943   if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
   944       IsExpectingSystemMessage()) {
   945     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   946   }
   948   AudioChannelService* service = AudioChannelService::GetAudioChannelService();
   949   if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
   950     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   951   }
   953   return HasAppType("homescreen") ?
   954          PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
   955          PROCESS_PRIORITY_BACKGROUND;
   956 }
   958 ProcessCPUPriority
   959 ParticularProcessPriorityManager::ComputeCPUPriority(ProcessPriority aPriority)
   960 {
   961   if (aPriority == PROCESS_PRIORITY_PREALLOC) {
   962     return PROCESS_CPU_PRIORITY_LOW;
   963   }
   965   if (aPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
   966     return PROCESS_CPU_PRIORITY_NORMAL;
   967   }
   969   return ProcessPriorityManagerImpl::GetSingleton()->
   970     OtherProcessHasHighPriority(this) ?
   971     PROCESS_CPU_PRIORITY_LOW :
   972     PROCESS_CPU_PRIORITY_NORMAL;
   973 }
   975 void
   976 ParticularProcessPriorityManager::ResetCPUPriorityNow()
   977 {
   978   SetPriorityNow(mPriority);
   979 }
   981 void
   982 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
   983                                                  uint32_t aBackgroundLRU)
   984 {
   985   SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU);
   986 }
   988 void
   989 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
   990                                                  ProcessCPUPriority aCPUPriority,
   991                                                  uint32_t aBackgroundLRU)
   992 {
   993   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
   994     MOZ_ASSERT(false);
   995     return;
   996   }
   998 #ifdef MOZ_NUWA_PROCESS
   999   // Do not attempt to change the priority of the Nuwa process
  1000   if (mContentParent->IsNuwaProcess()) {
  1001     return;
  1003 #endif
  1005   if (aBackgroundLRU > 0 &&
  1006       aPriority == PROCESS_PRIORITY_BACKGROUND &&
  1007       mPriority == PROCESS_PRIORITY_BACKGROUND) {
  1008     hal::SetProcessPriority(Pid(), mPriority, mCPUPriority, aBackgroundLRU);
  1010     nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d",
  1011       ProcessPriorityToString(mPriority, mCPUPriority),
  1012       aBackgroundLRU);
  1014     FireTestOnlyObserverNotification("process-priority-with-background-LRU-set",
  1015       ProcessPriorityWithBackgroundLRU.get());
  1018   if (!mContentParent ||
  1019       !ProcessPriorityManagerImpl::PrefsEnabled() ||
  1020       (mPriority == aPriority && mCPUPriority == aCPUPriority)) {
  1021     return;
  1024   // If the prefs were disabled after this ParticularProcessPriorityManager was
  1025   // created, we can at least avoid any further calls to
  1026   // hal::SetProcessPriority.  Supporting dynamic enabling/disabling of the
  1027   // ProcessPriorityManager is mostly for testing.
  1028   if (!ProcessPriorityManagerImpl::PrefsEnabled()) {
  1029     return;
  1032   if (aPriority == PROCESS_PRIORITY_BACKGROUND &&
  1033       mPriority != PROCESS_PRIORITY_BACKGROUND &&
  1034       !IsPreallocated()) {
  1035     ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent);
  1038   if (aPriority != PROCESS_PRIORITY_BACKGROUND &&
  1039       mPriority == PROCESS_PRIORITY_BACKGROUND &&
  1040       !IsPreallocated()) {
  1041     ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
  1044   LOGP("Changing priority from %s to %s.",
  1045        ProcessPriorityToString(mPriority, mCPUPriority),
  1046        ProcessPriorityToString(aPriority, aCPUPriority));
  1048   ProcessPriority oldPriority = mPriority;
  1050   mPriority = aPriority;
  1051   mCPUPriority = aCPUPriority;
  1052   hal::SetProcessPriority(Pid(), mPriority, mCPUPriority);
  1054   if (oldPriority != mPriority) {
  1055     unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
  1058   if (aPriority < PROCESS_PRIORITY_FOREGROUND) {
  1059     unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("low-memory"));
  1062   FireTestOnlyObserverNotification("process-priority-set",
  1063     ProcessPriorityToString(mPriority, mCPUPriority));
  1065   if (oldPriority != mPriority) {
  1066     ProcessPriorityManagerImpl::GetSingleton()->
  1067       NotifyProcessPriorityChanged(this, oldPriority);
  1071 void
  1072 ParticularProcessPriorityManager::ShutDown()
  1074   MOZ_ASSERT(mContentParent);
  1076   UnregisterWakeLockObserver(this);
  1078   if (mResetPriorityTimer) {
  1079     mResetPriorityTimer->Cancel();
  1080     mResetPriorityTimer = nullptr;
  1083   if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
  1084     ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
  1087   mContentParent = nullptr;
  1090 void
  1091 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
  1092   const char* aTopic,
  1093   const nsACString& aData /* = EmptyCString() */)
  1095   if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
  1096     return;
  1099   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  1100   NS_ENSURE_TRUE_VOID(os);
  1102   nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic);
  1104   LOG("Notifying observer %s, data %s",
  1105       topic.get(), PromiseFlatCString(aData).get());
  1106   os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get());
  1109 void
  1110 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
  1111   const char* aTopic,
  1112   const char* aData /* = nullptr */ )
  1114   if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
  1115     return;
  1118   nsAutoCString data;
  1119   if (aData) {
  1120     data.AppendASCII(aData);
  1123   FireTestOnlyObserverNotification(aTopic, data);
  1126 void
  1127 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
  1128   const char* aTopic,
  1129   const nsACString& aData /* = EmptyCString() */)
  1131   if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
  1132     return;
  1135   nsAutoCString data(nsPrintfCString("%lld", ChildID()));
  1136   if (!aData.IsEmpty()) {
  1137     data.AppendLiteral(":");
  1138     data.Append(aData);
  1141   // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return
  1142   // null, since ProcessPriorityManagerImpl is the only class which creates
  1143   // ParticularProcessPriorityManagers.
  1145   ProcessPriorityManagerImpl::GetSingleton()->
  1146     FireTestOnlyObserverNotification(aTopic, data);
  1149 StaticRefPtr<ProcessPriorityManagerChild>
  1150 ProcessPriorityManagerChild::sSingleton;
  1152 /* static */ void
  1153 ProcessPriorityManagerChild::StaticInit()
  1155   if (!sSingleton) {
  1156     sSingleton = new ProcessPriorityManagerChild();
  1157     sSingleton->Init();
  1158     ClearOnShutdown(&sSingleton);
  1162 /* static */ ProcessPriorityManagerChild*
  1163 ProcessPriorityManagerChild::Singleton()
  1165   StaticInit();
  1166   return sSingleton;
  1169 NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild,
  1170                   nsIObserver)
  1172 ProcessPriorityManagerChild::ProcessPriorityManagerChild()
  1174   if (XRE_GetProcessType() == GeckoProcessType_Default) {
  1175     mCachedPriority = PROCESS_PRIORITY_MASTER;
  1176   } else {
  1177     mCachedPriority = PROCESS_PRIORITY_UNKNOWN;
  1181 void
  1182 ProcessPriorityManagerChild::Init()
  1184   // The process priority should only be changed in child processes; don't even
  1185   // bother listening for changes if we're in the main process.
  1186   if (XRE_GetProcessType() != GeckoProcessType_Default) {
  1187     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  1188     NS_ENSURE_TRUE_VOID(os);
  1189     os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false);
  1193 NS_IMETHODIMP
  1194 ProcessPriorityManagerChild::Observe(
  1195   nsISupports* aSubject,
  1196   const char* aTopic,
  1197   const char16_t* aData)
  1199   MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed"));
  1201   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
  1202   NS_ENSURE_TRUE(props, NS_OK);
  1204   int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN);
  1205   props->GetPropertyAsInt32(NS_LITERAL_STRING("priority"), &priority);
  1206   NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK);
  1208   mCachedPriority = static_cast<ProcessPriority>(priority);
  1210   return NS_OK;
  1213 bool
  1214 ProcessPriorityManagerChild::CurrentProcessIsForeground()
  1216   return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
  1217          mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
  1220 bool
  1221 ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
  1223   return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
  1224          mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
  1227 /* static */ StaticAutoPtr<BackgroundProcessLRUPool>
  1228 BackgroundProcessLRUPool::sSingleton;
  1230 /* static */ BackgroundProcessLRUPool*
  1231 BackgroundProcessLRUPool::Singleton()
  1233   if (!sSingleton) {
  1234     sSingleton = new BackgroundProcessLRUPool();
  1235     ClearOnShutdown(&sSingleton);
  1237   return sSingleton;
  1240 BackgroundProcessLRUPool::BackgroundProcessLRUPool()
  1242   EnsureLRUPool();
  1245 uint32_t
  1246 BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex)
  1248   // Set LRU level of each background process and maintain LRU buffer as below:
  1250   // Priority background  : LRU0
  1251   // Priority background+1: LRU1, LRU2
  1252   // Priority background+2: LRU3, LRU4, LRU5, LRU6
  1253   // Priority background+3: LRU7, LRU8, LRU9, LRU10, LRU11, LRU12, LRU13, LRU14
  1254   // ...
  1255   // Priority background+L-1: 2^(number of background LRU pool levels - 1)
  1256   // (End of buffer)
  1258   return (uint32_t)(log((float)aBackgroundLRUPoolIndex) / log(2.0));
  1261 void
  1262 BackgroundProcessLRUPool::EnsureLRUPool()
  1264   // We set mBackgroundLRUPoolLevels according to our pref.
  1265   // This value is used to set background process LRU pool
  1266   if (!NS_SUCCEEDED(Preferences::GetInt(
  1267         "dom.ipc.processPriorityManager.backgroundLRUPoolLevels",
  1268         &mLRUPoolLevels))) {
  1269     mLRUPoolLevels = 1;
  1272   if (mLRUPoolLevels <= 0) {
  1273     MOZ_CRASH();
  1276   // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
  1277   // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
  1278   // This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
  1279   // See bug 822325 comment 49
  1280   MOZ_ASSERT(mLRUPoolLevels <= 6);
  1282   // LRU pool size = 2 ^ (number of background LRU pool levels) - 1
  1283   mLRUPoolSize = (1 << mLRUPoolLevels) - 1;
  1285   mLRUPoolAvailableIndex = 0;
  1287   LOG("Making background LRU pool with size(%d)", mLRUPoolSize);
  1289   mLRUPool.InsertElementsAt(0, mLRUPoolSize, (ContentParent*)nullptr);
  1292 void
  1293 BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool(
  1294     ContentParent* aContentParent)
  1296   for (int32_t i = 0; i < mLRUPoolSize; i++) {
  1297     if (mLRUPool[i]) {
  1298       if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
  1300         mLRUPool[i] = nullptr;
  1301         LOG("Remove ChildID(%llu) from LRU pool", aContentParent->ChildID());
  1303         // After we remove this ContentParent from LRU pool, we still need to
  1304         // update the available index if the index of removed one is less than
  1305         // the available index we already have.
  1306         UpdateAvailableIndexInLRUPool(aContentParent, i);
  1307         break;
  1313 nsresult
  1314 BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool(
  1315     ContentParent* aContentParent,
  1316     int32_t aTargetIndex)
  1318   // If we specify which index we want to assign to mLRUPoolAvailableIndex,
  1319   // We have to make sure the index in LRUPool doesn't point to any
  1320   // ContentParent.
  1321   if (aTargetIndex >= 0 && aTargetIndex < mLRUPoolSize &&
  1322       aTargetIndex < mLRUPoolAvailableIndex &&
  1323       !mLRUPool[aTargetIndex]) {
  1324     mLRUPoolAvailableIndex = aTargetIndex;
  1325     return NS_OK;
  1328   // When we didn't specify any legal aTargetIndex, then we just check
  1329   // whether current mLRUPoolAvailableIndex points to any ContentParent or not.
  1330   if (mLRUPoolAvailableIndex >= 0 && mLRUPoolAvailableIndex < mLRUPoolSize &&
  1331       !(mLRUPool[mLRUPoolAvailableIndex])) {
  1332     return NS_OK;
  1335   // Both above way failed. So now we have to find proper value
  1336   // for mLRUPoolAvailableIndex.
  1337   // We are looking for an available index. We only shift process with
  1338   // LRU less than the available index should have, so we stop update
  1339   // mLRUPoolAvailableIndex from the for loop once we got a candidate.
  1340   mLRUPoolAvailableIndex = -1;
  1342   for (int32_t i = 0; i < mLRUPoolSize; i++) {
  1343     if (mLRUPool[i]) {
  1344       if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
  1345         LOG("ChildID(%llu) already in LRU pool", aContentParent->ChildID());
  1346         MOZ_ASSERT(false);
  1347         return NS_ERROR_UNEXPECTED;
  1349       continue;
  1350     } else {
  1351       if (mLRUPoolAvailableIndex == -1) {
  1352         mLRUPoolAvailableIndex = i;
  1357   // If the LRUPool is already full, mLRUPoolAvailableIndex is still -1 after
  1358   // above loop finished. We should set mLRUPoolAvailableIndex
  1359   // to mLRUPoolSize - 1 in this case. Here uses the mod operator to do it:
  1360   // New mLRUPoolAvailableIndex either equals old mLRUPoolAvailableIndex, or
  1361   // mLRUPoolSize - 1 if old mLRUPoolAvailableIndex is -1.
  1362   mLRUPoolAvailableIndex =
  1363     (mLRUPoolAvailableIndex + mLRUPoolSize) % mLRUPoolSize;
  1365   return NS_OK;
  1368 void
  1369 BackgroundProcessLRUPool::ShiftLRUPool()
  1371   for (int32_t i = mLRUPoolAvailableIndex; i > 0; i--) {
  1372     mLRUPool[i] = mLRUPool[i - 1];
  1373     // Check whether i+1 is power of Two.
  1374     // If so, then it crossed a LRU group boundary and
  1375     // we need to assign its new process priority LRU.
  1376     if (!((i + 1) & i)) {
  1377       ProcessPriorityManagerImpl::GetSingleton()->SetProcessPriority(
  1378         mLRUPool[i], PROCESS_PRIORITY_BACKGROUND, CalculateLRULevel(i + 1));
  1383 void
  1384 BackgroundProcessLRUPool::AddIntoBackgroundLRUPool(
  1385     ContentParent* aContentParent)
  1387   // We have to make sure that we have correct available index in LRU pool
  1388   if (!NS_SUCCEEDED(
  1389       UpdateAvailableIndexInLRUPool(aContentParent))) {
  1390     return;
  1393   // Shift the list in the pool, so we have room at index 0 for the newly added
  1394   // ContentParent
  1395   ShiftLRUPool();
  1397   mLRUPool[0] = aContentParent;
  1399   LOG("Add ChildID(%llu) into LRU pool", aContentParent->ChildID());
  1402 } // anonymous namespace
  1404 namespace mozilla {
  1406 /* static */ void
  1407 ProcessPriorityManager::Init()
  1409   ProcessPriorityManagerImpl::StaticInit();
  1410   ProcessPriorityManagerChild::StaticInit();
  1413 /* static */ void
  1414 ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
  1415                                            ProcessPriority aPriority)
  1417   MOZ_ASSERT(aContentParent);
  1419   ProcessPriorityManagerImpl* singleton =
  1420     ProcessPriorityManagerImpl::GetSingleton();
  1421   if (singleton) {
  1422     singleton->SetProcessPriority(aContentParent, aPriority);
  1426 /* static */ void
  1427 ProcessPriorityManager::RemoveFromBackgroundLRUPool(
  1428     ContentParent* aContentParent)
  1430   MOZ_ASSERT(aContentParent);
  1432   BackgroundProcessLRUPool* singleton =
  1433     BackgroundProcessLRUPool::Singleton();
  1434   if (singleton) {
  1435     singleton->RemoveFromBackgroundLRUPool(aContentParent);
  1439 /* static */ void
  1440 ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent)
  1442   MOZ_ASSERT(aContentParent);
  1444   BackgroundProcessLRUPool* singleton =
  1445     BackgroundProcessLRUPool::Singleton();
  1446   if (singleton) {
  1447     singleton->AddIntoBackgroundLRUPool(aContentParent);
  1451 /* static */ bool
  1452 ProcessPriorityManager::CurrentProcessIsForeground()
  1454   return ProcessPriorityManagerChild::Singleton()->
  1455     CurrentProcessIsForeground();
  1458 /* static */ bool
  1459 ProcessPriorityManager::AnyProcessHasHighPriority()
  1461   ProcessPriorityManagerImpl* singleton =
  1462     ProcessPriorityManagerImpl::GetSingleton();
  1464   if (singleton) {
  1465     return singleton->ChildProcessHasHighPriority();
  1466   } else {
  1467     return ProcessPriorityManagerChild::Singleton()->
  1468       CurrentProcessIsHighPriority();
  1472 } // namespace mozilla

mercurial