|
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 "mozilla/PreallocatedProcessManager.h" |
|
8 #include "mozilla/ClearOnShutdown.h" |
|
9 #include "mozilla/Preferences.h" |
|
10 #include "mozilla/dom/ContentParent.h" |
|
11 #include "nsIPropertyBag2.h" |
|
12 #include "ProcessPriorityManager.h" |
|
13 #include "nsServiceManagerUtils.h" |
|
14 #include "nsCxPusher.h" |
|
15 |
|
16 #ifdef MOZ_NUWA_PROCESS |
|
17 #include "ipc/Nuwa.h" |
|
18 #endif |
|
19 |
|
20 // This number is fairly arbitrary ... the intention is to put off |
|
21 // launching another app process until the last one has finished |
|
22 // loading its content, to reduce CPU/memory/IO contention. |
|
23 #define DEFAULT_ALLOCATE_DELAY 1000 |
|
24 #define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds. |
|
25 |
|
26 using namespace mozilla; |
|
27 using namespace mozilla::hal; |
|
28 using namespace mozilla::dom; |
|
29 |
|
30 namespace { |
|
31 |
|
32 /** |
|
33 * This singleton class implements the static methods on |
|
34 * PreallocatedProcessManager. |
|
35 */ |
|
36 class PreallocatedProcessManagerImpl MOZ_FINAL |
|
37 : public nsIObserver |
|
38 { |
|
39 public: |
|
40 static PreallocatedProcessManagerImpl* Singleton(); |
|
41 |
|
42 NS_DECL_ISUPPORTS |
|
43 NS_DECL_NSIOBSERVER |
|
44 |
|
45 // See comments on PreallocatedProcessManager for these methods. |
|
46 void AllocateAfterDelay(); |
|
47 void AllocateOnIdle(); |
|
48 void AllocateNow(); |
|
49 already_AddRefed<ContentParent> Take(); |
|
50 |
|
51 #ifdef MOZ_NUWA_PROCESS |
|
52 public: |
|
53 void ScheduleDelayedNuwaFork(); |
|
54 void DelayedNuwaFork(); |
|
55 void PublishSpareProcess(ContentParent* aContent); |
|
56 void MaybeForgetSpare(ContentParent* aContent); |
|
57 void OnNuwaReady(); |
|
58 bool PreallocatedProcessReady(); |
|
59 already_AddRefed<ContentParent> GetSpareProcess(); |
|
60 void RunAfterPreallocatedProcessReady(nsIRunnable* aRunnable); |
|
61 |
|
62 private: |
|
63 void NuwaFork(); |
|
64 |
|
65 // initialization off the critical path of app startup. |
|
66 CancelableTask* mPreallocateAppProcessTask; |
|
67 |
|
68 // The array containing the preallocated processes. 4 as the inline storage size |
|
69 // should be enough so we don't need to grow the nsAutoTArray. |
|
70 nsAutoTArray<nsRefPtr<ContentParent>, 4> mSpareProcesses; |
|
71 |
|
72 nsTArray<nsCOMPtr<nsIRunnable> > mDelayedContentParentRequests; |
|
73 |
|
74 // Nuwa process is ready for creating new process. |
|
75 bool mIsNuwaReady; |
|
76 #endif |
|
77 |
|
78 private: |
|
79 static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; |
|
80 |
|
81 PreallocatedProcessManagerImpl(); |
|
82 DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl); |
|
83 |
|
84 void Init(); |
|
85 |
|
86 void RereadPrefs(); |
|
87 void Enable(); |
|
88 void Disable(); |
|
89 |
|
90 void ObserveProcessShutdown(nsISupports* aSubject); |
|
91 |
|
92 bool mEnabled; |
|
93 bool mShutdown; |
|
94 nsRefPtr<ContentParent> mPreallocatedAppProcess; |
|
95 }; |
|
96 |
|
97 /* static */ StaticRefPtr<PreallocatedProcessManagerImpl> |
|
98 PreallocatedProcessManagerImpl::sSingleton; |
|
99 |
|
100 /* static */ PreallocatedProcessManagerImpl* |
|
101 PreallocatedProcessManagerImpl::Singleton() |
|
102 { |
|
103 if (!sSingleton) { |
|
104 sSingleton = new PreallocatedProcessManagerImpl(); |
|
105 sSingleton->Init(); |
|
106 ClearOnShutdown(&sSingleton); |
|
107 } |
|
108 |
|
109 return sSingleton; |
|
110 } |
|
111 |
|
112 NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) |
|
113 |
|
114 PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() |
|
115 : mEnabled(false) |
|
116 #ifdef MOZ_NUWA_PROCESS |
|
117 , mPreallocateAppProcessTask(nullptr) |
|
118 , mIsNuwaReady(false) |
|
119 #endif |
|
120 , mShutdown(false) |
|
121 {} |
|
122 |
|
123 void |
|
124 PreallocatedProcessManagerImpl::Init() |
|
125 { |
|
126 Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled"); |
|
127 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); |
|
128 if (os) { |
|
129 os->AddObserver(this, "ipc:content-shutdown", |
|
130 /* weakRef = */ false); |
|
131 os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, |
|
132 /* weakRef = */ false); |
|
133 } |
|
134 RereadPrefs(); |
|
135 } |
|
136 |
|
137 NS_IMETHODIMP |
|
138 PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, |
|
139 const char* aTopic, |
|
140 const char16_t* aData) |
|
141 { |
|
142 if (!strcmp("ipc:content-shutdown", aTopic)) { |
|
143 ObserveProcessShutdown(aSubject); |
|
144 } else if (!strcmp("nsPref:changed", aTopic)) { |
|
145 // The only other observer we registered was for our prefs. |
|
146 RereadPrefs(); |
|
147 } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { |
|
148 mShutdown = true; |
|
149 } else { |
|
150 MOZ_ASSERT(false); |
|
151 } |
|
152 |
|
153 return NS_OK; |
|
154 } |
|
155 |
|
156 void |
|
157 PreallocatedProcessManagerImpl::RereadPrefs() |
|
158 { |
|
159 if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { |
|
160 Enable(); |
|
161 } else { |
|
162 Disable(); |
|
163 } |
|
164 } |
|
165 |
|
166 already_AddRefed<ContentParent> |
|
167 PreallocatedProcessManagerImpl::Take() |
|
168 { |
|
169 return mPreallocatedAppProcess.forget(); |
|
170 } |
|
171 |
|
172 void |
|
173 PreallocatedProcessManagerImpl::Enable() |
|
174 { |
|
175 if (mEnabled) { |
|
176 return; |
|
177 } |
|
178 |
|
179 mEnabled = true; |
|
180 #ifdef MOZ_NUWA_PROCESS |
|
181 ScheduleDelayedNuwaFork(); |
|
182 #else |
|
183 AllocateAfterDelay(); |
|
184 #endif |
|
185 } |
|
186 |
|
187 void |
|
188 PreallocatedProcessManagerImpl::AllocateAfterDelay() |
|
189 { |
|
190 if (!mEnabled || mPreallocatedAppProcess) { |
|
191 return; |
|
192 } |
|
193 |
|
194 MessageLoop::current()->PostDelayedTask( |
|
195 FROM_HERE, |
|
196 NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle), |
|
197 Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", |
|
198 DEFAULT_ALLOCATE_DELAY)); |
|
199 } |
|
200 |
|
201 void |
|
202 PreallocatedProcessManagerImpl::AllocateOnIdle() |
|
203 { |
|
204 if (!mEnabled || mPreallocatedAppProcess) { |
|
205 return; |
|
206 } |
|
207 |
|
208 MessageLoop::current()->PostIdleTask( |
|
209 FROM_HERE, |
|
210 NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); |
|
211 } |
|
212 |
|
213 void |
|
214 PreallocatedProcessManagerImpl::AllocateNow() |
|
215 { |
|
216 if (!mEnabled || mPreallocatedAppProcess) { |
|
217 return; |
|
218 } |
|
219 |
|
220 mPreallocatedAppProcess = ContentParent::PreallocateAppProcess(); |
|
221 } |
|
222 |
|
223 #ifdef MOZ_NUWA_PROCESS |
|
224 |
|
225 void |
|
226 PreallocatedProcessManagerImpl::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest) |
|
227 { |
|
228 MOZ_ASSERT(NS_IsMainThread()); |
|
229 mDelayedContentParentRequests.AppendElement(aRequest); |
|
230 |
|
231 // This is an urgent NuwaFork() request. Request to fork at once. |
|
232 DelayedNuwaFork(); |
|
233 } |
|
234 |
|
235 void |
|
236 PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork() |
|
237 { |
|
238 MOZ_ASSERT(NS_IsMainThread()); |
|
239 |
|
240 if (mPreallocateAppProcessTask) { |
|
241 // Make sure there is only one request running. |
|
242 return; |
|
243 } |
|
244 |
|
245 mPreallocateAppProcessTask = NewRunnableMethod( |
|
246 this, &PreallocatedProcessManagerImpl::DelayedNuwaFork); |
|
247 MessageLoop::current()->PostDelayedTask( |
|
248 FROM_HERE, mPreallocateAppProcessTask, |
|
249 Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", |
|
250 DEFAULT_ALLOCATE_DELAY)); |
|
251 } |
|
252 |
|
253 void |
|
254 PreallocatedProcessManagerImpl::DelayedNuwaFork() |
|
255 { |
|
256 MOZ_ASSERT(NS_IsMainThread()); |
|
257 |
|
258 mPreallocateAppProcessTask = nullptr; |
|
259 |
|
260 if (!mIsNuwaReady) { |
|
261 if (!mPreallocatedAppProcess && !mShutdown && mEnabled) { |
|
262 mPreallocatedAppProcess = ContentParent::RunNuwaProcess(); |
|
263 } |
|
264 // else mPreallocatedAppProcess is starting. It will NuwaFork() when ready. |
|
265 } else if (mSpareProcesses.IsEmpty()) { |
|
266 NuwaFork(); |
|
267 } |
|
268 } |
|
269 |
|
270 /** |
|
271 * Get a spare ContentParent from mSpareProcesses list. |
|
272 */ |
|
273 already_AddRefed<ContentParent> |
|
274 PreallocatedProcessManagerImpl::GetSpareProcess() |
|
275 { |
|
276 MOZ_ASSERT(NS_IsMainThread()); |
|
277 |
|
278 if (mSpareProcesses.IsEmpty()) { |
|
279 return nullptr; |
|
280 } |
|
281 |
|
282 nsRefPtr<ContentParent> process = mSpareProcesses.LastElement(); |
|
283 mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1); |
|
284 |
|
285 if (mSpareProcesses.IsEmpty() && mIsNuwaReady) { |
|
286 NS_ASSERTION(mPreallocatedAppProcess != nullptr, |
|
287 "Nuwa process is not present!"); |
|
288 ScheduleDelayedNuwaFork(); |
|
289 } |
|
290 |
|
291 return process.forget(); |
|
292 } |
|
293 |
|
294 /** |
|
295 * Publish a ContentParent to spare process list. |
|
296 */ |
|
297 void |
|
298 PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent) |
|
299 { |
|
300 MOZ_ASSERT(NS_IsMainThread()); |
|
301 |
|
302 if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { |
|
303 AutoJSContext cx; |
|
304 nsCOMPtr<nsIMessageBroadcaster> ppmm = |
|
305 do_GetService("@mozilla.org/parentprocessmessagemanager;1"); |
|
306 nsresult rv = ppmm->BroadcastAsyncMessage( |
|
307 NS_LITERAL_STRING("TEST-ONLY:nuwa-add-new-process"), |
|
308 JS::NullHandleValue, JS::NullHandleValue, cx, 1); |
|
309 } |
|
310 |
|
311 mSpareProcesses.AppendElement(aContent); |
|
312 |
|
313 if (!mDelayedContentParentRequests.IsEmpty()) { |
|
314 nsCOMPtr<nsIRunnable> runnable = mDelayedContentParentRequests[0]; |
|
315 mDelayedContentParentRequests.RemoveElementAt(0); |
|
316 NS_DispatchToMainThread(runnable); |
|
317 } |
|
318 } |
|
319 |
|
320 void |
|
321 PreallocatedProcessManagerImpl::MaybeForgetSpare(ContentParent* aContent) |
|
322 { |
|
323 MOZ_ASSERT(NS_IsMainThread()); |
|
324 |
|
325 if (!mDelayedContentParentRequests.IsEmpty()) { |
|
326 if (!mPreallocateAppProcessTask) { |
|
327 // This NuwaFork request is urgent. Don't delay it. |
|
328 DelayedNuwaFork(); |
|
329 } |
|
330 } |
|
331 |
|
332 if (mSpareProcesses.RemoveElement(aContent)) { |
|
333 return; |
|
334 } |
|
335 |
|
336 if (aContent == mPreallocatedAppProcess) { |
|
337 mPreallocatedAppProcess = nullptr; |
|
338 mIsNuwaReady = false; |
|
339 ScheduleDelayedNuwaFork(); |
|
340 } |
|
341 } |
|
342 |
|
343 void |
|
344 PreallocatedProcessManagerImpl::OnNuwaReady() |
|
345 { |
|
346 NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!"); |
|
347 ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess, |
|
348 hal::PROCESS_PRIORITY_MASTER); |
|
349 mIsNuwaReady = true; |
|
350 if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { |
|
351 AutoJSContext cx; |
|
352 nsCOMPtr<nsIMessageBroadcaster> ppmm = |
|
353 do_GetService("@mozilla.org/parentprocessmessagemanager;1"); |
|
354 nsresult rv = ppmm->BroadcastAsyncMessage( |
|
355 NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"), |
|
356 JS::NullHandleValue, JS::NullHandleValue, cx, 1); |
|
357 } |
|
358 NuwaFork(); |
|
359 } |
|
360 |
|
361 bool |
|
362 PreallocatedProcessManagerImpl::PreallocatedProcessReady() |
|
363 { |
|
364 return !mSpareProcesses.IsEmpty(); |
|
365 } |
|
366 |
|
367 |
|
368 void |
|
369 PreallocatedProcessManagerImpl::NuwaFork() |
|
370 { |
|
371 mPreallocatedAppProcess->SendNuwaFork(); |
|
372 } |
|
373 #endif |
|
374 |
|
375 void |
|
376 PreallocatedProcessManagerImpl::Disable() |
|
377 { |
|
378 if (!mEnabled) { |
|
379 return; |
|
380 } |
|
381 |
|
382 mEnabled = false; |
|
383 |
|
384 #ifdef MOZ_NUWA_PROCESS |
|
385 // Cancel pending fork. |
|
386 if (mPreallocateAppProcessTask) { |
|
387 mPreallocateAppProcessTask->Cancel(); |
|
388 mPreallocateAppProcessTask = nullptr; |
|
389 } |
|
390 #endif |
|
391 |
|
392 if (mPreallocatedAppProcess) { |
|
393 #ifdef MOZ_NUWA_PROCESS |
|
394 while (mSpareProcesses.Length() > 0){ |
|
395 nsRefPtr<ContentParent> process = mSpareProcesses[0]; |
|
396 process->Close(); |
|
397 mSpareProcesses.RemoveElementAt(0); |
|
398 } |
|
399 mIsNuwaReady = false; |
|
400 #endif |
|
401 mPreallocatedAppProcess->Close(); |
|
402 mPreallocatedAppProcess = nullptr; |
|
403 } |
|
404 } |
|
405 |
|
406 void |
|
407 PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject) |
|
408 { |
|
409 if (!mPreallocatedAppProcess) { |
|
410 return; |
|
411 } |
|
412 |
|
413 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); |
|
414 NS_ENSURE_TRUE_VOID(props); |
|
415 |
|
416 uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; |
|
417 props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); |
|
418 NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); |
|
419 |
|
420 if (childID == mPreallocatedAppProcess->ChildID()) { |
|
421 mPreallocatedAppProcess = nullptr; |
|
422 } |
|
423 } |
|
424 |
|
425 inline PreallocatedProcessManagerImpl* GetPPMImpl() |
|
426 { |
|
427 return PreallocatedProcessManagerImpl::Singleton(); |
|
428 } |
|
429 |
|
430 } // anonymous namespace |
|
431 |
|
432 namespace mozilla { |
|
433 |
|
434 /* static */ void |
|
435 PreallocatedProcessManager::AllocateAfterDelay() |
|
436 { |
|
437 #ifdef MOZ_NUWA_PROCESS |
|
438 GetPPMImpl()->ScheduleDelayedNuwaFork(); |
|
439 #else |
|
440 GetPPMImpl()->AllocateAfterDelay(); |
|
441 #endif |
|
442 } |
|
443 |
|
444 /* static */ void |
|
445 PreallocatedProcessManager::AllocateOnIdle() |
|
446 { |
|
447 GetPPMImpl()->AllocateOnIdle(); |
|
448 } |
|
449 |
|
450 /* static */ void |
|
451 PreallocatedProcessManager::AllocateNow() |
|
452 { |
|
453 GetPPMImpl()->AllocateNow(); |
|
454 } |
|
455 |
|
456 /* static */ already_AddRefed<ContentParent> |
|
457 PreallocatedProcessManager::Take() |
|
458 { |
|
459 #ifdef MOZ_NUWA_PROCESS |
|
460 return GetPPMImpl()->GetSpareProcess(); |
|
461 #else |
|
462 return GetPPMImpl()->Take(); |
|
463 #endif |
|
464 } |
|
465 |
|
466 #ifdef MOZ_NUWA_PROCESS |
|
467 /* static */ void |
|
468 PreallocatedProcessManager::PublishSpareProcess(ContentParent* aContent) |
|
469 { |
|
470 GetPPMImpl()->PublishSpareProcess(aContent); |
|
471 } |
|
472 |
|
473 /* static */ void |
|
474 PreallocatedProcessManager::MaybeForgetSpare(ContentParent* aContent) |
|
475 { |
|
476 GetPPMImpl()->MaybeForgetSpare(aContent); |
|
477 } |
|
478 |
|
479 /* static */ void |
|
480 PreallocatedProcessManager::OnNuwaReady() |
|
481 { |
|
482 GetPPMImpl()->OnNuwaReady(); |
|
483 } |
|
484 |
|
485 /*static */ bool |
|
486 PreallocatedProcessManager::PreallocatedProcessReady() |
|
487 { |
|
488 return GetPPMImpl()->PreallocatedProcessReady(); |
|
489 } |
|
490 |
|
491 /* static */ void |
|
492 PreallocatedProcessManager::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest) |
|
493 { |
|
494 GetPPMImpl()->RunAfterPreallocatedProcessReady(aRequest); |
|
495 } |
|
496 |
|
497 #endif |
|
498 |
|
499 } // namespace mozilla |