1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/ipc/PreallocatedProcessManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,499 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et ft=cpp : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/PreallocatedProcessManager.h" 1.11 +#include "mozilla/ClearOnShutdown.h" 1.12 +#include "mozilla/Preferences.h" 1.13 +#include "mozilla/dom/ContentParent.h" 1.14 +#include "nsIPropertyBag2.h" 1.15 +#include "ProcessPriorityManager.h" 1.16 +#include "nsServiceManagerUtils.h" 1.17 +#include "nsCxPusher.h" 1.18 + 1.19 +#ifdef MOZ_NUWA_PROCESS 1.20 +#include "ipc/Nuwa.h" 1.21 +#endif 1.22 + 1.23 +// This number is fairly arbitrary ... the intention is to put off 1.24 +// launching another app process until the last one has finished 1.25 +// loading its content, to reduce CPU/memory/IO contention. 1.26 +#define DEFAULT_ALLOCATE_DELAY 1000 1.27 +#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds. 1.28 + 1.29 +using namespace mozilla; 1.30 +using namespace mozilla::hal; 1.31 +using namespace mozilla::dom; 1.32 + 1.33 +namespace { 1.34 + 1.35 +/** 1.36 + * This singleton class implements the static methods on 1.37 + * PreallocatedProcessManager. 1.38 + */ 1.39 +class PreallocatedProcessManagerImpl MOZ_FINAL 1.40 + : public nsIObserver 1.41 +{ 1.42 +public: 1.43 + static PreallocatedProcessManagerImpl* Singleton(); 1.44 + 1.45 + NS_DECL_ISUPPORTS 1.46 + NS_DECL_NSIOBSERVER 1.47 + 1.48 + // See comments on PreallocatedProcessManager for these methods. 1.49 + void AllocateAfterDelay(); 1.50 + void AllocateOnIdle(); 1.51 + void AllocateNow(); 1.52 + already_AddRefed<ContentParent> Take(); 1.53 + 1.54 +#ifdef MOZ_NUWA_PROCESS 1.55 +public: 1.56 + void ScheduleDelayedNuwaFork(); 1.57 + void DelayedNuwaFork(); 1.58 + void PublishSpareProcess(ContentParent* aContent); 1.59 + void MaybeForgetSpare(ContentParent* aContent); 1.60 + void OnNuwaReady(); 1.61 + bool PreallocatedProcessReady(); 1.62 + already_AddRefed<ContentParent> GetSpareProcess(); 1.63 + void RunAfterPreallocatedProcessReady(nsIRunnable* aRunnable); 1.64 + 1.65 +private: 1.66 + void NuwaFork(); 1.67 + 1.68 + // initialization off the critical path of app startup. 1.69 + CancelableTask* mPreallocateAppProcessTask; 1.70 + 1.71 + // The array containing the preallocated processes. 4 as the inline storage size 1.72 + // should be enough so we don't need to grow the nsAutoTArray. 1.73 + nsAutoTArray<nsRefPtr<ContentParent>, 4> mSpareProcesses; 1.74 + 1.75 + nsTArray<nsCOMPtr<nsIRunnable> > mDelayedContentParentRequests; 1.76 + 1.77 + // Nuwa process is ready for creating new process. 1.78 + bool mIsNuwaReady; 1.79 +#endif 1.80 + 1.81 +private: 1.82 + static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; 1.83 + 1.84 + PreallocatedProcessManagerImpl(); 1.85 + DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl); 1.86 + 1.87 + void Init(); 1.88 + 1.89 + void RereadPrefs(); 1.90 + void Enable(); 1.91 + void Disable(); 1.92 + 1.93 + void ObserveProcessShutdown(nsISupports* aSubject); 1.94 + 1.95 + bool mEnabled; 1.96 + bool mShutdown; 1.97 + nsRefPtr<ContentParent> mPreallocatedAppProcess; 1.98 +}; 1.99 + 1.100 +/* static */ StaticRefPtr<PreallocatedProcessManagerImpl> 1.101 +PreallocatedProcessManagerImpl::sSingleton; 1.102 + 1.103 +/* static */ PreallocatedProcessManagerImpl* 1.104 +PreallocatedProcessManagerImpl::Singleton() 1.105 +{ 1.106 + if (!sSingleton) { 1.107 + sSingleton = new PreallocatedProcessManagerImpl(); 1.108 + sSingleton->Init(); 1.109 + ClearOnShutdown(&sSingleton); 1.110 + } 1.111 + 1.112 + return sSingleton; 1.113 +} 1.114 + 1.115 +NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) 1.116 + 1.117 +PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() 1.118 + : mEnabled(false) 1.119 +#ifdef MOZ_NUWA_PROCESS 1.120 + , mPreallocateAppProcessTask(nullptr) 1.121 + , mIsNuwaReady(false) 1.122 +#endif 1.123 + , mShutdown(false) 1.124 +{} 1.125 + 1.126 +void 1.127 +PreallocatedProcessManagerImpl::Init() 1.128 +{ 1.129 + Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled"); 1.130 + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 1.131 + if (os) { 1.132 + os->AddObserver(this, "ipc:content-shutdown", 1.133 + /* weakRef = */ false); 1.134 + os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, 1.135 + /* weakRef = */ false); 1.136 + } 1.137 + RereadPrefs(); 1.138 +} 1.139 + 1.140 +NS_IMETHODIMP 1.141 +PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, 1.142 + const char* aTopic, 1.143 + const char16_t* aData) 1.144 +{ 1.145 + if (!strcmp("ipc:content-shutdown", aTopic)) { 1.146 + ObserveProcessShutdown(aSubject); 1.147 + } else if (!strcmp("nsPref:changed", aTopic)) { 1.148 + // The only other observer we registered was for our prefs. 1.149 + RereadPrefs(); 1.150 + } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { 1.151 + mShutdown = true; 1.152 + } else { 1.153 + MOZ_ASSERT(false); 1.154 + } 1.155 + 1.156 + return NS_OK; 1.157 +} 1.158 + 1.159 +void 1.160 +PreallocatedProcessManagerImpl::RereadPrefs() 1.161 +{ 1.162 + if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { 1.163 + Enable(); 1.164 + } else { 1.165 + Disable(); 1.166 + } 1.167 +} 1.168 + 1.169 +already_AddRefed<ContentParent> 1.170 +PreallocatedProcessManagerImpl::Take() 1.171 +{ 1.172 + return mPreallocatedAppProcess.forget(); 1.173 +} 1.174 + 1.175 +void 1.176 +PreallocatedProcessManagerImpl::Enable() 1.177 +{ 1.178 + if (mEnabled) { 1.179 + return; 1.180 + } 1.181 + 1.182 + mEnabled = true; 1.183 +#ifdef MOZ_NUWA_PROCESS 1.184 + ScheduleDelayedNuwaFork(); 1.185 +#else 1.186 + AllocateAfterDelay(); 1.187 +#endif 1.188 +} 1.189 + 1.190 +void 1.191 +PreallocatedProcessManagerImpl::AllocateAfterDelay() 1.192 +{ 1.193 + if (!mEnabled || mPreallocatedAppProcess) { 1.194 + return; 1.195 + } 1.196 + 1.197 + MessageLoop::current()->PostDelayedTask( 1.198 + FROM_HERE, 1.199 + NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle), 1.200 + Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", 1.201 + DEFAULT_ALLOCATE_DELAY)); 1.202 +} 1.203 + 1.204 +void 1.205 +PreallocatedProcessManagerImpl::AllocateOnIdle() 1.206 +{ 1.207 + if (!mEnabled || mPreallocatedAppProcess) { 1.208 + return; 1.209 + } 1.210 + 1.211 + MessageLoop::current()->PostIdleTask( 1.212 + FROM_HERE, 1.213 + NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); 1.214 +} 1.215 + 1.216 +void 1.217 +PreallocatedProcessManagerImpl::AllocateNow() 1.218 +{ 1.219 + if (!mEnabled || mPreallocatedAppProcess) { 1.220 + return; 1.221 + } 1.222 + 1.223 + mPreallocatedAppProcess = ContentParent::PreallocateAppProcess(); 1.224 +} 1.225 + 1.226 +#ifdef MOZ_NUWA_PROCESS 1.227 + 1.228 +void 1.229 +PreallocatedProcessManagerImpl::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest) 1.230 +{ 1.231 + MOZ_ASSERT(NS_IsMainThread()); 1.232 + mDelayedContentParentRequests.AppendElement(aRequest); 1.233 + 1.234 + // This is an urgent NuwaFork() request. Request to fork at once. 1.235 + DelayedNuwaFork(); 1.236 +} 1.237 + 1.238 +void 1.239 +PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork() 1.240 +{ 1.241 + MOZ_ASSERT(NS_IsMainThread()); 1.242 + 1.243 + if (mPreallocateAppProcessTask) { 1.244 + // Make sure there is only one request running. 1.245 + return; 1.246 + } 1.247 + 1.248 + mPreallocateAppProcessTask = NewRunnableMethod( 1.249 + this, &PreallocatedProcessManagerImpl::DelayedNuwaFork); 1.250 + MessageLoop::current()->PostDelayedTask( 1.251 + FROM_HERE, mPreallocateAppProcessTask, 1.252 + Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", 1.253 + DEFAULT_ALLOCATE_DELAY)); 1.254 +} 1.255 + 1.256 +void 1.257 +PreallocatedProcessManagerImpl::DelayedNuwaFork() 1.258 +{ 1.259 + MOZ_ASSERT(NS_IsMainThread()); 1.260 + 1.261 + mPreallocateAppProcessTask = nullptr; 1.262 + 1.263 + if (!mIsNuwaReady) { 1.264 + if (!mPreallocatedAppProcess && !mShutdown && mEnabled) { 1.265 + mPreallocatedAppProcess = ContentParent::RunNuwaProcess(); 1.266 + } 1.267 + // else mPreallocatedAppProcess is starting. It will NuwaFork() when ready. 1.268 + } else if (mSpareProcesses.IsEmpty()) { 1.269 + NuwaFork(); 1.270 + } 1.271 +} 1.272 + 1.273 +/** 1.274 + * Get a spare ContentParent from mSpareProcesses list. 1.275 + */ 1.276 +already_AddRefed<ContentParent> 1.277 +PreallocatedProcessManagerImpl::GetSpareProcess() 1.278 +{ 1.279 + MOZ_ASSERT(NS_IsMainThread()); 1.280 + 1.281 + if (mSpareProcesses.IsEmpty()) { 1.282 + return nullptr; 1.283 + } 1.284 + 1.285 + nsRefPtr<ContentParent> process = mSpareProcesses.LastElement(); 1.286 + mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1); 1.287 + 1.288 + if (mSpareProcesses.IsEmpty() && mIsNuwaReady) { 1.289 + NS_ASSERTION(mPreallocatedAppProcess != nullptr, 1.290 + "Nuwa process is not present!"); 1.291 + ScheduleDelayedNuwaFork(); 1.292 + } 1.293 + 1.294 + return process.forget(); 1.295 +} 1.296 + 1.297 +/** 1.298 + * Publish a ContentParent to spare process list. 1.299 + */ 1.300 +void 1.301 +PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent) 1.302 +{ 1.303 + MOZ_ASSERT(NS_IsMainThread()); 1.304 + 1.305 + if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { 1.306 + AutoJSContext cx; 1.307 + nsCOMPtr<nsIMessageBroadcaster> ppmm = 1.308 + do_GetService("@mozilla.org/parentprocessmessagemanager;1"); 1.309 + nsresult rv = ppmm->BroadcastAsyncMessage( 1.310 + NS_LITERAL_STRING("TEST-ONLY:nuwa-add-new-process"), 1.311 + JS::NullHandleValue, JS::NullHandleValue, cx, 1); 1.312 + } 1.313 + 1.314 + mSpareProcesses.AppendElement(aContent); 1.315 + 1.316 + if (!mDelayedContentParentRequests.IsEmpty()) { 1.317 + nsCOMPtr<nsIRunnable> runnable = mDelayedContentParentRequests[0]; 1.318 + mDelayedContentParentRequests.RemoveElementAt(0); 1.319 + NS_DispatchToMainThread(runnable); 1.320 + } 1.321 +} 1.322 + 1.323 +void 1.324 +PreallocatedProcessManagerImpl::MaybeForgetSpare(ContentParent* aContent) 1.325 +{ 1.326 + MOZ_ASSERT(NS_IsMainThread()); 1.327 + 1.328 + if (!mDelayedContentParentRequests.IsEmpty()) { 1.329 + if (!mPreallocateAppProcessTask) { 1.330 + // This NuwaFork request is urgent. Don't delay it. 1.331 + DelayedNuwaFork(); 1.332 + } 1.333 + } 1.334 + 1.335 + if (mSpareProcesses.RemoveElement(aContent)) { 1.336 + return; 1.337 + } 1.338 + 1.339 + if (aContent == mPreallocatedAppProcess) { 1.340 + mPreallocatedAppProcess = nullptr; 1.341 + mIsNuwaReady = false; 1.342 + ScheduleDelayedNuwaFork(); 1.343 + } 1.344 +} 1.345 + 1.346 +void 1.347 +PreallocatedProcessManagerImpl::OnNuwaReady() 1.348 +{ 1.349 + NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!"); 1.350 + ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess, 1.351 + hal::PROCESS_PRIORITY_MASTER); 1.352 + mIsNuwaReady = true; 1.353 + if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) { 1.354 + AutoJSContext cx; 1.355 + nsCOMPtr<nsIMessageBroadcaster> ppmm = 1.356 + do_GetService("@mozilla.org/parentprocessmessagemanager;1"); 1.357 + nsresult rv = ppmm->BroadcastAsyncMessage( 1.358 + NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"), 1.359 + JS::NullHandleValue, JS::NullHandleValue, cx, 1); 1.360 + } 1.361 + NuwaFork(); 1.362 +} 1.363 + 1.364 +bool 1.365 +PreallocatedProcessManagerImpl::PreallocatedProcessReady() 1.366 +{ 1.367 + return !mSpareProcesses.IsEmpty(); 1.368 +} 1.369 + 1.370 + 1.371 +void 1.372 +PreallocatedProcessManagerImpl::NuwaFork() 1.373 +{ 1.374 + mPreallocatedAppProcess->SendNuwaFork(); 1.375 +} 1.376 +#endif 1.377 + 1.378 +void 1.379 +PreallocatedProcessManagerImpl::Disable() 1.380 +{ 1.381 + if (!mEnabled) { 1.382 + return; 1.383 + } 1.384 + 1.385 + mEnabled = false; 1.386 + 1.387 +#ifdef MOZ_NUWA_PROCESS 1.388 + // Cancel pending fork. 1.389 + if (mPreallocateAppProcessTask) { 1.390 + mPreallocateAppProcessTask->Cancel(); 1.391 + mPreallocateAppProcessTask = nullptr; 1.392 + } 1.393 +#endif 1.394 + 1.395 + if (mPreallocatedAppProcess) { 1.396 +#ifdef MOZ_NUWA_PROCESS 1.397 + while (mSpareProcesses.Length() > 0){ 1.398 + nsRefPtr<ContentParent> process = mSpareProcesses[0]; 1.399 + process->Close(); 1.400 + mSpareProcesses.RemoveElementAt(0); 1.401 + } 1.402 + mIsNuwaReady = false; 1.403 +#endif 1.404 + mPreallocatedAppProcess->Close(); 1.405 + mPreallocatedAppProcess = nullptr; 1.406 + } 1.407 +} 1.408 + 1.409 +void 1.410 +PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject) 1.411 +{ 1.412 + if (!mPreallocatedAppProcess) { 1.413 + return; 1.414 + } 1.415 + 1.416 + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); 1.417 + NS_ENSURE_TRUE_VOID(props); 1.418 + 1.419 + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; 1.420 + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); 1.421 + NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); 1.422 + 1.423 + if (childID == mPreallocatedAppProcess->ChildID()) { 1.424 + mPreallocatedAppProcess = nullptr; 1.425 + } 1.426 +} 1.427 + 1.428 +inline PreallocatedProcessManagerImpl* GetPPMImpl() 1.429 +{ 1.430 + return PreallocatedProcessManagerImpl::Singleton(); 1.431 +} 1.432 + 1.433 +} // anonymous namespace 1.434 + 1.435 +namespace mozilla { 1.436 + 1.437 +/* static */ void 1.438 +PreallocatedProcessManager::AllocateAfterDelay() 1.439 +{ 1.440 +#ifdef MOZ_NUWA_PROCESS 1.441 + GetPPMImpl()->ScheduleDelayedNuwaFork(); 1.442 +#else 1.443 + GetPPMImpl()->AllocateAfterDelay(); 1.444 +#endif 1.445 +} 1.446 + 1.447 +/* static */ void 1.448 +PreallocatedProcessManager::AllocateOnIdle() 1.449 +{ 1.450 + GetPPMImpl()->AllocateOnIdle(); 1.451 +} 1.452 + 1.453 +/* static */ void 1.454 +PreallocatedProcessManager::AllocateNow() 1.455 +{ 1.456 + GetPPMImpl()->AllocateNow(); 1.457 +} 1.458 + 1.459 +/* static */ already_AddRefed<ContentParent> 1.460 +PreallocatedProcessManager::Take() 1.461 +{ 1.462 +#ifdef MOZ_NUWA_PROCESS 1.463 + return GetPPMImpl()->GetSpareProcess(); 1.464 +#else 1.465 + return GetPPMImpl()->Take(); 1.466 +#endif 1.467 +} 1.468 + 1.469 +#ifdef MOZ_NUWA_PROCESS 1.470 +/* static */ void 1.471 +PreallocatedProcessManager::PublishSpareProcess(ContentParent* aContent) 1.472 +{ 1.473 + GetPPMImpl()->PublishSpareProcess(aContent); 1.474 +} 1.475 + 1.476 +/* static */ void 1.477 +PreallocatedProcessManager::MaybeForgetSpare(ContentParent* aContent) 1.478 +{ 1.479 + GetPPMImpl()->MaybeForgetSpare(aContent); 1.480 +} 1.481 + 1.482 +/* static */ void 1.483 +PreallocatedProcessManager::OnNuwaReady() 1.484 +{ 1.485 + GetPPMImpl()->OnNuwaReady(); 1.486 +} 1.487 + 1.488 +/*static */ bool 1.489 +PreallocatedProcessManager::PreallocatedProcessReady() 1.490 +{ 1.491 + return GetPPMImpl()->PreallocatedProcessReady(); 1.492 +} 1.493 + 1.494 +/* static */ void 1.495 +PreallocatedProcessManager::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest) 1.496 +{ 1.497 + GetPPMImpl()->RunAfterPreallocatedProcessReady(aRequest); 1.498 +} 1.499 + 1.500 +#endif 1.501 + 1.502 +} // namespace mozilla