Bumping manifests a=b2g-bump
[gecko.git] / dom / ipc / ProcessPriorityManager.cpp
blobb021ea0a55b5861b3559f5f7febab335fe35597a
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.
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()
73 static PRLogModuleInfo *sLog;
74 if (!sLog)
75 sLog = PR_NewLogModule("ProcessPriorityManager");
76 return sLog;
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.)
105 * ProcessPriorityManager::CurrentProcessIsForeground() and
106 * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
107 * any process, are handled separately, by the ProcessPriorityManagerChild
108 * class.
110 class ProcessPriorityManagerImpl MOZ_FINAL
111 : public nsIObserver
112 , public WakeLockObserver
114 public:
116 * If we're in the main process, get the ProcessPriorityManagerImpl
117 * singleton. If we're in a child process, return null.
119 static ProcessPriorityManagerImpl* GetSingleton();
121 static void StaticInit();
122 static bool PrefsEnabled();
124 NS_DECL_ISUPPORTS
125 NS_DECL_NSIOBSERVER
128 * This function implements ProcessPriorityManager::SetProcessPriority.
130 void SetProcessPriority(ContentParent* aContentParent,
131 ProcessPriority aPriority,
132 uint32_t aBackgroundLRU = 0);
135 * If a magic testing-only pref is set, notify the observer service on the
136 * given topic with the given data. This is used for testing
138 void FireTestOnlyObserverNotification(const char* aTopic,
139 const nsACString& aData = EmptyCString());
142 * Does some process, other than the one handled by aParticularManager, have
143 * priority FOREGROUND_HIGH?
145 bool OtherProcessHasHighPriority(
146 ParticularProcessPriorityManager* aParticularManager);
149 * Does one of the child processes have priority FOREGROUND_HIGH?
151 bool ChildProcessHasHighPriority();
154 * This must be called by a ParticularProcessPriorityManager when it changes
155 * its priority.
157 void NotifyProcessPriorityChanged(
158 ParticularProcessPriorityManager* aParticularManager,
159 hal::ProcessPriority aOldPriority);
162 * Implements WakeLockObserver, used to monitor wake lock changes in the
163 * main process.
165 virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE;
167 private:
168 static bool sPrefListenersRegistered;
169 static bool sInitialized;
170 static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
172 static void PrefChangedCallback(const char* aPref, void* aClosure);
174 ProcessPriorityManagerImpl();
175 ~ProcessPriorityManagerImpl();
176 DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl);
178 void Init();
180 already_AddRefed<ParticularProcessPriorityManager>
181 GetParticularProcessPriorityManager(ContentParent* aContentParent);
183 void ObserveContentParentCreated(nsISupports* aContentParent);
184 void ObserveContentParentDestroyed(nsISupports* aSubject);
185 void ResetAllCPUPriorities();
187 nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
188 mParticularManagers;
190 bool mHighPriority;
191 nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
195 * This singleton class implements the parts of the process priority manager
196 * that are available from all processes.
198 class ProcessPriorityManagerChild MOZ_FINAL
199 : public nsIObserver
201 public:
202 static void StaticInit();
203 static ProcessPriorityManagerChild* Singleton();
205 NS_DECL_ISUPPORTS
206 NS_DECL_NSIOBSERVER
208 bool CurrentProcessIsForeground();
209 bool CurrentProcessIsHighPriority();
211 private:
212 static StaticRefPtr<ProcessPriorityManagerChild> sSingleton;
214 ProcessPriorityManagerChild();
215 ~ProcessPriorityManagerChild() {}
216 DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild);
218 void Init();
220 hal::ProcessPriority mCachedPriority;
224 * This class manages the priority of one particular process. It is
225 * main-process only.
227 class ParticularProcessPriorityManager MOZ_FINAL
228 : public WakeLockObserver
229 , public nsIObserver
230 , public nsITimerCallback
231 , public nsSupportsWeakReference
233 ~ParticularProcessPriorityManager();
234 public:
235 explicit ParticularProcessPriorityManager(ContentParent* aContentParent);
237 NS_DECL_ISUPPORTS
238 NS_DECL_NSIOBSERVER
239 NS_DECL_NSITIMERCALLBACK
241 virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE;
242 void Init();
244 int32_t Pid() const;
245 uint64_t ChildID() const;
246 bool IsPreallocated() const;
249 * Used in logging, this method returns the ContentParent's name followed by
250 * ", ". If we can't get the ContentParent's name for some reason, it
251 * returns an empty string.
253 * The reference returned here is guaranteed to be live until the next call
254 * to NameWithComma() or until the ParticularProcessPriorityManager is
255 * destroyed, whichever comes first.
257 const nsAutoCString& NameWithComma();
259 bool HasAppType(const char* aAppType);
260 bool IsExpectingSystemMessage();
262 void OnAudioChannelProcessChanged(nsISupports* aSubject);
263 void OnRemoteBrowserFrameShown(nsISupports* aSubject);
264 void OnTabParentDestroyed(nsISupports* aSubject);
265 void OnFrameloaderVisibleChanged(nsISupports* aSubject);
266 void OnChannelConnected(nsISupports* aSubject);
268 ProcessPriority CurrentPriority();
269 ProcessPriority ComputePriority();
270 ProcessCPUPriority ComputeCPUPriority(ProcessPriority aPriority);
272 void ScheduleResetPriority(const char* aTimeoutPref);
273 void ResetPriority();
274 void ResetPriorityNow();
275 void ResetCPUPriorityNow();
278 * This overload is equivalent to SetPriorityNow(aPriority,
279 * ComputeCPUPriority()).
281 void SetPriorityNow(ProcessPriority aPriority,
282 uint32_t aBackgroundLRU = 0);
284 void SetPriorityNow(ProcessPriority aPriority,
285 ProcessCPUPriority aCPUPriority,
286 uint32_t aBackgroundLRU = 0);
288 void ShutDown();
290 private:
291 void FireTestOnlyObserverNotification(
292 const char* aTopic,
293 const nsACString& aData = EmptyCString());
295 void FireTestOnlyObserverNotification(
296 const char* aTopic,
297 const char* aData = nullptr);
299 ContentParent* mContentParent;
300 uint64_t mChildID;
301 ProcessPriority mPriority;
302 ProcessCPUPriority mCPUPriority;
303 bool mHoldsCPUWakeLock;
304 bool mHoldsHighPriorityWakeLock;
307 * Used to implement NameWithComma().
309 nsAutoCString mNameWithComma;
311 nsCOMPtr<nsITimer> mResetPriorityTimer;
314 class BackgroundProcessLRUPool MOZ_FINAL
316 public:
317 static BackgroundProcessLRUPool* Singleton();
320 * Used to remove a ContentParent from background LRU pool when
321 * it is destroyed or its priority changed from BACKGROUND to others.
323 void RemoveFromBackgroundLRUPool(ContentParent* aContentParent);
326 * Used to add a ContentParent into background LRU pool when
327 * its priority changed to BACKGROUND from others.
329 void AddIntoBackgroundLRUPool(ContentParent* aContentParent);
331 private:
332 static StaticAutoPtr<BackgroundProcessLRUPool> sSingleton;
334 int32_t mLRUPoolLevels;
335 int32_t mLRUPoolSize;
336 int32_t mLRUPoolAvailableIndex;
337 nsTArray<ContentParent*> mLRUPool;
339 uint32_t CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex);
341 nsresult UpdateAvailableIndexInLRUPool(
342 ContentParent* aContentParent,
343 int32_t aTargetIndex = -1);
345 void ShiftLRUPool();
347 void EnsureLRUPool();
349 BackgroundProcessLRUPool();
350 DISALLOW_EVIL_CONSTRUCTORS(BackgroundProcessLRUPool);
354 /* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
355 /* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
356 /* static */ StaticRefPtr<ProcessPriorityManagerImpl>
357 ProcessPriorityManagerImpl::sSingleton;
359 NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl,
360 nsIObserver);
362 /* static */ void
363 ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref,
364 void* aClosure)
366 StaticInit();
369 /* static */ bool
370 ProcessPriorityManagerImpl::PrefsEnabled()
372 return Preferences::GetBool("dom.ipc.processPriorityManager.enabled") &&
373 !Preferences::GetBool("dom.ipc.tabs.disabled");
376 /* static */ void
377 ProcessPriorityManagerImpl::StaticInit()
379 if (sInitialized) {
380 return;
383 // The process priority manager is main-process only.
384 if (XRE_GetProcessType() != GeckoProcessType_Default) {
385 sInitialized = true;
386 return;
389 // If IPC tabs aren't enabled at startup, don't bother with any of this.
390 if (!PrefsEnabled()) {
391 LOG("InitProcessPriorityManager bailing due to prefs.");
393 // Run StaticInit() again if the prefs change. We don't expect this to
394 // happen in normal operation, but it happens during testing.
395 if (!sPrefListenersRegistered) {
396 sPrefListenersRegistered = true;
397 Preferences::RegisterCallback(PrefChangedCallback,
398 "dom.ipc.processPriorityManager.enabled");
399 Preferences::RegisterCallback(PrefChangedCallback,
400 "dom.ipc.tabs.disabled");
402 return;
405 sInitialized = true;
407 sSingleton = new ProcessPriorityManagerImpl();
408 sSingleton->Init();
409 ClearOnShutdown(&sSingleton);
412 /* static */ ProcessPriorityManagerImpl*
413 ProcessPriorityManagerImpl::GetSingleton()
415 if (!sSingleton) {
416 StaticInit();
419 return sSingleton;
422 ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
423 : mHighPriority(false)
425 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
426 RegisterWakeLockObserver(this);
429 ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
431 UnregisterWakeLockObserver(this);
434 void
435 ProcessPriorityManagerImpl::Init()
437 LOG("Starting up. This is the master process.");
439 // The master process's priority never changes; set it here and then forget
440 // about it. We'll manage only subprocesses' priorities using the process
441 // priority manager.
442 hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER,
443 PROCESS_CPU_PRIORITY_NORMAL);
445 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
446 if (os) {
447 os->AddObserver(this, "ipc:content-created", /* ownsWeak */ false);
448 os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ false);
452 NS_IMETHODIMP
453 ProcessPriorityManagerImpl::Observe(
454 nsISupports* aSubject,
455 const char* aTopic,
456 const char16_t* aData)
458 nsDependentCString topic(aTopic);
459 if (topic.EqualsLiteral("ipc:content-created")) {
460 ObserveContentParentCreated(aSubject);
461 } else if (topic.EqualsLiteral("ipc:content-shutdown")) {
462 ObserveContentParentDestroyed(aSubject);
463 } else {
464 MOZ_ASSERT(false);
467 return NS_OK;
470 already_AddRefed<ParticularProcessPriorityManager>
471 ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
472 ContentParent* aContentParent)
474 nsRefPtr<ParticularProcessPriorityManager> pppm;
475 mParticularManagers.Get(aContentParent->ChildID(), &pppm);
476 if (!pppm) {
477 pppm = new ParticularProcessPriorityManager(aContentParent);
478 pppm->Init();
479 mParticularManagers.Put(aContentParent->ChildID(), pppm);
481 FireTestOnlyObserverNotification("process-created",
482 nsPrintfCString("%lld", aContentParent->ChildID()));
485 return pppm.forget();
488 void
489 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
490 ProcessPriority aPriority,
491 uint32_t aBackgroundLRU)
493 MOZ_ASSERT(aContentParent);
494 nsRefPtr<ParticularProcessPriorityManager> pppm =
495 GetParticularProcessPriorityManager(aContentParent);
496 pppm->SetPriorityNow(aPriority, aBackgroundLRU);
499 void
500 ProcessPriorityManagerImpl::ObserveContentParentCreated(
501 nsISupports* aContentParent)
503 // Do nothing; it's sufficient to get the PPPM. But assign to nsRefPtr so we
504 // don't leak the already_AddRefed object.
505 nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
506 nsRefPtr<ParticularProcessPriorityManager> pppm =
507 GetParticularProcessPriorityManager(cp->AsContentParent());
510 static PLDHashOperator
511 EnumerateParticularProcessPriorityManagers(
512 const uint64_t& aKey,
513 nsRefPtr<ParticularProcessPriorityManager> aValue,
514 void* aUserData)
516 nsTArray<nsRefPtr<ParticularProcessPriorityManager> >* aArray =
517 static_cast<nsTArray<nsRefPtr<ParticularProcessPriorityManager> >*>(aUserData);
518 aArray->AppendElement(aValue);
519 return PL_DHASH_NEXT;
522 void
523 ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
525 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
526 NS_ENSURE_TRUE_VOID(props);
528 uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
529 props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
530 NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
532 nsRefPtr<ParticularProcessPriorityManager> pppm;
533 mParticularManagers.Get(childID, &pppm);
534 MOZ_ASSERT(pppm);
535 if (pppm) {
536 pppm->ShutDown();
539 mParticularManagers.Remove(childID);
541 if (mHighPriorityChildIDs.Contains(childID)) {
542 mHighPriorityChildIDs.RemoveEntry(childID);
544 // We just lost a high-priority process; reset everyone's CPU priorities.
545 ResetAllCPUPriorities();
549 void
550 ProcessPriorityManagerImpl::ResetAllCPUPriorities( void )
552 nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
553 mParticularManagers.EnumerateRead(
554 &EnumerateParticularProcessPriorityManagers,
555 &pppms);
557 for (uint32_t i = 0; i < pppms.Length(); i++) {
558 pppms[i]->ResetCPUPriorityNow();
562 bool
563 ProcessPriorityManagerImpl::OtherProcessHasHighPriority(
564 ParticularProcessPriorityManager* aParticularManager)
566 if (mHighPriority) {
567 return true;
568 } else if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) {
569 return mHighPriorityChildIDs.Count() > 1;
571 return mHighPriorityChildIDs.Count() > 0;
574 bool
575 ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void )
577 return mHighPriorityChildIDs.Count() > 0;
580 void
581 ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
582 ParticularProcessPriorityManager* aParticularManager,
583 ProcessPriority aOldPriority)
585 // This priority change can only affect other processes' priorities if we're
586 // changing to/from FOREGROUND_HIGH.
588 if (aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
589 aParticularManager->CurrentPriority() <
590 PROCESS_PRIORITY_FOREGROUND_HIGH) {
592 return;
595 if (aParticularManager->CurrentPriority() >=
596 PROCESS_PRIORITY_FOREGROUND_HIGH) {
597 mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID());
598 } else {
599 mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
602 nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
603 mParticularManagers.EnumerateRead(
604 &EnumerateParticularProcessPriorityManagers,
605 &pppms);
607 for (uint32_t i = 0; i < pppms.Length(); i++) {
608 if (pppms[i] != aParticularManager) {
609 pppms[i]->ResetCPUPriorityNow();
614 /* virtual */ void
615 ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
617 /* The main process always has an ID of 0, if it is present in the wake-lock
618 * information then we explicitly requested a high-priority wake-lock for the
619 * main process. */
620 if (aInfo.topic().EqualsLiteral("high-priority")) {
621 if (aInfo.lockingProcesses().Contains((uint64_t)0)) {
622 mHighPriority = true;
623 } else {
624 mHighPriority = false;
627 /* The main process got a high-priority wakelock change; reset everyone's
628 * CPU priorities. */
629 ResetAllCPUPriorities();
631 LOG("Got wake lock changed event. "
632 "Now mHighPriorityParent = %d\n", mHighPriority);
637 NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
638 nsIObserver,
639 nsITimerCallback,
640 nsISupportsWeakReference);
642 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
643 ContentParent* aContentParent)
644 : mContentParent(aContentParent)
645 , mChildID(aContentParent->ChildID())
646 , mPriority(PROCESS_PRIORITY_UNKNOWN)
647 , mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
648 , mHoldsCPUWakeLock(false)
649 , mHoldsHighPriorityWakeLock(false)
651 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
652 LOGP("Creating ParticularProcessPriorityManager.");
655 void
656 ParticularProcessPriorityManager::Init()
658 RegisterWakeLockObserver(this);
660 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
661 if (os) {
662 os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true);
663 os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
664 os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
665 os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
668 // This process may already hold the CPU lock; for example, our parent may
669 // have acquired it on our behalf.
670 WakeLockInformation info1, info2;
671 GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
672 mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID());
674 GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2);
675 mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID());
676 LOGP("Done starting up. mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
677 mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
680 ParticularProcessPriorityManager::~ParticularProcessPriorityManager()
682 LOGP("Destroying ParticularProcessPriorityManager.");
684 // Unregister our wake lock observer if ShutDown hasn't been called. (The
685 // wake lock observer takes raw refs, so we don't want to take chances here!)
686 // We don't call UnregisterWakeLockObserver unconditionally because the code
687 // will print a warning if it's called unnecessarily.
689 if (mContentParent) {
690 UnregisterWakeLockObserver(this);
694 /* virtual */ void
695 ParticularProcessPriorityManager::Notify(const WakeLockInformation& aInfo)
697 if (!mContentParent) {
698 // We've been shut down.
699 return;
702 bool* dest = nullptr;
703 if (aInfo.topic().EqualsLiteral("cpu")) {
704 dest = &mHoldsCPUWakeLock;
705 } else if (aInfo.topic().EqualsLiteral("high-priority")) {
706 dest = &mHoldsHighPriorityWakeLock;
709 if (dest) {
710 bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID());
711 if (thisProcessLocks != *dest) {
712 *dest = thisProcessLocks;
713 LOGP("Got wake lock changed event. "
714 "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
715 mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
716 ResetPriority();
721 NS_IMETHODIMP
722 ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
723 const char* aTopic,
724 const char16_t* aData)
726 if (!mContentParent) {
727 // We've been shut down.
728 return NS_OK;
731 nsDependentCString topic(aTopic);
733 if (topic.EqualsLiteral("audio-channel-process-changed")) {
734 OnAudioChannelProcessChanged(aSubject);
735 } else if (topic.EqualsLiteral("remote-browser-shown")) {
736 OnRemoteBrowserFrameShown(aSubject);
737 } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
738 OnTabParentDestroyed(aSubject);
739 } else if (topic.EqualsLiteral("frameloader-visible-changed")) {
740 OnFrameloaderVisibleChanged(aSubject);
741 } else {
742 MOZ_ASSERT(false);
745 return NS_OK;
748 uint64_t
749 ParticularProcessPriorityManager::ChildID() const
751 // We have to cache mContentParent->ChildID() instead of getting it from the
752 // ContentParent each time because after ShutDown() is called, mContentParent
753 // is null. If we didn't cache ChildID(), then we wouldn't be able to run
754 // LOGP() after ShutDown().
755 return mChildID;
758 int32_t
759 ParticularProcessPriorityManager::Pid() const
761 return mContentParent ? mContentParent->Pid() : -1;
764 bool
765 ParticularProcessPriorityManager::IsPreallocated() const
767 return mContentParent ? mContentParent->IsPreallocated() : false;
770 const nsAutoCString&
771 ParticularProcessPriorityManager::NameWithComma()
773 mNameWithComma.Truncate();
774 if (!mContentParent) {
775 return mNameWithComma; // empty string
778 nsAutoString name;
779 mContentParent->FriendlyName(name);
780 if (name.IsEmpty()) {
781 return mNameWithComma; // empty string
784 mNameWithComma = NS_ConvertUTF16toUTF8(name);
785 mNameWithComma.AppendLiteral(", ");
786 return mNameWithComma;
789 void
790 ParticularProcessPriorityManager::OnAudioChannelProcessChanged(nsISupports* aSubject)
792 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
793 NS_ENSURE_TRUE_VOID(props);
795 uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
796 props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
797 if (childID == ChildID()) {
798 ResetPriority();
802 void
803 ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
805 nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
806 NS_ENSURE_TRUE_VOID(fl);
808 // Ignore notifications that aren't from a BrowserOrApp
809 bool isBrowserOrApp;
810 fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
811 if (!isBrowserOrApp) {
812 return;
815 nsCOMPtr<nsITabParent> tp;
816 fl->GetTabParent(getter_AddRefs(tp));
817 NS_ENSURE_TRUE_VOID(tp);
819 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
820 if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
821 return;
824 ResetPriority();
827 void
828 ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
830 nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
831 NS_ENSURE_TRUE_VOID(tp);
833 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
834 if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
835 return;
838 ResetPriority();
841 void
842 ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubject)
844 nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
845 NS_ENSURE_TRUE_VOID(fl);
847 nsCOMPtr<nsITabParent> tp;
848 fl->GetTabParent(getter_AddRefs(tp));
849 if (!tp) {
850 return;
853 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
854 if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) {
855 return;
858 // Most of the time when something changes in a process we call
859 // ResetPriority(), giving a grace period before downgrading its priority.
860 // But notice that here don't give a grace period: We call ResetPriorityNow()
861 // instead.
863 // We do this because we're reacting here to a setVisibility() call, which is
864 // an explicit signal from the process embedder that we should re-prioritize
865 // a process. If we gave a grace period in response to setVisibility()
866 // calls, it would be impossible for the embedder to explicitly prioritize
867 // processes and prevent e.g. the case where we switch which process is in
868 // the foreground and, during the old fg processs's grace period, it OOMs the
869 // new fg process.
871 ResetPriorityNow();
874 void
875 ParticularProcessPriorityManager::ResetPriority()
877 ProcessPriority processPriority = ComputePriority();
878 if (mPriority == PROCESS_PRIORITY_UNKNOWN ||
879 mPriority > processPriority) {
880 // Apps set at a perceivable background priority are often playing media.
881 // Most media will have short gaps while changing tracks between songs,
882 // switching videos, etc. Give these apps a longer grace period so they
883 // can get their next track started, if there is one, before getting
884 // downgraded.
885 if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
886 ScheduleResetPriority("backgroundPerceivableGracePeriodMS");
887 } else {
888 ScheduleResetPriority("backgroundGracePeriodMS");
890 return;
893 SetPriorityNow(processPriority);
896 void
897 ParticularProcessPriorityManager::ResetPriorityNow()
899 SetPriorityNow(ComputePriority());
902 void
903 ParticularProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref)
905 if (mResetPriorityTimer) {
906 LOGP("ScheduleResetPriority bailing; the timer is already running.");
907 return;
910 uint32_t timeout = Preferences::GetUint(
911 nsPrintfCString("dom.ipc.processPriorityManager.%s", aTimeoutPref).get());
912 LOGP("Scheduling reset timer to fire in %dms.", timeout);
913 mResetPriorityTimer = do_CreateInstance("@mozilla.org/timer;1");
914 mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
918 NS_IMETHODIMP
919 ParticularProcessPriorityManager::Notify(nsITimer* aTimer)
921 LOGP("Reset priority timer callback; about to ResetPriorityNow.");
922 ResetPriorityNow();
923 mResetPriorityTimer = nullptr;
924 return NS_OK;
927 bool
928 ParticularProcessPriorityManager::HasAppType(const char* aAppType)
930 const InfallibleTArray<PBrowserParent*>& browsers =
931 mContentParent->ManagedPBrowserParent();
932 for (uint32_t i = 0; i < browsers.Length(); i++) {
933 nsAutoString appType;
934 static_cast<TabParent*>(browsers[i])->GetAppType(appType);
935 if (appType.EqualsASCII(aAppType)) {
936 return true;
940 return false;
943 bool
944 ParticularProcessPriorityManager::IsExpectingSystemMessage()
946 const InfallibleTArray<PBrowserParent*>& browsers =
947 mContentParent->ManagedPBrowserParent();
948 for (uint32_t i = 0; i < browsers.Length(); i++) {
949 TabParent* tp = static_cast<TabParent*>(browsers[i]);
950 nsCOMPtr<nsIMozBrowserFrame> bf = do_QueryInterface(tp->GetOwnerElement());
951 if (!bf) {
952 continue;
955 if (bf->GetIsExpectingSystemMessage()) {
956 return true;
960 return false;
963 ProcessPriority
964 ParticularProcessPriorityManager::CurrentPriority()
966 return mPriority;
969 ProcessPriority
970 ParticularProcessPriorityManager::ComputePriority()
972 if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
973 HasAppType("critical")) {
974 return PROCESS_PRIORITY_FOREGROUND_HIGH;
977 bool isVisible = false;
978 const InfallibleTArray<PBrowserParent*>& browsers =
979 mContentParent->ManagedPBrowserParent();
980 for (uint32_t i = 0; i < browsers.Length(); i++) {
981 if (static_cast<TabParent*>(browsers[i])->IsVisible()) {
982 isVisible = true;
983 break;
987 if (isVisible) {
988 return HasAppType("inputmethod") ?
989 PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
990 PROCESS_PRIORITY_FOREGROUND;
993 if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
994 IsExpectingSystemMessage()) {
995 return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
998 AudioChannelService* service = AudioChannelService::GetOrCreateAudioChannelService();
999 if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
1000 return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
1003 return HasAppType("homescreen") ?
1004 PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
1005 PROCESS_PRIORITY_BACKGROUND;
1008 ProcessCPUPriority
1009 ParticularProcessPriorityManager::ComputeCPUPriority(ProcessPriority aPriority)
1011 if (aPriority == PROCESS_PRIORITY_PREALLOC) {
1012 return PROCESS_CPU_PRIORITY_LOW;
1015 if (aPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
1016 return PROCESS_CPU_PRIORITY_NORMAL;
1019 return ProcessPriorityManagerImpl::GetSingleton()->
1020 OtherProcessHasHighPriority(this) ?
1021 PROCESS_CPU_PRIORITY_LOW :
1022 PROCESS_CPU_PRIORITY_NORMAL;
1025 void
1026 ParticularProcessPriorityManager::ResetCPUPriorityNow()
1028 SetPriorityNow(mPriority);
1031 void
1032 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
1033 uint32_t aBackgroundLRU)
1035 SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU);
1038 void
1039 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
1040 ProcessCPUPriority aCPUPriority,
1041 uint32_t aBackgroundLRU)
1043 #ifdef MOZ_NUWA_PROCESS
1044 // Do not attempt to change the priority of the Nuwa process
1045 if (mContentParent->IsNuwaProcess()) {
1046 return;
1048 #endif
1050 if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
1051 MOZ_ASSERT(false);
1052 return;
1055 if (aBackgroundLRU > 0 &&
1056 aPriority == PROCESS_PRIORITY_BACKGROUND &&
1057 mPriority == PROCESS_PRIORITY_BACKGROUND) {
1058 hal::SetProcessPriority(Pid(), mPriority, mCPUPriority, aBackgroundLRU);
1060 nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d",
1061 ProcessPriorityToString(mPriority, mCPUPriority),
1062 aBackgroundLRU);
1064 FireTestOnlyObserverNotification("process-priority-with-background-LRU-set",
1065 ProcessPriorityWithBackgroundLRU.get());
1068 if (!mContentParent ||
1069 !ProcessPriorityManagerImpl::PrefsEnabled() ||
1070 (mPriority == aPriority && mCPUPriority == aCPUPriority)) {
1071 return;
1074 // If the prefs were disabled after this ParticularProcessPriorityManager was
1075 // created, we can at least avoid any further calls to
1076 // hal::SetProcessPriority. Supporting dynamic enabling/disabling of the
1077 // ProcessPriorityManager is mostly for testing.
1078 if (!ProcessPriorityManagerImpl::PrefsEnabled()) {
1079 return;
1082 if (aPriority == PROCESS_PRIORITY_BACKGROUND &&
1083 mPriority != PROCESS_PRIORITY_BACKGROUND &&
1084 !IsPreallocated()) {
1085 ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent);
1088 if (aPriority != PROCESS_PRIORITY_BACKGROUND &&
1089 mPriority == PROCESS_PRIORITY_BACKGROUND &&
1090 !IsPreallocated()) {
1091 ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
1094 LOGP("Changing priority from %s to %s.",
1095 ProcessPriorityToString(mPriority, mCPUPriority),
1096 ProcessPriorityToString(aPriority, aCPUPriority));
1098 ProcessPriority oldPriority = mPriority;
1100 mPriority = aPriority;
1101 mCPUPriority = aCPUPriority;
1102 hal::SetProcessPriority(Pid(), mPriority, mCPUPriority);
1104 if (oldPriority != mPriority) {
1105 unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
1108 if (aPriority < PROCESS_PRIORITY_FOREGROUND) {
1109 unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("low-memory"));
1112 FireTestOnlyObserverNotification("process-priority-set",
1113 ProcessPriorityToString(mPriority, mCPUPriority));
1115 if (oldPriority != mPriority) {
1116 ProcessPriorityManagerImpl::GetSingleton()->
1117 NotifyProcessPriorityChanged(this, oldPriority);
1121 void
1122 ParticularProcessPriorityManager::ShutDown()
1124 MOZ_ASSERT(mContentParent);
1126 UnregisterWakeLockObserver(this);
1128 if (mResetPriorityTimer) {
1129 mResetPriorityTimer->Cancel();
1130 mResetPriorityTimer = nullptr;
1133 if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
1134 ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
1137 mContentParent = nullptr;
1140 void
1141 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
1142 const char* aTopic,
1143 const nsACString& aData /* = EmptyCString() */)
1145 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1146 return;
1149 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1150 NS_ENSURE_TRUE_VOID(os);
1152 nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic);
1154 LOG("Notifying observer %s, data %s",
1155 topic.get(), PromiseFlatCString(aData).get());
1156 os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get());
1159 void
1160 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
1161 const char* aTopic,
1162 const char* aData /* = nullptr */ )
1164 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1165 return;
1168 nsAutoCString data;
1169 if (aData) {
1170 data.AppendASCII(aData);
1173 FireTestOnlyObserverNotification(aTopic, data);
1176 void
1177 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
1178 const char* aTopic,
1179 const nsACString& aData /* = EmptyCString() */)
1181 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1182 return;
1185 nsAutoCString data(nsPrintfCString("%lld", ChildID()));
1186 if (!aData.IsEmpty()) {
1187 data.Append(':');
1188 data.Append(aData);
1191 // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return
1192 // null, since ProcessPriorityManagerImpl is the only class which creates
1193 // ParticularProcessPriorityManagers.
1195 ProcessPriorityManagerImpl::GetSingleton()->
1196 FireTestOnlyObserverNotification(aTopic, data);
1199 StaticRefPtr<ProcessPriorityManagerChild>
1200 ProcessPriorityManagerChild::sSingleton;
1202 /* static */ void
1203 ProcessPriorityManagerChild::StaticInit()
1205 if (!sSingleton) {
1206 sSingleton = new ProcessPriorityManagerChild();
1207 sSingleton->Init();
1208 ClearOnShutdown(&sSingleton);
1212 /* static */ ProcessPriorityManagerChild*
1213 ProcessPriorityManagerChild::Singleton()
1215 StaticInit();
1216 return sSingleton;
1219 NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild,
1220 nsIObserver)
1222 ProcessPriorityManagerChild::ProcessPriorityManagerChild()
1224 if (XRE_GetProcessType() == GeckoProcessType_Default) {
1225 mCachedPriority = PROCESS_PRIORITY_MASTER;
1226 } else {
1227 mCachedPriority = PROCESS_PRIORITY_UNKNOWN;
1231 void
1232 ProcessPriorityManagerChild::Init()
1234 // The process priority should only be changed in child processes; don't even
1235 // bother listening for changes if we're in the main process.
1236 if (XRE_GetProcessType() != GeckoProcessType_Default) {
1237 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1238 NS_ENSURE_TRUE_VOID(os);
1239 os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false);
1243 NS_IMETHODIMP
1244 ProcessPriorityManagerChild::Observe(
1245 nsISupports* aSubject,
1246 const char* aTopic,
1247 const char16_t* aData)
1249 MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed"));
1251 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
1252 NS_ENSURE_TRUE(props, NS_OK);
1254 int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN);
1255 props->GetPropertyAsInt32(NS_LITERAL_STRING("priority"), &priority);
1256 NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK);
1258 mCachedPriority = static_cast<ProcessPriority>(priority);
1260 return NS_OK;
1263 bool
1264 ProcessPriorityManagerChild::CurrentProcessIsForeground()
1266 return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
1267 mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
1270 bool
1271 ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
1273 return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
1274 mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
1277 /* static */ StaticAutoPtr<BackgroundProcessLRUPool>
1278 BackgroundProcessLRUPool::sSingleton;
1280 /* static */ BackgroundProcessLRUPool*
1281 BackgroundProcessLRUPool::Singleton()
1283 if (!sSingleton) {
1284 sSingleton = new BackgroundProcessLRUPool();
1285 ClearOnShutdown(&sSingleton);
1287 return sSingleton;
1290 BackgroundProcessLRUPool::BackgroundProcessLRUPool()
1292 EnsureLRUPool();
1295 uint32_t
1296 BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex)
1298 // Set LRU level of each background process and maintain LRU buffer as below:
1300 // Priority background : LRU0
1301 // Priority background+1: LRU1, LRU2
1302 // Priority background+2: LRU3, LRU4, LRU5, LRU6
1303 // Priority background+3: LRU7, LRU8, LRU9, LRU10, LRU11, LRU12, LRU13, LRU14
1304 // ...
1305 // Priority background+L-1: 2^(number of background LRU pool levels - 1)
1306 // (End of buffer)
1308 return (uint32_t)(log((float)aBackgroundLRUPoolIndex) / log(2.0));
1311 void
1312 BackgroundProcessLRUPool::EnsureLRUPool()
1314 // We set mBackgroundLRUPoolLevels according to our pref.
1315 // This value is used to set background process LRU pool
1316 if (!NS_SUCCEEDED(Preferences::GetInt(
1317 "dom.ipc.processPriorityManager.backgroundLRUPoolLevels",
1318 &mLRUPoolLevels))) {
1319 mLRUPoolLevels = 1;
1322 if (mLRUPoolLevels <= 0) {
1323 MOZ_CRASH();
1326 // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
1327 // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
1328 // This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
1329 // See bug 822325 comment 49
1330 MOZ_ASSERT(mLRUPoolLevels <= 6);
1332 // LRU pool size = 2 ^ (number of background LRU pool levels) - 1
1333 mLRUPoolSize = (1 << mLRUPoolLevels) - 1;
1335 mLRUPoolAvailableIndex = 0;
1337 LOG("Making background LRU pool with size(%d)", mLRUPoolSize);
1339 mLRUPool.InsertElementsAt(0, mLRUPoolSize, (ContentParent*)nullptr);
1342 void
1343 BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool(
1344 ContentParent* aContentParent)
1346 for (int32_t i = 0; i < mLRUPoolSize; i++) {
1347 if (mLRUPool[i]) {
1348 if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
1350 mLRUPool[i] = nullptr;
1351 LOG("Remove ChildID(%llu) from LRU pool", aContentParent->ChildID());
1353 // After we remove this ContentParent from LRU pool, we still need to
1354 // update the available index if the index of removed one is less than
1355 // the available index we already have.
1356 UpdateAvailableIndexInLRUPool(aContentParent, i);
1357 break;
1363 nsresult
1364 BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool(
1365 ContentParent* aContentParent,
1366 int32_t aTargetIndex)
1368 // If we specify which index we want to assign to mLRUPoolAvailableIndex,
1369 // We have to make sure the index in LRUPool doesn't point to any
1370 // ContentParent.
1371 if (aTargetIndex >= 0 && aTargetIndex < mLRUPoolSize &&
1372 aTargetIndex < mLRUPoolAvailableIndex &&
1373 !mLRUPool[aTargetIndex]) {
1374 mLRUPoolAvailableIndex = aTargetIndex;
1375 return NS_OK;
1378 // When we didn't specify any legal aTargetIndex, then we just check
1379 // whether current mLRUPoolAvailableIndex points to any ContentParent or not.
1380 if (mLRUPoolAvailableIndex >= 0 && mLRUPoolAvailableIndex < mLRUPoolSize &&
1381 !(mLRUPool[mLRUPoolAvailableIndex])) {
1382 return NS_OK;
1385 // Both above way failed. So now we have to find proper value
1386 // for mLRUPoolAvailableIndex.
1387 // We are looking for an available index. We only shift process with
1388 // LRU less than the available index should have, so we stop update
1389 // mLRUPoolAvailableIndex from the for loop once we got a candidate.
1390 mLRUPoolAvailableIndex = -1;
1392 for (int32_t i = 0; i < mLRUPoolSize; i++) {
1393 if (mLRUPool[i]) {
1394 if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
1395 LOG("ChildID(%llu) already in LRU pool", aContentParent->ChildID());
1396 MOZ_ASSERT(false);
1397 return NS_ERROR_UNEXPECTED;
1399 continue;
1400 } else {
1401 if (mLRUPoolAvailableIndex == -1) {
1402 mLRUPoolAvailableIndex = i;
1407 // If the LRUPool is already full, mLRUPoolAvailableIndex is still -1 after
1408 // above loop finished. We should set mLRUPoolAvailableIndex
1409 // to mLRUPoolSize - 1 in this case. Here uses the mod operator to do it:
1410 // New mLRUPoolAvailableIndex either equals old mLRUPoolAvailableIndex, or
1411 // mLRUPoolSize - 1 if old mLRUPoolAvailableIndex is -1.
1412 mLRUPoolAvailableIndex =
1413 (mLRUPoolAvailableIndex + mLRUPoolSize) % mLRUPoolSize;
1415 return NS_OK;
1418 void
1419 BackgroundProcessLRUPool::ShiftLRUPool()
1421 for (int32_t i = mLRUPoolAvailableIndex; i > 0; i--) {
1422 mLRUPool[i] = mLRUPool[i - 1];
1423 // Check whether i+1 is power of Two.
1424 // If so, then it crossed a LRU group boundary and
1425 // we need to assign its new process priority LRU.
1426 if (!((i + 1) & i)) {
1427 ProcessPriorityManagerImpl::GetSingleton()->SetProcessPriority(
1428 mLRUPool[i], PROCESS_PRIORITY_BACKGROUND, CalculateLRULevel(i + 1));
1433 void
1434 BackgroundProcessLRUPool::AddIntoBackgroundLRUPool(
1435 ContentParent* aContentParent)
1437 // We have to make sure that we have correct available index in LRU pool
1438 if (!NS_SUCCEEDED(
1439 UpdateAvailableIndexInLRUPool(aContentParent))) {
1440 return;
1443 // Shift the list in the pool, so we have room at index 0 for the newly added
1444 // ContentParent
1445 ShiftLRUPool();
1447 mLRUPool[0] = aContentParent;
1449 LOG("Add ChildID(%llu) into LRU pool", aContentParent->ChildID());
1452 } // anonymous namespace
1454 namespace mozilla {
1456 /* static */ void
1457 ProcessPriorityManager::Init()
1459 ProcessPriorityManagerImpl::StaticInit();
1460 ProcessPriorityManagerChild::StaticInit();
1463 /* static */ void
1464 ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
1465 ProcessPriority aPriority)
1467 MOZ_ASSERT(aContentParent);
1469 ProcessPriorityManagerImpl* singleton =
1470 ProcessPriorityManagerImpl::GetSingleton();
1471 if (singleton) {
1472 singleton->SetProcessPriority(aContentParent, aPriority);
1476 /* static */ void
1477 ProcessPriorityManager::RemoveFromBackgroundLRUPool(
1478 ContentParent* aContentParent)
1480 MOZ_ASSERT(aContentParent);
1482 BackgroundProcessLRUPool* singleton =
1483 BackgroundProcessLRUPool::Singleton();
1484 if (singleton) {
1485 singleton->RemoveFromBackgroundLRUPool(aContentParent);
1489 /* static */ void
1490 ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent)
1492 MOZ_ASSERT(aContentParent);
1494 BackgroundProcessLRUPool* singleton =
1495 BackgroundProcessLRUPool::Singleton();
1496 if (singleton) {
1497 singleton->AddIntoBackgroundLRUPool(aContentParent);
1501 /* static */ bool
1502 ProcessPriorityManager::CurrentProcessIsForeground()
1504 return ProcessPriorityManagerChild::Singleton()->
1505 CurrentProcessIsForeground();
1508 /* static */ bool
1509 ProcessPriorityManager::AnyProcessHasHighPriority()
1511 ProcessPriorityManagerImpl* singleton =
1512 ProcessPriorityManagerImpl::GetSingleton();
1514 if (singleton) {
1515 return singleton->ChildProcessHasHighPriority();
1516 } else {
1517 return ProcessPriorityManagerChild::Singleton()->
1518 CurrentProcessIsHighPriority();
1522 } // namespace mozilla