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