1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/ipc/ProcessPriorityManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1472 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et ft=cpp : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ProcessPriorityManager.h" 1.11 +#include "mozilla/ClearOnShutdown.h" 1.12 +#include "mozilla/dom/ContentParent.h" 1.13 +#include "mozilla/dom/Element.h" 1.14 +#include "mozilla/dom/TabParent.h" 1.15 +#include "mozilla/Hal.h" 1.16 +#include "mozilla/Preferences.h" 1.17 +#include "mozilla/Services.h" 1.18 +#include "mozilla/unused.h" 1.19 +#include "AudioChannelService.h" 1.20 +#include "prlog.h" 1.21 +#include "nsPrintfCString.h" 1.22 +#include "nsXULAppAPI.h" 1.23 +#include "nsIFrameLoader.h" 1.24 +#include "nsIObserverService.h" 1.25 +#include "StaticPtr.h" 1.26 +#include "nsIMozBrowserFrame.h" 1.27 +#include "nsIObserver.h" 1.28 +#include "nsITimer.h" 1.29 +#include "nsIPropertyBag2.h" 1.30 +#include "nsComponentManagerUtils.h" 1.31 + 1.32 +#ifdef XP_WIN 1.33 +#include <process.h> 1.34 +#define getpid _getpid 1.35 +#else 1.36 +#include <unistd.h> 1.37 +#endif 1.38 + 1.39 +#ifdef LOG 1.40 +#undef LOG 1.41 +#endif 1.42 + 1.43 +// Use LOGP inside a ParticularProcessPriorityManager method; use LOG 1.44 +// everywhere else. LOGP prints out information about the particular process 1.45 +// priority manager. 1.46 +// 1.47 +// (Wow, our logging story is a huge mess.) 1.48 + 1.49 +// #define ENABLE_LOGGING 1 1.50 + 1.51 +#if defined(ANDROID) && defined(ENABLE_LOGGING) 1.52 +# include <android/log.h> 1.53 +# define LOG(fmt, ...) \ 1.54 + __android_log_print(ANDROID_LOG_INFO, \ 1.55 + "Gecko:ProcessPriorityManager", \ 1.56 + fmt, ## __VA_ARGS__) 1.57 +# define LOGP(fmt, ...) \ 1.58 + __android_log_print(ANDROID_LOG_INFO, \ 1.59 + "Gecko:ProcessPriorityManager", \ 1.60 + "[%schild-id=%llu, pid=%d] " fmt, \ 1.61 + NameWithComma().get(), \ 1.62 + (long long unsigned) ChildID(), Pid(), ## __VA_ARGS__) 1.63 + 1.64 +#elif defined(ENABLE_LOGGING) 1.65 +# define LOG(fmt, ...) \ 1.66 + printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__) 1.67 +# define LOGP(fmt, ...) \ 1.68 + printf("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt "\n", \ 1.69 + NameWithComma().get(), \ 1.70 + (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__) 1.71 + 1.72 +#elif defined(PR_LOGGING) 1.73 + static PRLogModuleInfo* 1.74 + GetPPMLog() 1.75 + { 1.76 + static PRLogModuleInfo *sLog; 1.77 + if (!sLog) 1.78 + sLog = PR_NewLogModule("ProcessPriorityManager"); 1.79 + return sLog; 1.80 + } 1.81 +# define LOG(fmt, ...) \ 1.82 + PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \ 1.83 + ("ProcessPriorityManager - " fmt, ##__VA_ARGS__)) 1.84 +# define LOGP(fmt, ...) \ 1.85 + PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \ 1.86 + ("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt, \ 1.87 + NameWithComma().get(), \ 1.88 + (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__)) 1.89 +#else 1.90 +#define LOG(fmt, ...) 1.91 +#define LOGP(fmt, ...) 1.92 +#endif 1.93 + 1.94 +using namespace mozilla; 1.95 +using namespace mozilla::dom; 1.96 +using namespace mozilla::hal; 1.97 + 1.98 +namespace { 1.99 + 1.100 +class ParticularProcessPriorityManager; 1.101 + 1.102 +/** 1.103 + * This singleton class does the work to implement the process priority manager 1.104 + * in the main process. This class may not be used in child processes. (You 1.105 + * can call StaticInit, but it won't do anything, and GetSingleton() will 1.106 + * return null.) 1.107 + * 1.108 + * ProcessPriorityManager::CurrentProcessIsForeground() and 1.109 + * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in 1.110 + * any process, are handled separately, by the ProcessPriorityManagerChild 1.111 + * class. 1.112 + */ 1.113 +class ProcessPriorityManagerImpl MOZ_FINAL 1.114 + : public nsIObserver 1.115 +{ 1.116 +public: 1.117 + /** 1.118 + * If we're in the main process, get the ProcessPriorityManagerImpl 1.119 + * singleton. If we're in a child process, return null. 1.120 + */ 1.121 + static ProcessPriorityManagerImpl* GetSingleton(); 1.122 + 1.123 + static void StaticInit(); 1.124 + static bool PrefsEnabled(); 1.125 + 1.126 + NS_DECL_ISUPPORTS 1.127 + NS_DECL_NSIOBSERVER 1.128 + 1.129 + /** 1.130 + * This function implements ProcessPriorityManager::SetProcessPriority. 1.131 + */ 1.132 + void SetProcessPriority(ContentParent* aContentParent, 1.133 + ProcessPriority aPriority, 1.134 + uint32_t aBackgroundLRU = 0); 1.135 + 1.136 + /** 1.137 + * If a magic testing-only pref is set, notify the observer service on the 1.138 + * given topic with the given data. This is used for testing 1.139 + */ 1.140 + void FireTestOnlyObserverNotification(const char* aTopic, 1.141 + const nsACString& aData = EmptyCString()); 1.142 + 1.143 + /** 1.144 + * Does some process, other than the one handled by aParticularManager, have 1.145 + * priority FOREGROUND_HIGH? 1.146 + */ 1.147 + bool OtherProcessHasHighPriority( 1.148 + ParticularProcessPriorityManager* aParticularManager); 1.149 + 1.150 + /** 1.151 + * Does one of the child processes have priority FOREGROUND_HIGH? 1.152 + */ 1.153 + bool ChildProcessHasHighPriority(); 1.154 + 1.155 + /** 1.156 + * This must be called by a ParticularProcessPriorityManager when it changes 1.157 + * its priority. 1.158 + */ 1.159 + void NotifyProcessPriorityChanged( 1.160 + ParticularProcessPriorityManager* aParticularManager, 1.161 + hal::ProcessPriority aOldPriority); 1.162 + 1.163 +private: 1.164 + static bool sPrefListenersRegistered; 1.165 + static bool sInitialized; 1.166 + static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton; 1.167 + 1.168 + static void PrefChangedCallback(const char* aPref, void* aClosure); 1.169 + 1.170 + ProcessPriorityManagerImpl(); 1.171 + ~ProcessPriorityManagerImpl() {} 1.172 + DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl); 1.173 + 1.174 + void Init(); 1.175 + 1.176 + already_AddRefed<ParticularProcessPriorityManager> 1.177 + GetParticularProcessPriorityManager(ContentParent* aContentParent); 1.178 + 1.179 + void ObserveContentParentCreated(nsISupports* aContentParent); 1.180 + void ObserveContentParentDestroyed(nsISupports* aSubject); 1.181 + 1.182 + nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> > 1.183 + mParticularManagers; 1.184 + 1.185 + nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs; 1.186 +}; 1.187 + 1.188 +/** 1.189 + * This singleton class implements the parts of the process priority manager 1.190 + * that are available from all processes. 1.191 + */ 1.192 +class ProcessPriorityManagerChild MOZ_FINAL 1.193 + : public nsIObserver 1.194 +{ 1.195 +public: 1.196 + static void StaticInit(); 1.197 + static ProcessPriorityManagerChild* Singleton(); 1.198 + 1.199 + NS_DECL_ISUPPORTS 1.200 + NS_DECL_NSIOBSERVER 1.201 + 1.202 + bool CurrentProcessIsForeground(); 1.203 + bool CurrentProcessIsHighPriority(); 1.204 + 1.205 +private: 1.206 + static StaticRefPtr<ProcessPriorityManagerChild> sSingleton; 1.207 + 1.208 + ProcessPriorityManagerChild(); 1.209 + ~ProcessPriorityManagerChild() {} 1.210 + DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild); 1.211 + 1.212 + void Init(); 1.213 + 1.214 + hal::ProcessPriority mCachedPriority; 1.215 +}; 1.216 + 1.217 +/** 1.218 + * This class manages the priority of one particular process. It is 1.219 + * main-process only. 1.220 + */ 1.221 +class ParticularProcessPriorityManager MOZ_FINAL 1.222 + : public WakeLockObserver 1.223 + , public nsIObserver 1.224 + , public nsITimerCallback 1.225 + , public nsSupportsWeakReference 1.226 +{ 1.227 +public: 1.228 + ParticularProcessPriorityManager(ContentParent* aContentParent); 1.229 + ~ParticularProcessPriorityManager(); 1.230 + 1.231 + NS_DECL_ISUPPORTS 1.232 + NS_DECL_NSIOBSERVER 1.233 + NS_DECL_NSITIMERCALLBACK 1.234 + 1.235 + virtual void Notify(const WakeLockInformation& aInfo) MOZ_OVERRIDE; 1.236 + void Init(); 1.237 + 1.238 + int32_t Pid() const; 1.239 + uint64_t ChildID() const; 1.240 + bool IsPreallocated() const; 1.241 + 1.242 + /** 1.243 + * Used in logging, this method returns the ContentParent's name followed by 1.244 + * ", ". If we can't get the ContentParent's name for some reason, it 1.245 + * returns an empty string. 1.246 + * 1.247 + * The reference returned here is guaranteed to be live until the next call 1.248 + * to NameWithComma() or until the ParticularProcessPriorityManager is 1.249 + * destroyed, whichever comes first. 1.250 + */ 1.251 + const nsAutoCString& NameWithComma(); 1.252 + 1.253 + bool HasAppType(const char* aAppType); 1.254 + bool IsExpectingSystemMessage(); 1.255 + 1.256 + void OnAudioChannelProcessChanged(nsISupports* aSubject); 1.257 + void OnRemoteBrowserFrameShown(nsISupports* aSubject); 1.258 + void OnTabParentDestroyed(nsISupports* aSubject); 1.259 + void OnFrameloaderVisibleChanged(nsISupports* aSubject); 1.260 + void OnChannelConnected(nsISupports* aSubject); 1.261 + 1.262 + ProcessPriority CurrentPriority(); 1.263 + ProcessPriority ComputePriority(); 1.264 + ProcessCPUPriority ComputeCPUPriority(ProcessPriority aPriority); 1.265 + 1.266 + void ScheduleResetPriority(const char* aTimeoutPref); 1.267 + void ResetPriority(); 1.268 + void ResetPriorityNow(); 1.269 + void ResetCPUPriorityNow(); 1.270 + 1.271 + /** 1.272 + * This overload is equivalent to SetPriorityNow(aPriority, 1.273 + * ComputeCPUPriority()). 1.274 + */ 1.275 + void SetPriorityNow(ProcessPriority aPriority, 1.276 + uint32_t aBackgroundLRU = 0); 1.277 + 1.278 + void SetPriorityNow(ProcessPriority aPriority, 1.279 + ProcessCPUPriority aCPUPriority, 1.280 + uint32_t aBackgroundLRU = 0); 1.281 + 1.282 + void ShutDown(); 1.283 + 1.284 +private: 1.285 + void FireTestOnlyObserverNotification( 1.286 + const char* aTopic, 1.287 + const nsACString& aData = EmptyCString()); 1.288 + 1.289 + void FireTestOnlyObserverNotification( 1.290 + const char* aTopic, 1.291 + const char* aData = nullptr); 1.292 + 1.293 + ContentParent* mContentParent; 1.294 + uint64_t mChildID; 1.295 + ProcessPriority mPriority; 1.296 + ProcessCPUPriority mCPUPriority; 1.297 + bool mHoldsCPUWakeLock; 1.298 + bool mHoldsHighPriorityWakeLock; 1.299 + 1.300 + /** 1.301 + * Used to implement NameWithComma(). 1.302 + */ 1.303 + nsAutoCString mNameWithComma; 1.304 + 1.305 + nsCOMPtr<nsITimer> mResetPriorityTimer; 1.306 +}; 1.307 + 1.308 +class BackgroundProcessLRUPool MOZ_FINAL 1.309 +{ 1.310 +public: 1.311 + static BackgroundProcessLRUPool* Singleton(); 1.312 + 1.313 + /** 1.314 + * Used to remove a ContentParent from background LRU pool when 1.315 + * it is destroyed or its priority changed from BACKGROUND to others. 1.316 + */ 1.317 + void RemoveFromBackgroundLRUPool(ContentParent* aContentParent); 1.318 + 1.319 + /** 1.320 + * Used to add a ContentParent into background LRU pool when 1.321 + * its priority changed to BACKGROUND from others. 1.322 + */ 1.323 + void AddIntoBackgroundLRUPool(ContentParent* aContentParent); 1.324 + 1.325 +private: 1.326 + static StaticAutoPtr<BackgroundProcessLRUPool> sSingleton; 1.327 + 1.328 + int32_t mLRUPoolLevels; 1.329 + int32_t mLRUPoolSize; 1.330 + int32_t mLRUPoolAvailableIndex; 1.331 + nsTArray<ContentParent*> mLRUPool; 1.332 + 1.333 + uint32_t CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex); 1.334 + 1.335 + nsresult UpdateAvailableIndexInLRUPool( 1.336 + ContentParent* aContentParent, 1.337 + int32_t aTargetIndex = -1); 1.338 + 1.339 + void ShiftLRUPool(); 1.340 + 1.341 + void EnsureLRUPool(); 1.342 + 1.343 + BackgroundProcessLRUPool(); 1.344 + DISALLOW_EVIL_CONSTRUCTORS(BackgroundProcessLRUPool); 1.345 + 1.346 +}; 1.347 + 1.348 +/* static */ bool ProcessPriorityManagerImpl::sInitialized = false; 1.349 +/* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false; 1.350 +/* static */ StaticRefPtr<ProcessPriorityManagerImpl> 1.351 + ProcessPriorityManagerImpl::sSingleton; 1.352 + 1.353 +NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl, 1.354 + nsIObserver); 1.355 + 1.356 +/* static */ void 1.357 +ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref, 1.358 + void* aClosure) 1.359 +{ 1.360 + StaticInit(); 1.361 +} 1.362 + 1.363 +/* static */ bool 1.364 +ProcessPriorityManagerImpl::PrefsEnabled() 1.365 +{ 1.366 + return Preferences::GetBool("dom.ipc.processPriorityManager.enabled") && 1.367 + !Preferences::GetBool("dom.ipc.tabs.disabled"); 1.368 +} 1.369 + 1.370 +/* static */ void 1.371 +ProcessPriorityManagerImpl::StaticInit() 1.372 +{ 1.373 + if (sInitialized) { 1.374 + return; 1.375 + } 1.376 + 1.377 + // The process priority manager is main-process only. 1.378 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.379 + sInitialized = true; 1.380 + return; 1.381 + } 1.382 + 1.383 + // If IPC tabs aren't enabled at startup, don't bother with any of this. 1.384 + if (!PrefsEnabled()) { 1.385 + LOG("InitProcessPriorityManager bailing due to prefs."); 1.386 + 1.387 + // Run StaticInit() again if the prefs change. We don't expect this to 1.388 + // happen in normal operation, but it happens during testing. 1.389 + if (!sPrefListenersRegistered) { 1.390 + sPrefListenersRegistered = true; 1.391 + Preferences::RegisterCallback(PrefChangedCallback, 1.392 + "dom.ipc.processPriorityManager.enabled"); 1.393 + Preferences::RegisterCallback(PrefChangedCallback, 1.394 + "dom.ipc.tabs.disabled"); 1.395 + } 1.396 + return; 1.397 + } 1.398 + 1.399 + sInitialized = true; 1.400 + 1.401 + sSingleton = new ProcessPriorityManagerImpl(); 1.402 + sSingleton->Init(); 1.403 + ClearOnShutdown(&sSingleton); 1.404 +} 1.405 + 1.406 +/* static */ ProcessPriorityManagerImpl* 1.407 +ProcessPriorityManagerImpl::GetSingleton() 1.408 +{ 1.409 + if (!sSingleton) { 1.410 + StaticInit(); 1.411 + } 1.412 + 1.413 + return sSingleton; 1.414 +} 1.415 + 1.416 +ProcessPriorityManagerImpl::ProcessPriorityManagerImpl() 1.417 +{ 1.418 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.419 +} 1.420 + 1.421 +void 1.422 +ProcessPriorityManagerImpl::Init() 1.423 +{ 1.424 + LOG("Starting up. This is the master process."); 1.425 + 1.426 + // The master process's priority never changes; set it here and then forget 1.427 + // about it. We'll manage only subprocesses' priorities using the process 1.428 + // priority manager. 1.429 + hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER, 1.430 + PROCESS_CPU_PRIORITY_NORMAL); 1.431 + 1.432 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.433 + if (os) { 1.434 + os->AddObserver(this, "ipc:content-created", /* ownsWeak */ false); 1.435 + os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ false); 1.436 + } 1.437 +} 1.438 + 1.439 +NS_IMETHODIMP 1.440 +ProcessPriorityManagerImpl::Observe( 1.441 + nsISupports* aSubject, 1.442 + const char* aTopic, 1.443 + const char16_t* aData) 1.444 +{ 1.445 + nsDependentCString topic(aTopic); 1.446 + if (topic.EqualsLiteral("ipc:content-created")) { 1.447 + ObserveContentParentCreated(aSubject); 1.448 + } else if (topic.EqualsLiteral("ipc:content-shutdown")) { 1.449 + ObserveContentParentDestroyed(aSubject); 1.450 + } else { 1.451 + MOZ_ASSERT(false); 1.452 + } 1.453 + 1.454 + return NS_OK; 1.455 +} 1.456 + 1.457 +already_AddRefed<ParticularProcessPriorityManager> 1.458 +ProcessPriorityManagerImpl::GetParticularProcessPriorityManager( 1.459 + ContentParent* aContentParent) 1.460 +{ 1.461 + nsRefPtr<ParticularProcessPriorityManager> pppm; 1.462 + mParticularManagers.Get(aContentParent->ChildID(), &pppm); 1.463 + if (!pppm) { 1.464 + pppm = new ParticularProcessPriorityManager(aContentParent); 1.465 + pppm->Init(); 1.466 + mParticularManagers.Put(aContentParent->ChildID(), pppm); 1.467 + 1.468 + FireTestOnlyObserverNotification("process-created", 1.469 + nsPrintfCString("%lld", aContentParent->ChildID())); 1.470 + } 1.471 + 1.472 + return pppm.forget(); 1.473 +} 1.474 + 1.475 +void 1.476 +ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent, 1.477 + ProcessPriority aPriority, 1.478 + uint32_t aBackgroundLRU) 1.479 +{ 1.480 + MOZ_ASSERT(aContentParent); 1.481 + nsRefPtr<ParticularProcessPriorityManager> pppm = 1.482 + GetParticularProcessPriorityManager(aContentParent); 1.483 + pppm->SetPriorityNow(aPriority, aBackgroundLRU); 1.484 +} 1.485 + 1.486 +void 1.487 +ProcessPriorityManagerImpl::ObserveContentParentCreated( 1.488 + nsISupports* aContentParent) 1.489 +{ 1.490 + // Do nothing; it's sufficient to get the PPPM. But assign to nsRefPtr so we 1.491 + // don't leak the already_AddRefed object. 1.492 + nsCOMPtr<nsIObserver> cp = do_QueryInterface(aContentParent); 1.493 + nsRefPtr<ParticularProcessPriorityManager> pppm = 1.494 + GetParticularProcessPriorityManager(static_cast<ContentParent*>(cp.get())); 1.495 +} 1.496 + 1.497 +static PLDHashOperator 1.498 +EnumerateParticularProcessPriorityManagers( 1.499 + const uint64_t& aKey, 1.500 + nsRefPtr<ParticularProcessPriorityManager> aValue, 1.501 + void* aUserData) 1.502 +{ 1.503 + nsTArray<nsRefPtr<ParticularProcessPriorityManager> >* aArray = 1.504 + static_cast<nsTArray<nsRefPtr<ParticularProcessPriorityManager> >*>(aUserData); 1.505 + aArray->AppendElement(aValue); 1.506 + return PL_DHASH_NEXT; 1.507 +} 1.508 + 1.509 +void 1.510 +ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject) 1.511 +{ 1.512 + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 1.513 + NS_ENSURE_TRUE_VOID(props); 1.514 + 1.515 + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; 1.516 + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); 1.517 + NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); 1.518 + 1.519 + nsRefPtr<ParticularProcessPriorityManager> pppm; 1.520 + mParticularManagers.Get(childID, &pppm); 1.521 + MOZ_ASSERT(pppm); 1.522 + if (pppm) { 1.523 + pppm->ShutDown(); 1.524 + } 1.525 + 1.526 + mParticularManagers.Remove(childID); 1.527 + 1.528 + if (mHighPriorityChildIDs.Contains(childID)) { 1.529 + mHighPriorityChildIDs.RemoveEntry(childID); 1.530 + 1.531 + // We just lost a high-priority process; reset everyone's CPU priorities. 1.532 + nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms; 1.533 + mParticularManagers.EnumerateRead( 1.534 + &EnumerateParticularProcessPriorityManagers, 1.535 + &pppms); 1.536 + 1.537 + for (uint32_t i = 0; i < pppms.Length(); i++) { 1.538 + pppms[i]->ResetCPUPriorityNow(); 1.539 + } 1.540 + } 1.541 +} 1.542 + 1.543 +bool 1.544 +ProcessPriorityManagerImpl::OtherProcessHasHighPriority( 1.545 + ParticularProcessPriorityManager* aParticularManager) 1.546 +{ 1.547 + if (mHighPriorityChildIDs.Contains(aParticularManager->ChildID())) { 1.548 + return mHighPriorityChildIDs.Count() > 1; 1.549 + } 1.550 + return mHighPriorityChildIDs.Count() > 0; 1.551 +} 1.552 + 1.553 +bool 1.554 +ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void ) 1.555 +{ 1.556 + return mHighPriorityChildIDs.Count() > 0; 1.557 +} 1.558 + 1.559 +void 1.560 +ProcessPriorityManagerImpl::NotifyProcessPriorityChanged( 1.561 + ParticularProcessPriorityManager* aParticularManager, 1.562 + ProcessPriority aOldPriority) 1.563 +{ 1.564 + // This priority change can only affect other processes' priorities if we're 1.565 + // changing to/from FOREGROUND_HIGH. 1.566 + 1.567 + if (aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH && 1.568 + aParticularManager->CurrentPriority() < 1.569 + PROCESS_PRIORITY_FOREGROUND_HIGH) { 1.570 + 1.571 + return; 1.572 + } 1.573 + 1.574 + if (aParticularManager->CurrentPriority() >= 1.575 + PROCESS_PRIORITY_FOREGROUND_HIGH) { 1.576 + mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID()); 1.577 + } else { 1.578 + mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID()); 1.579 + } 1.580 + 1.581 + nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms; 1.582 + mParticularManagers.EnumerateRead( 1.583 + &EnumerateParticularProcessPriorityManagers, 1.584 + &pppms); 1.585 + 1.586 + for (uint32_t i = 0; i < pppms.Length(); i++) { 1.587 + if (pppms[i] != aParticularManager) { 1.588 + pppms[i]->ResetCPUPriorityNow(); 1.589 + } 1.590 + } 1.591 +} 1.592 + 1.593 +NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager, 1.594 + nsIObserver, 1.595 + nsITimerCallback, 1.596 + nsISupportsWeakReference); 1.597 + 1.598 +ParticularProcessPriorityManager::ParticularProcessPriorityManager( 1.599 + ContentParent* aContentParent) 1.600 + : mContentParent(aContentParent) 1.601 + , mChildID(aContentParent->ChildID()) 1.602 + , mPriority(PROCESS_PRIORITY_UNKNOWN) 1.603 + , mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL) 1.604 + , mHoldsCPUWakeLock(false) 1.605 + , mHoldsHighPriorityWakeLock(false) 1.606 +{ 1.607 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.608 + LOGP("Creating ParticularProcessPriorityManager."); 1.609 +} 1.610 + 1.611 +void 1.612 +ParticularProcessPriorityManager::Init() 1.613 +{ 1.614 + RegisterWakeLockObserver(this); 1.615 + 1.616 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.617 + if (os) { 1.618 + os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true); 1.619 + os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true); 1.620 + os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true); 1.621 + os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true); 1.622 + } 1.623 + 1.624 + // This process may already hold the CPU lock; for example, our parent may 1.625 + // have acquired it on our behalf. 1.626 + WakeLockInformation info1, info2; 1.627 + GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1); 1.628 + mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID()); 1.629 + 1.630 + GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2); 1.631 + mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID()); 1.632 + LOGP("Done starting up. mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d", 1.633 + mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock); 1.634 +} 1.635 + 1.636 +ParticularProcessPriorityManager::~ParticularProcessPriorityManager() 1.637 +{ 1.638 + LOGP("Destroying ParticularProcessPriorityManager."); 1.639 + 1.640 + // Unregister our wake lock observer if ShutDown hasn't been called. (The 1.641 + // wake lock observer takes raw refs, so we don't want to take chances here!) 1.642 + // We don't call UnregisterWakeLockObserver unconditionally because the code 1.643 + // will print a warning if it's called unnecessarily. 1.644 + 1.645 + if (mContentParent) { 1.646 + UnregisterWakeLockObserver(this); 1.647 + } 1.648 +} 1.649 + 1.650 +/* virtual */ void 1.651 +ParticularProcessPriorityManager::Notify(const WakeLockInformation& aInfo) 1.652 +{ 1.653 + if (!mContentParent) { 1.654 + // We've been shut down. 1.655 + return; 1.656 + } 1.657 + 1.658 + bool* dest = nullptr; 1.659 + if (aInfo.topic().EqualsLiteral("cpu")) { 1.660 + dest = &mHoldsCPUWakeLock; 1.661 + } else if (aInfo.topic().EqualsLiteral("high-priority")) { 1.662 + dest = &mHoldsHighPriorityWakeLock; 1.663 + } 1.664 + 1.665 + if (dest) { 1.666 + bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID()); 1.667 + if (thisProcessLocks != *dest) { 1.668 + *dest = thisProcessLocks; 1.669 + LOGP("Got wake lock changed event. " 1.670 + "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d", 1.671 + mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock); 1.672 + ResetPriority(); 1.673 + } 1.674 + } 1.675 +} 1.676 + 1.677 +NS_IMETHODIMP 1.678 +ParticularProcessPriorityManager::Observe(nsISupports* aSubject, 1.679 + const char* aTopic, 1.680 + const char16_t* aData) 1.681 +{ 1.682 + if (!mContentParent) { 1.683 + // We've been shut down. 1.684 + return NS_OK; 1.685 + } 1.686 + 1.687 + nsDependentCString topic(aTopic); 1.688 + 1.689 + if (topic.EqualsLiteral("audio-channel-process-changed")) { 1.690 + OnAudioChannelProcessChanged(aSubject); 1.691 + } else if (topic.EqualsLiteral("remote-browser-shown")) { 1.692 + OnRemoteBrowserFrameShown(aSubject); 1.693 + } else if (topic.EqualsLiteral("ipc:browser-destroyed")) { 1.694 + OnTabParentDestroyed(aSubject); 1.695 + } else if (topic.EqualsLiteral("frameloader-visible-changed")) { 1.696 + OnFrameloaderVisibleChanged(aSubject); 1.697 + } else { 1.698 + MOZ_ASSERT(false); 1.699 + } 1.700 + 1.701 + return NS_OK; 1.702 +} 1.703 + 1.704 +uint64_t 1.705 +ParticularProcessPriorityManager::ChildID() const 1.706 +{ 1.707 + // We have to cache mContentParent->ChildID() instead of getting it from the 1.708 + // ContentParent each time because after ShutDown() is called, mContentParent 1.709 + // is null. If we didn't cache ChildID(), then we wouldn't be able to run 1.710 + // LOGP() after ShutDown(). 1.711 + return mChildID; 1.712 +} 1.713 + 1.714 +int32_t 1.715 +ParticularProcessPriorityManager::Pid() const 1.716 +{ 1.717 + return mContentParent ? mContentParent->Pid() : -1; 1.718 +} 1.719 + 1.720 +bool 1.721 +ParticularProcessPriorityManager::IsPreallocated() const 1.722 +{ 1.723 + return mContentParent ? mContentParent->IsPreallocated() : false; 1.724 +} 1.725 + 1.726 +const nsAutoCString& 1.727 +ParticularProcessPriorityManager::NameWithComma() 1.728 +{ 1.729 + mNameWithComma.Truncate(); 1.730 + if (!mContentParent) { 1.731 + return mNameWithComma; // empty string 1.732 + } 1.733 + 1.734 + nsAutoString name; 1.735 + mContentParent->FriendlyName(name); 1.736 + if (name.IsEmpty()) { 1.737 + return mNameWithComma; // empty string 1.738 + } 1.739 + 1.740 + mNameWithComma = NS_ConvertUTF16toUTF8(name); 1.741 + mNameWithComma.AppendLiteral(", "); 1.742 + return mNameWithComma; 1.743 +} 1.744 + 1.745 +void 1.746 +ParticularProcessPriorityManager::OnAudioChannelProcessChanged(nsISupports* aSubject) 1.747 +{ 1.748 + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 1.749 + NS_ENSURE_TRUE_VOID(props); 1.750 + 1.751 + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; 1.752 + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); 1.753 + if (childID == ChildID()) { 1.754 + ResetPriority(); 1.755 + } 1.756 +} 1.757 + 1.758 +void 1.759 +ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject) 1.760 +{ 1.761 + nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); 1.762 + NS_ENSURE_TRUE_VOID(fl); 1.763 + 1.764 + // Ignore notifications that aren't from a BrowserOrApp 1.765 + bool isBrowserOrApp; 1.766 + fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp); 1.767 + if (!isBrowserOrApp) { 1.768 + return; 1.769 + } 1.770 + 1.771 + nsCOMPtr<nsITabParent> tp; 1.772 + fl->GetTabParent(getter_AddRefs(tp)); 1.773 + NS_ENSURE_TRUE_VOID(tp); 1.774 + 1.775 + if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) { 1.776 + return; 1.777 + } 1.778 + 1.779 + ResetPriority(); 1.780 +} 1.781 + 1.782 +void 1.783 +ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject) 1.784 +{ 1.785 + nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject); 1.786 + NS_ENSURE_TRUE_VOID(tp); 1.787 + 1.788 + if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) { 1.789 + return; 1.790 + } 1.791 + 1.792 + ResetPriority(); 1.793 +} 1.794 + 1.795 +void 1.796 +ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubject) 1.797 +{ 1.798 + nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); 1.799 + NS_ENSURE_TRUE_VOID(fl); 1.800 + 1.801 + nsCOMPtr<nsITabParent> tp; 1.802 + fl->GetTabParent(getter_AddRefs(tp)); 1.803 + if (!tp) { 1.804 + return; 1.805 + } 1.806 + 1.807 + if (static_cast<TabParent*>(tp.get())->Manager() != mContentParent) { 1.808 + return; 1.809 + } 1.810 + 1.811 + // Most of the time when something changes in a process we call 1.812 + // ResetPriority(), giving a grace period before downgrading its priority. 1.813 + // But notice that here don't give a grace period: We call ResetPriorityNow() 1.814 + // instead. 1.815 + // 1.816 + // We do this because we're reacting here to a setVisibility() call, which is 1.817 + // an explicit signal from the process embedder that we should re-prioritize 1.818 + // a process. If we gave a grace period in response to setVisibility() 1.819 + // calls, it would be impossible for the embedder to explicitly prioritize 1.820 + // processes and prevent e.g. the case where we switch which process is in 1.821 + // the foreground and, during the old fg processs's grace period, it OOMs the 1.822 + // new fg process. 1.823 + 1.824 + ResetPriorityNow(); 1.825 +} 1.826 + 1.827 +void 1.828 +ParticularProcessPriorityManager::ResetPriority() 1.829 +{ 1.830 + ProcessPriority processPriority = ComputePriority(); 1.831 + if (mPriority == PROCESS_PRIORITY_UNKNOWN || 1.832 + mPriority > processPriority) { 1.833 + // Apps set at a perceivable background priority are often playing media. 1.834 + // Most media will have short gaps while changing tracks between songs, 1.835 + // switching videos, etc. Give these apps a longer grace period so they 1.836 + // can get their next track started, if there is one, before getting 1.837 + // downgraded. 1.838 + if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) { 1.839 + ScheduleResetPriority("backgroundPerceivableGracePeriodMS"); 1.840 + } else { 1.841 + ScheduleResetPriority("backgroundGracePeriodMS"); 1.842 + } 1.843 + return; 1.844 + } 1.845 + 1.846 + SetPriorityNow(processPriority); 1.847 +} 1.848 + 1.849 +void 1.850 +ParticularProcessPriorityManager::ResetPriorityNow() 1.851 +{ 1.852 + SetPriorityNow(ComputePriority()); 1.853 +} 1.854 + 1.855 +void 1.856 +ParticularProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref) 1.857 +{ 1.858 + if (mResetPriorityTimer) { 1.859 + LOGP("ScheduleResetPriority bailing; the timer is already running."); 1.860 + return; 1.861 + } 1.862 + 1.863 + uint32_t timeout = Preferences::GetUint( 1.864 + nsPrintfCString("dom.ipc.processPriorityManager.%s", aTimeoutPref).get()); 1.865 + LOGP("Scheduling reset timer to fire in %dms.", timeout); 1.866 + mResetPriorityTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.867 + mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); 1.868 +} 1.869 + 1.870 + 1.871 +NS_IMETHODIMP 1.872 +ParticularProcessPriorityManager::Notify(nsITimer* aTimer) 1.873 +{ 1.874 + LOGP("Reset priority timer callback; about to ResetPriorityNow."); 1.875 + ResetPriorityNow(); 1.876 + mResetPriorityTimer = nullptr; 1.877 + return NS_OK; 1.878 +} 1.879 + 1.880 +bool 1.881 +ParticularProcessPriorityManager::HasAppType(const char* aAppType) 1.882 +{ 1.883 + const InfallibleTArray<PBrowserParent*>& browsers = 1.884 + mContentParent->ManagedPBrowserParent(); 1.885 + for (uint32_t i = 0; i < browsers.Length(); i++) { 1.886 + nsAutoString appType; 1.887 + static_cast<TabParent*>(browsers[i])->GetAppType(appType); 1.888 + if (appType.EqualsASCII(aAppType)) { 1.889 + return true; 1.890 + } 1.891 + } 1.892 + 1.893 + return false; 1.894 +} 1.895 + 1.896 +bool 1.897 +ParticularProcessPriorityManager::IsExpectingSystemMessage() 1.898 +{ 1.899 + const InfallibleTArray<PBrowserParent*>& browsers = 1.900 + mContentParent->ManagedPBrowserParent(); 1.901 + for (uint32_t i = 0; i < browsers.Length(); i++) { 1.902 + TabParent* tp = static_cast<TabParent*>(browsers[i]); 1.903 + nsCOMPtr<nsIMozBrowserFrame> bf = do_QueryInterface(tp->GetOwnerElement()); 1.904 + if (!bf) { 1.905 + continue; 1.906 + } 1.907 + 1.908 + if (bf->GetIsExpectingSystemMessage()) { 1.909 + return true; 1.910 + } 1.911 + } 1.912 + 1.913 + return false; 1.914 +} 1.915 + 1.916 +ProcessPriority 1.917 +ParticularProcessPriorityManager::CurrentPriority() 1.918 +{ 1.919 + return mPriority; 1.920 +} 1.921 + 1.922 +ProcessPriority 1.923 +ParticularProcessPriorityManager::ComputePriority() 1.924 +{ 1.925 + if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) && 1.926 + HasAppType("critical")) { 1.927 + return PROCESS_PRIORITY_FOREGROUND_HIGH; 1.928 + } 1.929 + 1.930 + bool isVisible = false; 1.931 + const InfallibleTArray<PBrowserParent*>& browsers = 1.932 + mContentParent->ManagedPBrowserParent(); 1.933 + for (uint32_t i = 0; i < browsers.Length(); i++) { 1.934 + if (static_cast<TabParent*>(browsers[i])->IsVisible()) { 1.935 + isVisible = true; 1.936 + break; 1.937 + } 1.938 + } 1.939 + 1.940 + if (isVisible) { 1.941 + return HasAppType("keyboard") ? 1.942 + PROCESS_PRIORITY_FOREGROUND_KEYBOARD : 1.943 + PROCESS_PRIORITY_FOREGROUND; 1.944 + } 1.945 + 1.946 + if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) && 1.947 + IsExpectingSystemMessage()) { 1.948 + return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE; 1.949 + } 1.950 + 1.951 + AudioChannelService* service = AudioChannelService::GetAudioChannelService(); 1.952 + if (service->ProcessContentOrNormalChannelIsActive(ChildID())) { 1.953 + return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE; 1.954 + } 1.955 + 1.956 + return HasAppType("homescreen") ? 1.957 + PROCESS_PRIORITY_BACKGROUND_HOMESCREEN : 1.958 + PROCESS_PRIORITY_BACKGROUND; 1.959 +} 1.960 + 1.961 +ProcessCPUPriority 1.962 +ParticularProcessPriorityManager::ComputeCPUPriority(ProcessPriority aPriority) 1.963 +{ 1.964 + if (aPriority == PROCESS_PRIORITY_PREALLOC) { 1.965 + return PROCESS_CPU_PRIORITY_LOW; 1.966 + } 1.967 + 1.968 + if (aPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) { 1.969 + return PROCESS_CPU_PRIORITY_NORMAL; 1.970 + } 1.971 + 1.972 + return ProcessPriorityManagerImpl::GetSingleton()-> 1.973 + OtherProcessHasHighPriority(this) ? 1.974 + PROCESS_CPU_PRIORITY_LOW : 1.975 + PROCESS_CPU_PRIORITY_NORMAL; 1.976 +} 1.977 + 1.978 +void 1.979 +ParticularProcessPriorityManager::ResetCPUPriorityNow() 1.980 +{ 1.981 + SetPriorityNow(mPriority); 1.982 +} 1.983 + 1.984 +void 1.985 +ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority, 1.986 + uint32_t aBackgroundLRU) 1.987 +{ 1.988 + SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU); 1.989 +} 1.990 + 1.991 +void 1.992 +ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority, 1.993 + ProcessCPUPriority aCPUPriority, 1.994 + uint32_t aBackgroundLRU) 1.995 +{ 1.996 + if (aPriority == PROCESS_PRIORITY_UNKNOWN) { 1.997 + MOZ_ASSERT(false); 1.998 + return; 1.999 + } 1.1000 + 1.1001 +#ifdef MOZ_NUWA_PROCESS 1.1002 + // Do not attempt to change the priority of the Nuwa process 1.1003 + if (mContentParent->IsNuwaProcess()) { 1.1004 + return; 1.1005 + } 1.1006 +#endif 1.1007 + 1.1008 + if (aBackgroundLRU > 0 && 1.1009 + aPriority == PROCESS_PRIORITY_BACKGROUND && 1.1010 + mPriority == PROCESS_PRIORITY_BACKGROUND) { 1.1011 + hal::SetProcessPriority(Pid(), mPriority, mCPUPriority, aBackgroundLRU); 1.1012 + 1.1013 + nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d", 1.1014 + ProcessPriorityToString(mPriority, mCPUPriority), 1.1015 + aBackgroundLRU); 1.1016 + 1.1017 + FireTestOnlyObserverNotification("process-priority-with-background-LRU-set", 1.1018 + ProcessPriorityWithBackgroundLRU.get()); 1.1019 + } 1.1020 + 1.1021 + if (!mContentParent || 1.1022 + !ProcessPriorityManagerImpl::PrefsEnabled() || 1.1023 + (mPriority == aPriority && mCPUPriority == aCPUPriority)) { 1.1024 + return; 1.1025 + } 1.1026 + 1.1027 + // If the prefs were disabled after this ParticularProcessPriorityManager was 1.1028 + // created, we can at least avoid any further calls to 1.1029 + // hal::SetProcessPriority. Supporting dynamic enabling/disabling of the 1.1030 + // ProcessPriorityManager is mostly for testing. 1.1031 + if (!ProcessPriorityManagerImpl::PrefsEnabled()) { 1.1032 + return; 1.1033 + } 1.1034 + 1.1035 + if (aPriority == PROCESS_PRIORITY_BACKGROUND && 1.1036 + mPriority != PROCESS_PRIORITY_BACKGROUND && 1.1037 + !IsPreallocated()) { 1.1038 + ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent); 1.1039 + } 1.1040 + 1.1041 + if (aPriority != PROCESS_PRIORITY_BACKGROUND && 1.1042 + mPriority == PROCESS_PRIORITY_BACKGROUND && 1.1043 + !IsPreallocated()) { 1.1044 + ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent); 1.1045 + } 1.1046 + 1.1047 + LOGP("Changing priority from %s to %s.", 1.1048 + ProcessPriorityToString(mPriority, mCPUPriority), 1.1049 + ProcessPriorityToString(aPriority, aCPUPriority)); 1.1050 + 1.1051 + ProcessPriority oldPriority = mPriority; 1.1052 + 1.1053 + mPriority = aPriority; 1.1054 + mCPUPriority = aCPUPriority; 1.1055 + hal::SetProcessPriority(Pid(), mPriority, mCPUPriority); 1.1056 + 1.1057 + if (oldPriority != mPriority) { 1.1058 + unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority); 1.1059 + } 1.1060 + 1.1061 + if (aPriority < PROCESS_PRIORITY_FOREGROUND) { 1.1062 + unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("low-memory")); 1.1063 + } 1.1064 + 1.1065 + FireTestOnlyObserverNotification("process-priority-set", 1.1066 + ProcessPriorityToString(mPriority, mCPUPriority)); 1.1067 + 1.1068 + if (oldPriority != mPriority) { 1.1069 + ProcessPriorityManagerImpl::GetSingleton()-> 1.1070 + NotifyProcessPriorityChanged(this, oldPriority); 1.1071 + } 1.1072 +} 1.1073 + 1.1074 +void 1.1075 +ParticularProcessPriorityManager::ShutDown() 1.1076 +{ 1.1077 + MOZ_ASSERT(mContentParent); 1.1078 + 1.1079 + UnregisterWakeLockObserver(this); 1.1080 + 1.1081 + if (mResetPriorityTimer) { 1.1082 + mResetPriorityTimer->Cancel(); 1.1083 + mResetPriorityTimer = nullptr; 1.1084 + } 1.1085 + 1.1086 + if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) { 1.1087 + ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent); 1.1088 + } 1.1089 + 1.1090 + mContentParent = nullptr; 1.1091 +} 1.1092 + 1.1093 +void 1.1094 +ProcessPriorityManagerImpl::FireTestOnlyObserverNotification( 1.1095 + const char* aTopic, 1.1096 + const nsACString& aData /* = EmptyCString() */) 1.1097 +{ 1.1098 + if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { 1.1099 + return; 1.1100 + } 1.1101 + 1.1102 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.1103 + NS_ENSURE_TRUE_VOID(os); 1.1104 + 1.1105 + nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic); 1.1106 + 1.1107 + LOG("Notifying observer %s, data %s", 1.1108 + topic.get(), PromiseFlatCString(aData).get()); 1.1109 + os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get()); 1.1110 +} 1.1111 + 1.1112 +void 1.1113 +ParticularProcessPriorityManager::FireTestOnlyObserverNotification( 1.1114 + const char* aTopic, 1.1115 + const char* aData /* = nullptr */ ) 1.1116 +{ 1.1117 + if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { 1.1118 + return; 1.1119 + } 1.1120 + 1.1121 + nsAutoCString data; 1.1122 + if (aData) { 1.1123 + data.AppendASCII(aData); 1.1124 + } 1.1125 + 1.1126 + FireTestOnlyObserverNotification(aTopic, data); 1.1127 +} 1.1128 + 1.1129 +void 1.1130 +ParticularProcessPriorityManager::FireTestOnlyObserverNotification( 1.1131 + const char* aTopic, 1.1132 + const nsACString& aData /* = EmptyCString() */) 1.1133 +{ 1.1134 + if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { 1.1135 + return; 1.1136 + } 1.1137 + 1.1138 + nsAutoCString data(nsPrintfCString("%lld", ChildID())); 1.1139 + if (!aData.IsEmpty()) { 1.1140 + data.AppendLiteral(":"); 1.1141 + data.Append(aData); 1.1142 + } 1.1143 + 1.1144 + // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return 1.1145 + // null, since ProcessPriorityManagerImpl is the only class which creates 1.1146 + // ParticularProcessPriorityManagers. 1.1147 + 1.1148 + ProcessPriorityManagerImpl::GetSingleton()-> 1.1149 + FireTestOnlyObserverNotification(aTopic, data); 1.1150 +} 1.1151 + 1.1152 +StaticRefPtr<ProcessPriorityManagerChild> 1.1153 +ProcessPriorityManagerChild::sSingleton; 1.1154 + 1.1155 +/* static */ void 1.1156 +ProcessPriorityManagerChild::StaticInit() 1.1157 +{ 1.1158 + if (!sSingleton) { 1.1159 + sSingleton = new ProcessPriorityManagerChild(); 1.1160 + sSingleton->Init(); 1.1161 + ClearOnShutdown(&sSingleton); 1.1162 + } 1.1163 +} 1.1164 + 1.1165 +/* static */ ProcessPriorityManagerChild* 1.1166 +ProcessPriorityManagerChild::Singleton() 1.1167 +{ 1.1168 + StaticInit(); 1.1169 + return sSingleton; 1.1170 +} 1.1171 + 1.1172 +NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild, 1.1173 + nsIObserver) 1.1174 + 1.1175 +ProcessPriorityManagerChild::ProcessPriorityManagerChild() 1.1176 +{ 1.1177 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.1178 + mCachedPriority = PROCESS_PRIORITY_MASTER; 1.1179 + } else { 1.1180 + mCachedPriority = PROCESS_PRIORITY_UNKNOWN; 1.1181 + } 1.1182 +} 1.1183 + 1.1184 +void 1.1185 +ProcessPriorityManagerChild::Init() 1.1186 +{ 1.1187 + // The process priority should only be changed in child processes; don't even 1.1188 + // bother listening for changes if we're in the main process. 1.1189 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.1190 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.1191 + NS_ENSURE_TRUE_VOID(os); 1.1192 + os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false); 1.1193 + } 1.1194 +} 1.1195 + 1.1196 +NS_IMETHODIMP 1.1197 +ProcessPriorityManagerChild::Observe( 1.1198 + nsISupports* aSubject, 1.1199 + const char* aTopic, 1.1200 + const char16_t* aData) 1.1201 +{ 1.1202 + MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed")); 1.1203 + 1.1204 + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 1.1205 + NS_ENSURE_TRUE(props, NS_OK); 1.1206 + 1.1207 + int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN); 1.1208 + props->GetPropertyAsInt32(NS_LITERAL_STRING("priority"), &priority); 1.1209 + NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK); 1.1210 + 1.1211 + mCachedPriority = static_cast<ProcessPriority>(priority); 1.1212 + 1.1213 + return NS_OK; 1.1214 +} 1.1215 + 1.1216 +bool 1.1217 +ProcessPriorityManagerChild::CurrentProcessIsForeground() 1.1218 +{ 1.1219 + return mCachedPriority == PROCESS_PRIORITY_UNKNOWN || 1.1220 + mCachedPriority >= PROCESS_PRIORITY_FOREGROUND; 1.1221 +} 1.1222 + 1.1223 +bool 1.1224 +ProcessPriorityManagerChild::CurrentProcessIsHighPriority() 1.1225 +{ 1.1226 + return mCachedPriority == PROCESS_PRIORITY_UNKNOWN || 1.1227 + mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH; 1.1228 +} 1.1229 + 1.1230 +/* static */ StaticAutoPtr<BackgroundProcessLRUPool> 1.1231 +BackgroundProcessLRUPool::sSingleton; 1.1232 + 1.1233 +/* static */ BackgroundProcessLRUPool* 1.1234 +BackgroundProcessLRUPool::Singleton() 1.1235 +{ 1.1236 + if (!sSingleton) { 1.1237 + sSingleton = new BackgroundProcessLRUPool(); 1.1238 + ClearOnShutdown(&sSingleton); 1.1239 + } 1.1240 + return sSingleton; 1.1241 +} 1.1242 + 1.1243 +BackgroundProcessLRUPool::BackgroundProcessLRUPool() 1.1244 +{ 1.1245 + EnsureLRUPool(); 1.1246 +} 1.1247 + 1.1248 +uint32_t 1.1249 +BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex) 1.1250 +{ 1.1251 + // Set LRU level of each background process and maintain LRU buffer as below: 1.1252 + 1.1253 + // Priority background : LRU0 1.1254 + // Priority background+1: LRU1, LRU2 1.1255 + // Priority background+2: LRU3, LRU4, LRU5, LRU6 1.1256 + // Priority background+3: LRU7, LRU8, LRU9, LRU10, LRU11, LRU12, LRU13, LRU14 1.1257 + // ... 1.1258 + // Priority background+L-1: 2^(number of background LRU pool levels - 1) 1.1259 + // (End of buffer) 1.1260 + 1.1261 + return (uint32_t)(log((float)aBackgroundLRUPoolIndex) / log(2.0)); 1.1262 +} 1.1263 + 1.1264 +void 1.1265 +BackgroundProcessLRUPool::EnsureLRUPool() 1.1266 +{ 1.1267 + // We set mBackgroundLRUPoolLevels according to our pref. 1.1268 + // This value is used to set background process LRU pool 1.1269 + if (!NS_SUCCEEDED(Preferences::GetInt( 1.1270 + "dom.ipc.processPriorityManager.backgroundLRUPoolLevels", 1.1271 + &mLRUPoolLevels))) { 1.1272 + mLRUPoolLevels = 1; 1.1273 + } 1.1274 + 1.1275 + if (mLRUPoolLevels <= 0) { 1.1276 + MOZ_CRASH(); 1.1277 + } 1.1278 + 1.1279 + // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines 1.1280 + // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10. 1.1281 + // This means we can only have at most (15 -10 + 1) = 6 background LRU levels. 1.1282 + // See bug 822325 comment 49 1.1283 + MOZ_ASSERT(mLRUPoolLevels <= 6); 1.1284 + 1.1285 + // LRU pool size = 2 ^ (number of background LRU pool levels) - 1 1.1286 + mLRUPoolSize = (1 << mLRUPoolLevels) - 1; 1.1287 + 1.1288 + mLRUPoolAvailableIndex = 0; 1.1289 + 1.1290 + LOG("Making background LRU pool with size(%d)", mLRUPoolSize); 1.1291 + 1.1292 + mLRUPool.InsertElementsAt(0, mLRUPoolSize, (ContentParent*)nullptr); 1.1293 +} 1.1294 + 1.1295 +void 1.1296 +BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool( 1.1297 + ContentParent* aContentParent) 1.1298 +{ 1.1299 + for (int32_t i = 0; i < mLRUPoolSize; i++) { 1.1300 + if (mLRUPool[i]) { 1.1301 + if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) { 1.1302 + 1.1303 + mLRUPool[i] = nullptr; 1.1304 + LOG("Remove ChildID(%llu) from LRU pool", aContentParent->ChildID()); 1.1305 + 1.1306 + // After we remove this ContentParent from LRU pool, we still need to 1.1307 + // update the available index if the index of removed one is less than 1.1308 + // the available index we already have. 1.1309 + UpdateAvailableIndexInLRUPool(aContentParent, i); 1.1310 + break; 1.1311 + } 1.1312 + } 1.1313 + } 1.1314 +} 1.1315 + 1.1316 +nsresult 1.1317 +BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool( 1.1318 + ContentParent* aContentParent, 1.1319 + int32_t aTargetIndex) 1.1320 +{ 1.1321 + // If we specify which index we want to assign to mLRUPoolAvailableIndex, 1.1322 + // We have to make sure the index in LRUPool doesn't point to any 1.1323 + // ContentParent. 1.1324 + if (aTargetIndex >= 0 && aTargetIndex < mLRUPoolSize && 1.1325 + aTargetIndex < mLRUPoolAvailableIndex && 1.1326 + !mLRUPool[aTargetIndex]) { 1.1327 + mLRUPoolAvailableIndex = aTargetIndex; 1.1328 + return NS_OK; 1.1329 + } 1.1330 + 1.1331 + // When we didn't specify any legal aTargetIndex, then we just check 1.1332 + // whether current mLRUPoolAvailableIndex points to any ContentParent or not. 1.1333 + if (mLRUPoolAvailableIndex >= 0 && mLRUPoolAvailableIndex < mLRUPoolSize && 1.1334 + !(mLRUPool[mLRUPoolAvailableIndex])) { 1.1335 + return NS_OK; 1.1336 + } 1.1337 + 1.1338 + // Both above way failed. So now we have to find proper value 1.1339 + // for mLRUPoolAvailableIndex. 1.1340 + // We are looking for an available index. We only shift process with 1.1341 + // LRU less than the available index should have, so we stop update 1.1342 + // mLRUPoolAvailableIndex from the for loop once we got a candidate. 1.1343 + mLRUPoolAvailableIndex = -1; 1.1344 + 1.1345 + for (int32_t i = 0; i < mLRUPoolSize; i++) { 1.1346 + if (mLRUPool[i]) { 1.1347 + if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) { 1.1348 + LOG("ChildID(%llu) already in LRU pool", aContentParent->ChildID()); 1.1349 + MOZ_ASSERT(false); 1.1350 + return NS_ERROR_UNEXPECTED; 1.1351 + } 1.1352 + continue; 1.1353 + } else { 1.1354 + if (mLRUPoolAvailableIndex == -1) { 1.1355 + mLRUPoolAvailableIndex = i; 1.1356 + } 1.1357 + } 1.1358 + } 1.1359 + 1.1360 + // If the LRUPool is already full, mLRUPoolAvailableIndex is still -1 after 1.1361 + // above loop finished. We should set mLRUPoolAvailableIndex 1.1362 + // to mLRUPoolSize - 1 in this case. Here uses the mod operator to do it: 1.1363 + // New mLRUPoolAvailableIndex either equals old mLRUPoolAvailableIndex, or 1.1364 + // mLRUPoolSize - 1 if old mLRUPoolAvailableIndex is -1. 1.1365 + mLRUPoolAvailableIndex = 1.1366 + (mLRUPoolAvailableIndex + mLRUPoolSize) % mLRUPoolSize; 1.1367 + 1.1368 + return NS_OK; 1.1369 +} 1.1370 + 1.1371 +void 1.1372 +BackgroundProcessLRUPool::ShiftLRUPool() 1.1373 +{ 1.1374 + for (int32_t i = mLRUPoolAvailableIndex; i > 0; i--) { 1.1375 + mLRUPool[i] = mLRUPool[i - 1]; 1.1376 + // Check whether i+1 is power of Two. 1.1377 + // If so, then it crossed a LRU group boundary and 1.1378 + // we need to assign its new process priority LRU. 1.1379 + if (!((i + 1) & i)) { 1.1380 + ProcessPriorityManagerImpl::GetSingleton()->SetProcessPriority( 1.1381 + mLRUPool[i], PROCESS_PRIORITY_BACKGROUND, CalculateLRULevel(i + 1)); 1.1382 + } 1.1383 + } 1.1384 +} 1.1385 + 1.1386 +void 1.1387 +BackgroundProcessLRUPool::AddIntoBackgroundLRUPool( 1.1388 + ContentParent* aContentParent) 1.1389 +{ 1.1390 + // We have to make sure that we have correct available index in LRU pool 1.1391 + if (!NS_SUCCEEDED( 1.1392 + UpdateAvailableIndexInLRUPool(aContentParent))) { 1.1393 + return; 1.1394 + } 1.1395 + 1.1396 + // Shift the list in the pool, so we have room at index 0 for the newly added 1.1397 + // ContentParent 1.1398 + ShiftLRUPool(); 1.1399 + 1.1400 + mLRUPool[0] = aContentParent; 1.1401 + 1.1402 + LOG("Add ChildID(%llu) into LRU pool", aContentParent->ChildID()); 1.1403 +} 1.1404 + 1.1405 +} // anonymous namespace 1.1406 + 1.1407 +namespace mozilla { 1.1408 + 1.1409 +/* static */ void 1.1410 +ProcessPriorityManager::Init() 1.1411 +{ 1.1412 + ProcessPriorityManagerImpl::StaticInit(); 1.1413 + ProcessPriorityManagerChild::StaticInit(); 1.1414 +} 1.1415 + 1.1416 +/* static */ void 1.1417 +ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent, 1.1418 + ProcessPriority aPriority) 1.1419 +{ 1.1420 + MOZ_ASSERT(aContentParent); 1.1421 + 1.1422 + ProcessPriorityManagerImpl* singleton = 1.1423 + ProcessPriorityManagerImpl::GetSingleton(); 1.1424 + if (singleton) { 1.1425 + singleton->SetProcessPriority(aContentParent, aPriority); 1.1426 + } 1.1427 +} 1.1428 + 1.1429 +/* static */ void 1.1430 +ProcessPriorityManager::RemoveFromBackgroundLRUPool( 1.1431 + ContentParent* aContentParent) 1.1432 +{ 1.1433 + MOZ_ASSERT(aContentParent); 1.1434 + 1.1435 + BackgroundProcessLRUPool* singleton = 1.1436 + BackgroundProcessLRUPool::Singleton(); 1.1437 + if (singleton) { 1.1438 + singleton->RemoveFromBackgroundLRUPool(aContentParent); 1.1439 + } 1.1440 +} 1.1441 + 1.1442 +/* static */ void 1.1443 +ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent) 1.1444 +{ 1.1445 + MOZ_ASSERT(aContentParent); 1.1446 + 1.1447 + BackgroundProcessLRUPool* singleton = 1.1448 + BackgroundProcessLRUPool::Singleton(); 1.1449 + if (singleton) { 1.1450 + singleton->AddIntoBackgroundLRUPool(aContentParent); 1.1451 + } 1.1452 +} 1.1453 + 1.1454 +/* static */ bool 1.1455 +ProcessPriorityManager::CurrentProcessIsForeground() 1.1456 +{ 1.1457 + return ProcessPriorityManagerChild::Singleton()-> 1.1458 + CurrentProcessIsForeground(); 1.1459 +} 1.1460 + 1.1461 +/* static */ bool 1.1462 +ProcessPriorityManager::AnyProcessHasHighPriority() 1.1463 +{ 1.1464 + ProcessPriorityManagerImpl* singleton = 1.1465 + ProcessPriorityManagerImpl::GetSingleton(); 1.1466 + 1.1467 + if (singleton) { 1.1468 + return singleton->ChildProcessHasHighPriority(); 1.1469 + } else { 1.1470 + return ProcessPriorityManagerChild::Singleton()-> 1.1471 + CurrentProcessIsHighPriority(); 1.1472 + } 1.1473 +} 1.1474 + 1.1475 +} // namespace mozilla