Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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;
1002 }
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());
1016 }
1018 if (!mContentParent ||
1019 !ProcessPriorityManagerImpl::PrefsEnabled() ||
1020 (mPriority == aPriority && mCPUPriority == aCPUPriority)) {
1021 return;
1022 }
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;
1030 }
1032 if (aPriority == PROCESS_PRIORITY_BACKGROUND &&
1033 mPriority != PROCESS_PRIORITY_BACKGROUND &&
1034 !IsPreallocated()) {
1035 ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent);
1036 }
1038 if (aPriority != PROCESS_PRIORITY_BACKGROUND &&
1039 mPriority == PROCESS_PRIORITY_BACKGROUND &&
1040 !IsPreallocated()) {
1041 ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
1042 }
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);
1056 }
1058 if (aPriority < PROCESS_PRIORITY_FOREGROUND) {
1059 unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("low-memory"));
1060 }
1062 FireTestOnlyObserverNotification("process-priority-set",
1063 ProcessPriorityToString(mPriority, mCPUPriority));
1065 if (oldPriority != mPriority) {
1066 ProcessPriorityManagerImpl::GetSingleton()->
1067 NotifyProcessPriorityChanged(this, oldPriority);
1068 }
1069 }
1071 void
1072 ParticularProcessPriorityManager::ShutDown()
1073 {
1074 MOZ_ASSERT(mContentParent);
1076 UnregisterWakeLockObserver(this);
1078 if (mResetPriorityTimer) {
1079 mResetPriorityTimer->Cancel();
1080 mResetPriorityTimer = nullptr;
1081 }
1083 if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
1084 ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
1085 }
1087 mContentParent = nullptr;
1088 }
1090 void
1091 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
1092 const char* aTopic,
1093 const nsACString& aData /* = EmptyCString() */)
1094 {
1095 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1096 return;
1097 }
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());
1107 }
1109 void
1110 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
1111 const char* aTopic,
1112 const char* aData /* = nullptr */ )
1113 {
1114 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1115 return;
1116 }
1118 nsAutoCString data;
1119 if (aData) {
1120 data.AppendASCII(aData);
1121 }
1123 FireTestOnlyObserverNotification(aTopic, data);
1124 }
1126 void
1127 ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
1128 const char* aTopic,
1129 const nsACString& aData /* = EmptyCString() */)
1130 {
1131 if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
1132 return;
1133 }
1135 nsAutoCString data(nsPrintfCString("%lld", ChildID()));
1136 if (!aData.IsEmpty()) {
1137 data.AppendLiteral(":");
1138 data.Append(aData);
1139 }
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);
1147 }
1149 StaticRefPtr<ProcessPriorityManagerChild>
1150 ProcessPriorityManagerChild::sSingleton;
1152 /* static */ void
1153 ProcessPriorityManagerChild::StaticInit()
1154 {
1155 if (!sSingleton) {
1156 sSingleton = new ProcessPriorityManagerChild();
1157 sSingleton->Init();
1158 ClearOnShutdown(&sSingleton);
1159 }
1160 }
1162 /* static */ ProcessPriorityManagerChild*
1163 ProcessPriorityManagerChild::Singleton()
1164 {
1165 StaticInit();
1166 return sSingleton;
1167 }
1169 NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild,
1170 nsIObserver)
1172 ProcessPriorityManagerChild::ProcessPriorityManagerChild()
1173 {
1174 if (XRE_GetProcessType() == GeckoProcessType_Default) {
1175 mCachedPriority = PROCESS_PRIORITY_MASTER;
1176 } else {
1177 mCachedPriority = PROCESS_PRIORITY_UNKNOWN;
1178 }
1179 }
1181 void
1182 ProcessPriorityManagerChild::Init()
1183 {
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);
1190 }
1191 }
1193 NS_IMETHODIMP
1194 ProcessPriorityManagerChild::Observe(
1195 nsISupports* aSubject,
1196 const char* aTopic,
1197 const char16_t* aData)
1198 {
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;
1211 }
1213 bool
1214 ProcessPriorityManagerChild::CurrentProcessIsForeground()
1215 {
1216 return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
1217 mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
1218 }
1220 bool
1221 ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
1222 {
1223 return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
1224 mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
1225 }
1227 /* static */ StaticAutoPtr<BackgroundProcessLRUPool>
1228 BackgroundProcessLRUPool::sSingleton;
1230 /* static */ BackgroundProcessLRUPool*
1231 BackgroundProcessLRUPool::Singleton()
1232 {
1233 if (!sSingleton) {
1234 sSingleton = new BackgroundProcessLRUPool();
1235 ClearOnShutdown(&sSingleton);
1236 }
1237 return sSingleton;
1238 }
1240 BackgroundProcessLRUPool::BackgroundProcessLRUPool()
1241 {
1242 EnsureLRUPool();
1243 }
1245 uint32_t
1246 BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex)
1247 {
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));
1259 }
1261 void
1262 BackgroundProcessLRUPool::EnsureLRUPool()
1263 {
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;
1270 }
1272 if (mLRUPoolLevels <= 0) {
1273 MOZ_CRASH();
1274 }
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);
1290 }
1292 void
1293 BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool(
1294 ContentParent* aContentParent)
1295 {
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;
1308 }
1309 }
1310 }
1311 }
1313 nsresult
1314 BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool(
1315 ContentParent* aContentParent,
1316 int32_t aTargetIndex)
1317 {
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;
1326 }
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;
1333 }
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;
1348 }
1349 continue;
1350 } else {
1351 if (mLRUPoolAvailableIndex == -1) {
1352 mLRUPoolAvailableIndex = i;
1353 }
1354 }
1355 }
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;
1366 }
1368 void
1369 BackgroundProcessLRUPool::ShiftLRUPool()
1370 {
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));
1379 }
1380 }
1381 }
1383 void
1384 BackgroundProcessLRUPool::AddIntoBackgroundLRUPool(
1385 ContentParent* aContentParent)
1386 {
1387 // We have to make sure that we have correct available index in LRU pool
1388 if (!NS_SUCCEEDED(
1389 UpdateAvailableIndexInLRUPool(aContentParent))) {
1390 return;
1391 }
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());
1400 }
1402 } // anonymous namespace
1404 namespace mozilla {
1406 /* static */ void
1407 ProcessPriorityManager::Init()
1408 {
1409 ProcessPriorityManagerImpl::StaticInit();
1410 ProcessPriorityManagerChild::StaticInit();
1411 }
1413 /* static */ void
1414 ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
1415 ProcessPriority aPriority)
1416 {
1417 MOZ_ASSERT(aContentParent);
1419 ProcessPriorityManagerImpl* singleton =
1420 ProcessPriorityManagerImpl::GetSingleton();
1421 if (singleton) {
1422 singleton->SetProcessPriority(aContentParent, aPriority);
1423 }
1424 }
1426 /* static */ void
1427 ProcessPriorityManager::RemoveFromBackgroundLRUPool(
1428 ContentParent* aContentParent)
1429 {
1430 MOZ_ASSERT(aContentParent);
1432 BackgroundProcessLRUPool* singleton =
1433 BackgroundProcessLRUPool::Singleton();
1434 if (singleton) {
1435 singleton->RemoveFromBackgroundLRUPool(aContentParent);
1436 }
1437 }
1439 /* static */ void
1440 ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent)
1441 {
1442 MOZ_ASSERT(aContentParent);
1444 BackgroundProcessLRUPool* singleton =
1445 BackgroundProcessLRUPool::Singleton();
1446 if (singleton) {
1447 singleton->AddIntoBackgroundLRUPool(aContentParent);
1448 }
1449 }
1451 /* static */ bool
1452 ProcessPriorityManager::CurrentProcessIsForeground()
1453 {
1454 return ProcessPriorityManagerChild::Singleton()->
1455 CurrentProcessIsForeground();
1456 }
1458 /* static */ bool
1459 ProcessPriorityManager::AnyProcessHasHighPriority()
1460 {
1461 ProcessPriorityManagerImpl* singleton =
1462 ProcessPriorityManagerImpl::GetSingleton();
1464 if (singleton) {
1465 return singleton->ChildProcessHasHighPriority();
1466 } else {
1467 return ProcessPriorityManagerChild::Singleton()->
1468 CurrentProcessIsHighPriority();
1469 }
1470 }
1472 } // namespace mozilla