Coverage Report

Created: 2018-09-25 14:53

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