dom/ipc/ProcessPriorityManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial