ProcessPriorityManager.cpp 42 KB

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