dom/ipc/PreallocatedProcessManager.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:469413739e1c
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

mercurial