From 882d1434a2706d4f48c231f58bb9bef33906a610 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Wed, 1 Feb 2017 13:34:24 +0100 Subject: [PATCH] Bug 1324428 - Simplified preallocated process manager. r=billm --- dom/ipc/ContentChild.cpp | 21 ++ dom/ipc/ContentParent.cpp | 175 ++++++++++++---- dom/ipc/ContentParent.h | 32 +++ dom/ipc/PContent.ipdl | 3 + dom/ipc/PreallocatedProcessManager.cpp | 278 +++++++++++++++++++++++++ dom/ipc/PreallocatedProcessManager.h | 82 ++++++++ dom/ipc/moz.build | 2 + dom/ipc/tests/mochitest.ini | 2 + dom/ipc/tests/test_Preallocated.html | 51 +++++ hal/Hal.cpp | 2 + hal/HalTypes.h | 3 + 11 files changed, 609 insertions(+), 42 deletions(-) create mode 100644 dom/ipc/PreallocatedProcessManager.cpp create mode 100644 dom/ipc/PreallocatedProcessManager.h create mode 100644 dom/ipc/tests/test_Preallocated.html diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 993f63b69287..4889745c71a6 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1398,6 +1398,15 @@ ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL, return IPC_OK(); } +static StaticRefPtr gFirstIdleTask; + +static void +FirstIdle(void) +{ + MOZ_ASSERT(gFirstIdleTask); + gFirstIdleTask = nullptr; + ContentChild::GetSingleton()->SendFirstIdle(); +} mozilla::jsipc::PJavaScriptChild * ContentChild::AllocPJavaScriptChild() @@ -1457,6 +1466,15 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor, { MOZ_ASSERT(!IsShuttingDown()); + static bool hasRunOnce = false; + if (!hasRunOnce) { + hasRunOnce = true; + MOZ_ASSERT(!gFirstIdleTask); + RefPtr firstIdleTask = NewCancelableRunnableFunction(FirstIdle); + gFirstIdleTask = firstIdleTask; + NS_IdleDispatchToCurrentThread(firstIdleTask.forget()); + } + return nsIContentChild::RecvPBrowserConstructor(aActor, aTabId, aContext, aChromeFlags, aCpID, aIsForBrowser); } @@ -1995,6 +2013,9 @@ ContentChild::ActorDestroy(ActorDestroyReason why) // keep persistent state. ProcessChild::QuickExit(); #else + if (gFirstIdleTask) { + gFirstIdleTask->Cancel(); + } nsHostObjectProtocolHandler::RemoveDataEntries(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 41ff9dfd4dd5..0eff89258c98 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -156,6 +156,7 @@ #include "nsThreadUtils.h" #include "nsToolkitCompsCID.h" #include "nsWidgetsCID.h" +#include "PreallocatedProcessManager.h" #include "ProcessPriorityManager.h" #include "SandboxHal.h" #include "ScreenManagerParent.h" @@ -476,6 +477,23 @@ static const char* sObserverTopics[] = { "cacheservice:empty-cache", }; +// PreallocateProcess is called by the PreallocatedProcessManager. +// ContentParent then takes this process back within GetNewOrUsedBrowserProcess. +/*static*/ already_AddRefed +ContentParent::PreallocateProcess() +{ + RefPtr process = + new ContentParent(/* aOpener = */ nullptr, + NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); + + if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) { + return nullptr; + } + + process->Init(); + return process.forget(); +} + /*static*/ void ContentParent::StartUp() { @@ -509,6 +527,9 @@ ContentParent::StartUp() BackgroundChild::Startup(); + // Try to preallocate a process that we can use later. + PreallocatedProcessManager::AllocateAfterDelay(); + sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS"); #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) @@ -575,6 +596,74 @@ ContentParent::JoinAllSubprocesses() sCanLaunchSubprocesses = false; } +/*static*/ uint32_t +ContentParent::GetPoolSize(const nsAString& aContentProcessType) +{ + if (!sBrowserContentParents) { + return 0; + } + + nsTArray* parents = + sBrowserContentParents->Get(aContentProcessType); + + return parents ? parents->Length() : 0; +} + + +/*static*/ nsTArray& +ContentParent::GetOrCreatePool(const nsAString& aContentProcessType) +{ + if (!sBrowserContentParents) { + sBrowserContentParents = + new nsClassHashtable>; + } + + return *sBrowserContentParents->LookupOrAdd(aContentProcessType); +} + +/*static*/ uint32_t +ContentParent::GetMaxProcessCount(const nsAString& aContentProcessType) +{ + int32_t maxContentParents; + nsAutoCString processCountPref("dom.ipc.processCount."); + processCountPref.Append(NS_ConvertUTF16toUTF8(aContentProcessType)); + if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) { + maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1); + } + + if (maxContentParents < 1) { + maxContentParents = 1; + } + + return static_cast(maxContentParents); +} + +/*static*/ bool +ContentParent::IsMaxProcessCountReached(const nsAString& aContentProcessType) +{ + return GetPoolSize(aContentProcessType) >= GetMaxProcessCount(aContentProcessType); +} + +/*static*/ already_AddRefed +ContentParent::RandomSelect(const nsTArray& aContentParents, + ContentParent* aOpener, int32_t aMaxContentParents) +{ + uint32_t maxSelectable = std::min(static_cast(aContentParents.Length()), + static_cast(aMaxContentParents)); + uint32_t startIdx = rand() % maxSelectable; + uint32_t currIdx = startIdx; + do { + RefPtr p = aContentParents[currIdx]; + NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?"); + if (p->mOpener == aOpener) { + return p.forget(); + } + currIdx = (currIdx + 1) % maxSelectable; + } while (currIdx != startIdx); + + return nullptr; +} + /*static*/ already_AddRefed ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType, ProcessPriority aPriority, @@ -585,58 +674,42 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType, if (aNew) { *aNew = false; } - - if (!sBrowserContentParents) { - sBrowserContentParents = - new nsClassHashtable>; - } - // Decide which pool of content parents we are going to be pulling from based // on the aRemoteType and aLargeAllocationProcess flag. nsAutoString contentProcessType(aLargeAllocationProcess ? NS_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE) : aRemoteType); - nsTArray* contentParents = - sBrowserContentParents->LookupOrAdd(contentProcessType); - int32_t maxContentParents; - nsAutoCString processCountPref("dom.ipc.processCount."); - processCountPref.Append(NS_ConvertUTF16toUTF8(contentProcessType)); - if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) { - maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1); + nsTArray& contentParents = GetOrCreatePool(contentProcessType); + + uint32_t maxContentParents = GetMaxProcessCount(contentProcessType); + + RefPtr p; + if (contentParents.Length() >= uint32_t(maxContentParents) && + (p = RandomSelect(contentParents, aOpener, maxContentParents))) { + return p.forget(); } - if (maxContentParents < 1) { - maxContentParents = 1; + // Try to take the preallocated process only for the default process type. + if (contentProcessType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) && + (p = PreallocatedProcessManager::Take())) { + // For pre-allocated process we have not set the opener yet. + p->mOpener = aOpener; + } else { + p = new ContentParent(aOpener, contentProcessType); + + if (aNew) { + *aNew = true; + } + + if (!p->LaunchSubprocess(aPriority)) { + return nullptr; + } + + p->Init(); } - if (contentParents->Length() >= uint32_t(maxContentParents)) { - uint32_t maxSelectable = std::min(static_cast(contentParents->Length()), - static_cast(maxContentParents)); - uint32_t startIdx = rand() % maxSelectable; - uint32_t currIdx = startIdx; - do { - RefPtr p = (*contentParents)[currIdx]; - NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?"); - if (p->mOpener == aOpener) { - return p.forget(); - } - currIdx = (currIdx + 1) % maxSelectable; - } while (currIdx != startIdx); - } - - RefPtr p = new ContentParent(aOpener, contentProcessType); - if (aNew) { - *aNew = true; - } - - if (!p->LaunchSubprocess(aPriority)) { - return nullptr; - } - - p->Init(); - - contentParents->AppendElement(p); + contentParents.AppendElement(p); return p.forget(); } @@ -2250,6 +2323,17 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword) return IPC_OK(); } +mozilla::ipc::IPCResult +ContentParent::RecvFirstIdle() +{ + // When the ContentChild goes idle, it sends us a FirstIdle message + // which we use as a good time to prelaunch another process. If we + // prelaunch any sooner than this, then we'll be competing with the + // child process and slowing it down. + PreallocatedProcessManager::AllocateAfterDelay(); + return IPC_OK(); +} + mozilla::ipc::IPCResult ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, const bool& aHidden) @@ -2552,6 +2636,13 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, SerializeURI(nullptr, *aUserContentCSSURL); } + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + nsAutoString cpId; + cpId.AppendInt(static_cast(this->ChildID())); + obs->NotifyObservers(static_cast(this), "ipc:content-initializing", cpId.get()); + } + return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 03ff24a52c38..c78f5fd83f4e 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -63,6 +63,8 @@ class SandboxBroker; class SandboxBrokerPolicyFactory; #endif +class PreallocatedProcessManagerImpl; + namespace embedding { class PrintingParent; } @@ -116,10 +118,17 @@ class ContentParent final : public PContentParent typedef mozilla::ipc::PrincipalInfo PrincipalInfo; typedef mozilla::dom::ClonedMessageData ClonedMessageData; + friend class mozilla::PreallocatedProcessManagerImpl; + public: virtual bool IsContentParent() const override { return true; } + /** + * Create a subprocess suitable for use later as a content process. + */ + static already_AddRefed PreallocateProcess(); + /** * Start up the content-process machinery. This might include * scheduling pre-launch tasks. @@ -137,6 +146,22 @@ public: */ static void JoinAllSubprocesses(); + static uint32_t GetPoolSize(const nsAString& aContentProcessType); + + static uint32_t GetMaxProcessCount(const nsAString& aContentProcessType); + + static bool IsMaxProcessCountReached(const nsAString& aContentProcessType); + + /** + * Picks a random content parent from |aContentParents| with a given |aOpener| + * respecting the index limit set by |aMaxContentParents|. + * Returns null if non available. + */ + static already_AddRefed + RandomSelect(const nsTArray& aContentParents, + ContentParent* aOpener, + int32_t maxContentParents); + /** * Get or create a content process for: * 1. browser iframe @@ -713,6 +738,11 @@ private: TabParent* aTopLevel, const TabId& aTabId, uint64_t* aId); + /** + * Get or create the corresponding content parent array to |aContentProcessType|. + */ + static nsTArray& GetOrCreatePool(const nsAString& aContentProcessType); + virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint&& aEndpoint) override; virtual mozilla::ipc::IPCResult RecvGetProcessAttributes(ContentParentId* aCpId, @@ -965,6 +995,8 @@ private: virtual mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist) override; + virtual mozilla::ipc::IPCResult RecvFirstIdle() override; + virtual mozilla::ipc::IPCResult RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, const bool& aHidden) override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 3f2f819eaaa1..474882426f41 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -896,6 +896,9 @@ parent: // Notify the parent of the presence or absence of private docshells async PrivateDocShellsExist(bool aExist); + // Tell the parent that the child has gone idle for the first time. + async FirstIdle(); + async AudioChannelServiceStatus(bool aActiveTelephonyChannel, bool aContentOrNormalChannel, bool aAnyActiveChannel); diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp new file mode 100644 index 000000000000..756e069933a7 --- /dev/null +++ b/dom/ipc/PreallocatedProcessManager.cpp @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/PreallocatedProcessManager.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsIPropertyBag2.h" +#include "ProcessPriorityManager.h" +#include "nsServiceManagerUtils.h" + +// This number is fairly arbitrary ... the intention is to put off +// launching another app process until the last one has finished +// loading its content, to reduce CPU/memory/IO contention. +#define DEFAULT_ALLOCATE_DELAY 1000 + +using namespace mozilla; +using namespace mozilla::hal; +using namespace mozilla::dom; + +namespace mozilla { + +/** + * This singleton class implements the static methods on + * PreallocatedProcessManager. + */ +class PreallocatedProcessManagerImpl final + : public nsIObserver +{ +public: + static PreallocatedProcessManagerImpl* Singleton(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + // See comments on PreallocatedProcessManager for these methods. + void AllocateAfterDelay(); + void AllocateOnIdle(); + void AllocateNow(); + already_AddRefed Take(); + +private: + static mozilla::StaticRefPtr sSingleton; + + PreallocatedProcessManagerImpl(); + ~PreallocatedProcessManagerImpl() {} + DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl); + + void Init(); + + void RereadPrefs(); + void Enable(); + void Disable(); + void CloseProcess(); + + void ObserveProcessShutdown(nsISupports* aSubject); + + bool mEnabled; + bool mShutdown; + RefPtr mPreallocatedProcess; +}; + +/* static */ StaticRefPtr +PreallocatedProcessManagerImpl::sSingleton; + +/* static */ PreallocatedProcessManagerImpl* +PreallocatedProcessManagerImpl::Singleton() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!sSingleton) { + sSingleton = new PreallocatedProcessManagerImpl(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } + + // First time when we init sSingleton, the pref database might not be in a + // reliable state (we are too early), so despite dom.ipc.processPrelaunch.enabled + // is set to true Preferences::GetBool will return false (it cannot find the pref). + // Since Init() above will be called only once, and the pref will not be changed, + // the manger will stay disabled. To prevent that let's re-read the pref each time + // someone accessing the manager singleton. This is a hack but this is not a hot code + // so it should be fine. + sSingleton->RereadPrefs(); + + return sSingleton; +} + +NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) + +PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() + : mEnabled(false) + , mShutdown(false) +{} + +void +PreallocatedProcessManagerImpl::Init() +{ + Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled"); + // We have to respect processCount at all time. This is especially important + // for testing. + Preferences::AddStrongObserver(this, "dom.ipc.processCount"); + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->AddObserver(this, "ipc:content-shutdown", + /* weakRef = */ false); + os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, + /* weakRef = */ false); + } + RereadPrefs(); +} + +NS_IMETHODIMP +PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!strcmp("ipc:content-shutdown", aTopic)) { + ObserveProcessShutdown(aSubject); + } else if (!strcmp("nsPref:changed", aTopic)) { + // The only other observer we registered was for our prefs. + RereadPrefs(); + } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { + Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled"); + Preferences::RemoveObserver(this, "dom.ipc.processCount"); + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "ipc:content-shutdown"); + os->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + } + } else { + MOZ_ASSERT(false); + } + + return NS_OK; +} + +void +PreallocatedProcessManagerImpl::RereadPrefs() +{ + if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { + Enable(); + } else { + Disable(); + } + + if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) { + CloseProcess(); + } +} + +already_AddRefed +PreallocatedProcessManagerImpl::Take() +{ + return mPreallocatedProcess.forget(); +} + +void +PreallocatedProcessManagerImpl::Enable() +{ + if (mEnabled) { + return; + } + + mEnabled = true; + AllocateAfterDelay(); +} + +void +PreallocatedProcessManagerImpl::AllocateAfterDelay() +{ + if (!mEnabled || mPreallocatedProcess) { + return; + } + + // Originally AllocateOnIdle() was post here, but since the gecko parent + // message loop in practice never goes idle, that didn't work out well. + // Let's just launch the process after the delay. + NS_DelayedDispatchToCurrentThread( + NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow), + Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", + DEFAULT_ALLOCATE_DELAY)); +} + +void +PreallocatedProcessManagerImpl::AllocateOnIdle() +{ + if (!mEnabled || mPreallocatedProcess) { + return; + } + + NS_IdleDispatchToCurrentThread(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); +} + +void +PreallocatedProcessManagerImpl::AllocateNow() +{ + if (!mEnabled || mPreallocatedProcess || + ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) { + return; + } + + mPreallocatedProcess = ContentParent::PreallocateProcess(); +} + +void +PreallocatedProcessManagerImpl::Disable() +{ + if (!mEnabled) { + return; + } + + mEnabled = false; + CloseProcess(); +} + +void +PreallocatedProcessManagerImpl::CloseProcess() +{ + if (mPreallocatedProcess) { + mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE); + mPreallocatedProcess = nullptr; + } +} + +void +PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject) +{ + if (!mPreallocatedProcess) { + return; + } + + nsCOMPtr props = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(props); + + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); + NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); + + if (childID == mPreallocatedProcess->ChildID()) { + mPreallocatedProcess = nullptr; + } +} + +inline PreallocatedProcessManagerImpl* GetPPMImpl() +{ + return PreallocatedProcessManagerImpl::Singleton(); +} + +/* static */ void +PreallocatedProcessManager::AllocateAfterDelay() +{ + GetPPMImpl()->AllocateAfterDelay(); +} + +/* static */ void +PreallocatedProcessManager::AllocateOnIdle() +{ + GetPPMImpl()->AllocateOnIdle(); +} + +/* static */ void +PreallocatedProcessManager::AllocateNow() +{ + GetPPMImpl()->AllocateNow(); +} + +/* static */ already_AddRefed +PreallocatedProcessManager::Take() +{ + return GetPPMImpl()->Take(); +} + +} // namespace mozilla diff --git a/dom/ipc/PreallocatedProcessManager.h b/dom/ipc/PreallocatedProcessManager.h new file mode 100644 index 000000000000..4acfa093f645 --- /dev/null +++ b/dom/ipc/PreallocatedProcessManager.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_PreallocatedProcessManager_h +#define mozilla_PreallocatedProcessManager_h + +#include "base/basictypes.h" +#include "mozilla/AlreadyAddRefed.h" + +namespace mozilla { +namespace dom { +class ContentParent; +} // namespace dom + +/** + * This class manages a ContentParent that it starts up ahead of any particular + * need. You can then call Take() to get this process and use it. Since we + * already started it up, it should be ready for use faster than if you'd + * created the process when you needed it. + * + * This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes + * from false to true, it preallocates a process. If it changes from true to + * false, it kills the preallocated process, if any. + * + * We don't expect this pref to flip between true and false in production, but + * flipping the pref is important for tests. + */ +class PreallocatedProcessManager final +{ + typedef mozilla::dom::ContentParent ContentParent; + +public: + /** + * Create a process after a delay. We wait for a period of time (specified + * by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process + * to go idle, then allocate the new process. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateAfterDelay(); + + /** + * Create a process once this process goes idle. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateOnIdle(); + + /** + * Create a process right now. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateNow(); + + /** + * Take the preallocated process, if we have one. If we don't have one, this + * returns null. + * + * If you call Take() twice in a row, the second call is guaranteed to return + * null. + * + * After you Take() the preallocated process, you need to call one of the + * Allocate* functions (or change the dom.ipc.processPrelaunch pref from + * false to true) before we'll create a new process. + */ + static already_AddRefed Take(); + +private: + PreallocatedProcessManager(); + DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager); +}; + +} // namespace mozilla + +#endif // defined mozilla_PreallocatedProcessManager_h diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 95b28c1838b5..452e1fe45166 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -39,6 +39,7 @@ EXPORTS.mozilla.dom += [ ] EXPORTS.mozilla += [ + 'PreallocatedProcessManager.h', 'ProcessHangMonitor.h', 'ProcessHangMonitorIPC.h', 'ProcessPriorityManager.h', @@ -58,6 +59,7 @@ UNIFIED_SOURCES += [ 'nsIContentChild.cpp', 'nsIContentParent.cpp', 'PermissionMessageUtils.cpp', + 'PreallocatedProcessManager.cpp', 'ProcessPriorityManager.cpp', 'ScreenManagerParent.cpp', 'StructuredCloneData.cpp', diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini index acd6a595c114..fba9c182f51d 100644 --- a/dom/ipc/tests/mochitest.ini +++ b/dom/ipc/tests/mochitest.ini @@ -21,3 +21,5 @@ skip-if = !e10s support-files = blob_verify.sjs !/dom/canvas/test/captureStream_common.js +[test_Preallocated.html] +skip-if = !e10s diff --git a/dom/ipc/tests/test_Preallocated.html b/dom/ipc/tests/test_Preallocated.html new file mode 100644 index 000000000000..c191bcd60bcd --- /dev/null +++ b/dom/ipc/tests/test_Preallocated.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + diff --git a/hal/Hal.cpp b/hal/Hal.cpp index 693d95da1a89..fa1b889d970b 100644 --- a/hal/Hal.cpp +++ b/hal/Hal.cpp @@ -915,6 +915,8 @@ ProcessPriorityToString(ProcessPriority aPriority) switch (aPriority) { case PROCESS_PRIORITY_MASTER: return "MASTER"; + case PROCESS_PRIORITY_PREALLOC: + return "PREALLOC"; case PROCESS_PRIORITY_FOREGROUND_HIGH: return "FOREGROUND_HIGH"; case PROCESS_PRIORITY_FOREGROUND: diff --git a/hal/HalTypes.h b/hal/HalTypes.h index f714ab4bbd2c..5f0ad3ff7b20 100644 --- a/hal/HalTypes.h +++ b/hal/HalTypes.h @@ -60,6 +60,9 @@ enum ProcessPriority { PROCESS_PRIORITY_BACKGROUND, PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE, PROCESS_PRIORITY_FOREGROUND_KEYBOARD, + // The special class for the preallocated process, high memory priority but + // low CPU priority. + PROCESS_PRIORITY_PREALLOC, // Any priority greater than or equal to FOREGROUND is considered // "foreground" for the purposes of priority testing, for example // CurrentProcessIsForeground().