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