From 4899b7707ea1cd0eb947484c73547aa66483d0d0 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Thu, 28 May 2020 14:33:24 +0000 Subject: [PATCH] Bug 1602757: add preallocation cache for webIsolated (fission) processes r=nika,smaug Differential Revision: https://phabricator.services.mozilla.com/D69589 --- dom/base/ChromeUtils.cpp | 4 + dom/chrome-webidl/ChromeUtils.webidl | 1 + dom/ipc/ContentChild.cpp | 32 ++- dom/ipc/ContentParent.cpp | 274 ++++++++++++++++++++--- dom/ipc/ContentParent.h | 16 +- dom/ipc/PreallocatedProcessManager.cpp | 235 ++++++++++++++----- dom/ipc/PreallocatedProcessManager.h | 29 ++- dom/ipc/ProcessPriorityManager.cpp | 3 + modules/libpref/init/StaticPrefList.yaml | 9 +- widget/ProcInfo.h | 4 +- 10 files changed, 494 insertions(+), 113 deletions(-) diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 879c12aa6cf6..426cdeba6856 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -760,6 +760,7 @@ static WebIDLProcType ProcTypeToWebIDL(mozilla::ProcType aType) { #ifdef MOZ_ENABLE_FORKSERVER PROCTYPE_TO_WEBIDL_CASE(ForkServer, ForkServer); #endif + PROCTYPE_TO_WEBIDL_CASE(Preallocated, Preallocated); PROCTYPE_TO_WEBIDL_CASE(Unknown, Unknown); } @@ -861,6 +862,9 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, } else if (remoteType.EqualsLiteral( LARGE_ALLOCATION_REMOTE_TYPE)) { type = mozilla::ProcType::WebLargeAllocation; + } else if (remoteType.EqualsLiteral( + PREALLOC_REMOTE_TYPE)) { + type = mozilla::ProcType::Preallocated; } else { MOZ_CRASH("Unknown remoteType"); } diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl index bb327e919eff..f6023a80aa05 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -517,6 +517,7 @@ enum WebIDLProcType { #ifdef MOZ_ENABLE_FORKSERVER "forkServer", #endif + "preallocated", "unknown", }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 2fe91affcd5c..e31ac7d712ed 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -29,6 +29,7 @@ #include "mozilla/Unused.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/TelemetryIPC.h" #include "mozilla/RemoteDecoderManagerChild.h" @@ -2545,12 +2546,27 @@ mozilla::ipc::IPCResult ContentChild::RecvAppInfo( mozilla::ipc::IPCResult ContentChild::RecvRemoteType( const nsString& aRemoteType) { - MOZ_ASSERT(DOMStringIsNull(mRemoteType)); + if (!DOMStringIsNull(mRemoteType)) { + // Preallocated processes are type PREALLOC_REMOTE_TYPE; they can become + // anything except a File: process. + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Changing remoteType of process %d from %s to %s", getpid(), + NS_ConvertUTF16toUTF8(mRemoteType).get(), + NS_ConvertUTF16toUTF8(aRemoteType).get())); + // prealloc->anything (but file) or web->web allowed + MOZ_RELEASE_ASSERT(!aRemoteType.EqualsLiteral(FILE_REMOTE_TYPE) && + (mRemoteType.EqualsLiteral(PREALLOC_REMOTE_TYPE) || + (mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) && + aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE)))); + } else { + // Initial setting of remote type. Either to 'prealloc' or the actual + // final type (if we didn't use a preallocated process) + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Setting remoteType of process %d to %s", getpid(), + NS_ConvertUTF16toUTF8(aRemoteType).get())); + } - mRemoteType.Assign(aRemoteType); - - // For non-default ("web") types, update the process name so about:memory's - // process names are more obvious. + // Update the process name so about:memory's process names are more obvious. if (aRemoteType.EqualsLiteral(FILE_REMOTE_TYPE)) { SetProcessName(NS_LITERAL_STRING("file:// Content")); } else if (aRemoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) { @@ -2559,7 +2575,13 @@ mozilla::ipc::IPCResult ContentChild::RecvRemoteType( SetProcessName(NS_LITERAL_STRING("Privileged Content")); } else if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) { SetProcessName(NS_LITERAL_STRING("Large Allocation Web Content")); + } else if (RemoteTypePrefix(aRemoteType) + .EqualsLiteral(FISSION_WEB_REMOTE_TYPE)) { + SetProcessName(NS_LITERAL_STRING("Isolated Web Content")); } + // else "prealloc", "web" or "webCOOP+COEP" type -> "Web Content" already set + + mRemoteType.Assign(aRemoteType); return IPC_OK(); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index b6ef5c23b3df..c6058031be5b 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -20,6 +20,9 @@ # include "mozilla/a11y/PDocAccessible.h" #endif #include "GeckoProfiler.h" +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerMarkerPayload.h" +#endif #include "GMPServiceParent.h" #include "HandlerServiceParent.h" #include "IHistory.h" @@ -349,6 +352,11 @@ extern FileDescriptor CreateAudioIPCConnection(); namespace dom { +LazyLogModule gProcessLog("Process"); + +/* static */ +LogModule* ContentParent::GetLog() { return gProcessLog; } + #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" #define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" @@ -475,6 +483,10 @@ ContentParentsMemoryReporter::CollectReports( return NS_OK; } +// A hashtable (by type) of processes/ContentParents. This includes +// processes that are in the Preallocator cache (which would be type +// 'prealloc'), and recycled processes ('web' and in the future +// eTLD+1-locked) processes). nsClassHashtable>* ContentParent::sBrowserContentParents; @@ -632,8 +644,10 @@ static const char* sObserverTopics[] = { /*static*/ RefPtr ContentParent::PreallocateProcess() { RefPtr process = new ContentParent( - /* aOpener = */ nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); + /* aOpener = */ nullptr, NS_LITERAL_STRING(PREALLOC_REMOTE_TYPE)); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Preallocating process of type " PREALLOC_REMOTE_TYPE)); return process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC); } @@ -712,6 +726,8 @@ const nsDependentSubstring RemoteTypePrefix( } bool IsWebRemoteType(const nsAString& aContentProcessType) { + // Note: matches webIsolated as well as web (and webLargeAllocation, and + // webCOOP+COEP) return StringBeginsWith(aContentProcessType, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); } @@ -729,7 +745,7 @@ uint32_t ContentParent::GetMaxProcessCount( RemoteTypePrefix(aContentProcessType); // Check for the default remote type of "web", as it uses different prefs. - if (processTypePrefix.EqualsLiteral("web")) { + if (processTypePrefix.EqualsLiteral(DEFAULT_REMOTE_TYPE)) { return GetMaxWebProcessCount(); } @@ -754,27 +770,57 @@ bool ContentParent::IsMaxProcessCountReached( GetMaxProcessCount(aContentProcessType); } +// Really more ReleaseUnneededProcesses() /*static*/ void ContentParent::ReleaseCachedProcesses() { - if (!GetPoolSize(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("ReleaseCachedProcesses:")); + if (!sBrowserContentParents) { return; } - // We might want to extend this for other process types as well in the - // future... - nsTArray& contentParents = - GetOrCreatePool(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); +#ifdef DEBUG + int num = 0; + for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) { + nsTArray* contentParents = iter.Data().get(); + num += contentParents->Length(); + for (auto* cp : *contentParents) { + MOZ_LOG( + ContentParent::GetLog(), LogLevel::Debug, + ("%s: %zu processes", NS_ConvertUTF16toUTF8(cp->mRemoteType).get(), + contentParents->Length())); + break; + } + } +#endif + // We process the toRelease array outside of the iteration to avoid modifying + // the list (via RemoveFromList()) while we're iterating it. nsTArray toRelease; + for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) { + nsTArray* contentParents = iter.Data().get(); - // Shuting down these processes will change the array so let's use another - // array for the removal. - for (auto* cp : contentParents) { - if (cp->ManagedPBrowserParent().Count() == 0) { - toRelease.AppendElement(cp); + // Shutting down these processes will change the array so let's use another + // array for the removal. + for (auto* cp : *contentParents) { + if (cp->ManagedPBrowserParent().Count() == 0 && + !cp->HasActiveWorkerOrJSPlugin() && + cp->mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE)) { + toRelease.AppendElement(cp); + } else { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + (" Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d", + cp, NS_ConvertUTF16toUTF8(cp->mRemoteType).get(), + cp->ManagedPBrowserParent().Count(), + cp->HasActiveWorkerOrJSPlugin())); + } } } for (auto* cp : toRelease) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + (" Shutdown %p (%s)", cp, + NS_ConvertUTF16toUTF8(cp->mRemoteType).get())); + PreallocatedProcessManager::Erase(cp); // Start a soft shutdown. cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE); // Make sure we don't select this process for new tabs. @@ -839,6 +885,19 @@ already_AddRefed ContentParent::GetUsedBrowserProcess( // If the provider returned an existing ContentParent, use that one. if (0 <= index && static_cast(index) <= aMaxContentParents) { RefPtr retval = aContentParents[index]; +#ifdef MOZ_GECKO_PROFILER + if (profiler_thread_is_being_profiled()) { + nsPrintfCString marker("Reused process %u", + (unsigned int)retval->ChildID()); + TimeStamp now = TimeStamp::Now(); + PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload, + (marker, now, now)); + } +#endif + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(), + (unsigned int)retval->ChildID(), + NS_ConvertUTF16toUTF8(aRemoteType).get())); return retval.forget(); } } else { @@ -848,21 +907,55 @@ already_AddRefed ContentParent::GetUsedBrowserProcess( RefPtr random; if (aContentParents.Length() >= aMaxContentParents && (random = MinTabSelect(aContentParents, aOpener, aMaxContentParents))) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetUsedProcess: Reused random process %p (%d) for %s", + random.get(), (unsigned int)random->ChildID(), + NS_ConvertUTF16toUTF8(aRemoteType).get())); return random.forget(); } } - // Try to take the preallocated process only for the default process type. + // Try to take the preallocated process except for blacklisted types. // The preallocated process manager might not had the chance yet to release // the process after a very recent ShutDownProcess, let's make sure we don't // try to reuse a process that is being shut down. RefPtr p; - if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) && - (p = PreallocatedProcessManager::Take()) && !p->mShutdownPending) { + bool preallocated = false; + if (!aRemoteType.EqualsLiteral(FILE_REMOTE_TYPE) && + !aRemoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE) && // Bug 1638119 + (p = PreallocatedProcessManager::Take(aRemoteType)) && + !p->mShutdownPending) { + // p may be a preallocated process, or (if not PREALLOC_REMOTE_TYPE) + // a perviously-used process that's being recycled. Currently this is + // only done for short-duration web (DEFAULT_REMOTE_TYPE) processes + preallocated = p->mRemoteType.EqualsLiteral(PREALLOC_REMOTE_TYPE); // For pre-allocated process we have not set the opener yet. +#ifdef MOZ_GECKO_PROFILER + if (profiler_thread_is_being_profiled()) { + nsPrintfCString marker("Assigned %s process %u", + preallocated ? "preallocated" : "reused web", + (unsigned int)p->ChildID()); + TimeStamp now = TimeStamp::Now(); + PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload, + (marker, now, now)); + } +#endif + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Adopted %s process %p for type %s", + preallocated ? "preallocated" : "reused web", p.get(), + NS_ConvertUTF16toUTF8(aRemoteType).get())); p->mOpener = aOpener; - aContentParents.AppendElement(p); p->mActivateTS = TimeStamp::Now(); + aContentParents.AppendElement(p); + if (preallocated) { + p->mRemoteType.Assign(aRemoteType); + // Specialize this process for the appropriate eTLD+1 + Unused << p->SendRemoteType(p->mRemoteType); + } else { + // we only allow "web" to "web" for security reasons + MOZ_RELEASE_ASSERT(p->mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) && + aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE)); + } return p.forget(); } @@ -877,12 +970,17 @@ ContentParent::GetNewOrUsedBrowserProcessInternal(Element* aFrameElement, ContentParent* aOpener, bool aPreferUsed, bool aIsSync) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetNewOrUsedProcess for type %s", + NS_ConvertUTF16toUTF8(aRemoteType).get())); nsTArray& contentParents = GetOrCreatePool(aRemoteType); uint32_t maxContentParents = GetMaxProcessCount(aRemoteType); if (aRemoteType.EqualsLiteral( LARGE_ALLOCATION_REMOTE_TYPE) // We never want to re-use // Large-Allocation processes. && contentParents.Length() >= maxContentParents) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetNewOrUsedProcess: returning Large Used process")); return GetNewOrUsedBrowserProcessInternal( aFrameElement, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), aPriority, aOpener, /*aPreferUsed =*/false, aIsSync); @@ -895,11 +993,18 @@ ContentParent::GetNewOrUsedBrowserProcessInternal(Element* aFrameElement, if (contentParent) { // We have located a process. It may not have finished initializing, // this will be for the caller to handle. + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetNewOrUsedProcess: Used process %p (launching %d)", + contentParent.get(), contentParent->IsLaunching())); return contentParent.forget(); } // No reusable process. Let's create and launch one. // The life cycle will be set to `LifecycleState::LAUNCHING`. + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Launching new process immediately for type %s", + NS_ConvertUTF16toUTF8(aRemoteType).get())); + contentParent = new ContentParent(aOpener, aRemoteType); if (!contentParent->BeginSubprocessLaunch(aIsSync, aPriority)) { // Launch aborted because of shutdown. Bailout. @@ -912,9 +1017,12 @@ ContentParent::GetNewOrUsedBrowserProcessInternal(Element* aFrameElement, // Until the new process is ready let's not allow to start up any // preallocated processes. The blocker will be removed once we receive // the first idle message. - PreallocatedProcessManager::AddBlocker(contentParent); + contentParent->mIsAPreallocBlocker = true; + PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent); MOZ_ASSERT(contentParent->IsLaunching()); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("GetNewOrUsedProcess: new process %p", contentParent.get())); return contentParent.forget(); } @@ -1412,6 +1520,8 @@ void ContentParent::Init() { } void ContentParent::MaybeAsyncSendShutDownMessage() { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("MaybeAsyncSendShutDownMessage %p", this)); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!TryToRecycle()); @@ -1554,6 +1664,8 @@ void ContentParent::RemoveFromList() { } void ContentParent::MarkAsDead() { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("Marking ContentProcess %p as dead", this)); if (!mShutdownPending) { RemoveFromList(); } @@ -1717,10 +1829,21 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) { } mIdleListeners.Clear(); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("destroying Subprocess in ActorDestroy: ContentParent %p " + "mSubprocess %p handle %ld", + this, mSubprocess, + mSubprocess ? (long)mSubprocess->GetChildProcessHandle() : -1)); // FIXME (bug 1520997): does this really need an additional dispatch? MessageLoop::current()->PostTask(NS_NewRunnableFunction( - "DelayedDeleteSubprocessRunnable", - [subprocess = mSubprocess] { subprocess->Destroy(); })); + "DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] { + MOZ_LOG( + ContentParent::GetLog(), LogLevel::Debug, + ("destroyed Subprocess in ActorDestroy: Subprocess %p handle %ld", + subprocess, + subprocess ? (long)subprocess->GetChildProcessHandle() : -1)); + subprocess->Destroy(); + })); mSubprocess = nullptr; ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); @@ -1754,23 +1877,53 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) { void ContentParent::ActorDealloc() { mSelfRef = nullptr; } bool ContentParent::TryToRecycle() { - // This life time check should be replaced by a memory health check (memory - // usage + fragmentation). - const double kMaxLifeSpan = 5; - if (mShutdownPending || mCalledKillHard || !IsAlive() || - !mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) || - (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan || - !PreallocatedProcessManager::Provide(this)) { + // We can only do this if we have a separate cache for recycled + // 'web' processes, and handle them differently than webIsolated ones + if (!mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE)) { return false; } + // This life time check should be replaced by a memory health check (memory + // usage + fragmentation). - // The PreallocatedProcessManager took over the ownership let's not keep a - // reference to it, until we don't take it back. - RemoveFromList(); + // Note that this is specifically to help with edge cases that rapidly + // create-and-destroy processes + const double kMaxLifeSpan = 5; + MOZ_LOG( + ContentParent::GetLog(), LogLevel::Debug, + ("TryToRecycle ContentProcess %p (%u) with lifespan %f seconds", this, + (unsigned int)ChildID(), (TimeStamp::Now() - mActivateTS).ToSeconds())); + + if (mShutdownPending || mCalledKillHard || !IsAlive() || + !mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) || + (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("TryToRecycle did not take ownership of %p", this)); + // It's possible that the process was already cached via Provide() (such + // as from TabDestroyed), and we're being called from a different path, + // such as UnregisterRemoveWorkerActor(), and we're now past kMaxLifeSpan + // (or some other). Ensure that if we're going to destroy this process + // that we don't have it in the cache. + PreallocatedProcessManager::Erase(this); + return false; + } else { + // This will either cache it and take ownership, realize it was already + // cached (due to this being called a second time via a different + // path), or it will decide to not take ownership (if it has another + // already cached) + bool retval = PreallocatedProcessManager::Provide(this); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Provide did %stake ownership of %p", retval ? "" : "not ", this)); + if (retval) { + // The PreallocatedProcessManager took over the ownership let's not keep a + // reference to it + RemoveFromList(); + } + return retval; + } return true; } -bool ContentParent::ShouldKeepProcessAlive() { +bool ContentParent::HasActiveWorkerOrJSPlugin() { if (IsForJSPlugin()) { return true; } @@ -1782,6 +1935,13 @@ bool ContentParent::ShouldKeepProcessAlive() { return true; } } + return false; +} + +bool ContentParent::ShouldKeepProcessAlive() { + if (HasActiveWorkerOrJSPlugin()) { + return true; + } if (!sBrowserContentParents) { return false; @@ -1839,6 +1999,8 @@ void ContentParent::NotifyTabDestroying() { return; } + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("NotifyTabDestroying %p", this)); if (TryToRecycle()) { return; } @@ -1885,8 +2047,11 @@ void ContentParent::NotifyTabDestroyed(const TabId& aTabId, // There can be more than one PBrowser for a given app process // because of popup windows. When the last one closes, shut // us down. + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("NotifyTabDestroyed %p", this)); if (ManagedPBrowserParent().Count() == 1 && !ShouldKeepProcessAlive() && !TryToRecycle()) { + MarkAsDead(); MaybeAsyncSendShutDownMessage(); } } @@ -2091,7 +2256,10 @@ void ContentParent::LaunchSubprocessReject() { // Now that communication with the child is complete, we can cleanup // the preference serializer. mPrefSerializer = nullptr; - PreallocatedProcessManager::RemoveBlocker(this); + if (mIsAPreallocBlocker) { + PreallocatedProcessManager::RemoveBlocker(mRemoteType, this); + mIsAPreallocBlocker = false; + } MarkAsDead(); } @@ -2103,6 +2271,16 @@ bool ContentParent::LaunchSubprocessResolve(bool aIsSync, mPrefSerializer = nullptr; const auto launchResumeTS = TimeStamp::Now(); +#ifdef MOZ_GECKO_PROFILER + if (profiler_thread_is_being_profiled()) { + nsPrintfCString marker("Process start%s for %u", + mIsAPreallocBlocker ? " (immediate)" : "", + (unsigned int)ChildID()); + PROFILER_ADD_MARKER_WITH_PAYLOAD( + mIsAPreallocBlocker ? "Process Immediate Launch" : "Process Launch", + DOM, TextMarkerPayload, (marker, mLaunchTS, launchResumeTS)); + } +#endif if (!sCreatedFirstContentProcess) { nsCOMPtr obs = mozilla::services::GetObserverService(); @@ -2208,6 +2386,7 @@ ContentParent::ContentParent(ContentParent* aOpener, mLaunchYieldTS(mLaunchTS), mActivateTS(mLaunchTS), mOpener(aOpener), + mIsAPreallocBlocker(false), mRemoteType(aRemoteType), mChildID(gContentChildID++), mGeolocationWatchID(-1), @@ -2250,6 +2429,10 @@ ContentParent::ContentParent(ContentParent* aOpener, NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); bool isFile = mRemoteType.EqualsLiteral(FILE_REMOTE_TYPE); mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("CreateSubprocess: ContentParent %p mSubprocess %p handle %ld", this, + mSubprocess, + mSubprocess ? (long)mSubprocess->GetChildProcessHandle() : -1)); } ContentParent::~ContentParent() { @@ -2259,6 +2442,13 @@ ContentParent::~ContentParent() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (mIsAPreallocBlocker) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Removing blocker on ContentProcess destruction")); + PreallocatedProcessManager::RemoveBlocker(mRemoteType, this); + mIsAPreallocBlocker = false; + } + // We should be removed from all these lists in ActorDestroy. MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this)); if (IsForJSPlugin()) { @@ -2267,12 +2457,18 @@ ContentParent::~ContentParent() { } else { MOZ_ASSERT(!sBrowserContentParents || !sBrowserContentParents->Contains(mRemoteType) || - !sBrowserContentParents->Get(mRemoteType)->Contains(this)); + !sBrowserContentParents->Get(mRemoteType)->Contains(this) || + sCanLaunchSubprocesses == + false); // aka in shutdown - avoid timing issues } // Normally mSubprocess is destroyed in ActorDestroy, but that won't // happen if the process wasn't launched or if it failed to launch. if (mSubprocess) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("DestroySubprocess: ContentParent %p mSubprocess %p handle %ld", + this, mSubprocess, + mSubprocess ? (long)mSubprocess->GetChildProcessHandle() : -1)); mSubprocess->Destroy(); } } @@ -2280,6 +2476,8 @@ ContentParent::~ContentParent() { bool ContentParent::InitInternal(ProcessPriority aInitialPriority) { XPCOMInitData xpcomInit; + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("ContentParent::InitInternal: %p", (void*)this)); nsCOMPtr io(do_GetIOService()); MOZ_ASSERT(io, "No IO service?"); DebugOnly rv = io->GetOffline(&xpcomInit.isOffline()); @@ -2865,7 +3063,10 @@ mozilla::ipc::IPCResult ContentParent::RecvFirstIdle() { // When the ContentChild goes idle, it sends us a FirstIdle message // which we use as a good time to signal the PreallocatedProcessManager // that it can start allocating processes from now on. - PreallocatedProcessManager::RemoveBlocker(this); + if (mIsAPreallocBlocker) { + PreallocatedProcessManager::RemoveBlocker(mRemoteType, this); + mIsAPreallocBlocker = false; + } return IPC_OK(); } @@ -3363,6 +3564,10 @@ void ContentParent::KillHard(const char* aReason) { } if (mSubprocess) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("KillHard Subprocess: ContentParent %p mSubprocess %p handle %ld", + this, mSubprocess, + mSubprocess ? (long)mSubprocess->GetChildProcessHandle() : -1)); mSubprocess->SetAlreadyDead(); } @@ -5993,8 +6198,11 @@ void ContentParent::UnregisterRemoveWorkerActor() { } ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose, + ("UnregisterRemoveWorkerActor %p", this)); if (!cpm->GetBrowserParentCountByProcessId(ChildID()) && !ShouldKeepProcessAlive() && !TryToRecycle()) { + MarkAsDead(); MaybeAsyncSendShutDownMessage(); } } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 821e62f182cc..c169da655a74 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -54,15 +54,16 @@ // Process names as reported by about:memory are defined in // ContentChild:RecvRemoteType. Add your value there too or it will be called // "Web Content". +#define PREALLOC_REMOTE_TYPE "prealloc" #define DEFAULT_REMOTE_TYPE "web" -#define FISSION_WEB_REMOTE_TYPE "webIsolated" #define FILE_REMOTE_TYPE "file" #define EXTENSION_REMOTE_TYPE "extension" #define PRIVILEGEDABOUT_REMOTE_TYPE "privilegedabout" #define PRIVILEGEDMOZILLA_REMOTE_TYPE "privilegedmozilla" -#define WITH_COOP_COEP_REMOTE_TYPE_PREFIX "webCOOP+COEP=" -// This must start with the DEFAULT_REMOTE_TYPE above. +// These must start with the DEFAULT_REMOTE_TYPE above. +#define FISSION_WEB_REMOTE_TYPE "webIsolated" +#define WITH_COOP_COEP_REMOTE_TYPE_PREFIX "webCOOP+COEP=" #define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation" class nsConsoleService; @@ -161,6 +162,8 @@ class ContentParent final NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID) + static LogModule* GetLog(); + /** * Create a subprocess suitable for use later as a content process. */ @@ -786,6 +789,11 @@ class ContentParent final */ void RemoveFromList(); + /** + * Return if the process has an active worker or JSPlugin + */ + bool HasActiveWorkerOrJSPlugin(); + /** * Decide whether the process should be kept alive even when it would normally * be shut down, for example when all its tabs are closed. @@ -1343,6 +1351,8 @@ class ContentParent final TimeStamp mActivateTS; ContentParent* mOpener; + bool mIsAPreallocBlocker; // We called AddBlocker for this ContentParent + nsString mRemoteType; ContentParentId mChildID; diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp index 2baf5690d68a..0f7aff0256fc 100644 --- a/dom/ipc/PreallocatedProcessManager.cpp +++ b/dom/ipc/PreallocatedProcessManager.cpp @@ -11,10 +11,12 @@ #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_fission.h" #include "nsIPropertyBag2.h" #include "ProcessPriorityManager.h" #include "nsServiceManagerUtils.h" #include "nsIXULRuntime.h" +#include using namespace mozilla::hal; using namespace mozilla::dom; @@ -25,6 +27,8 @@ namespace mozilla { * PreallocatedProcessManager. */ class PreallocatedProcessManagerImpl final : public nsIObserver { + friend class PreallocatedProcessManager; + public: static PreallocatedProcessManagerImpl* Singleton(); @@ -34,13 +38,14 @@ class PreallocatedProcessManagerImpl final : public nsIObserver { // See comments on PreallocatedProcessManager for these methods. void AddBlocker(ContentParent* aParent); void RemoveBlocker(ContentParent* aParent); - already_AddRefed Take(); + already_AddRefed Take(const nsAString& aRemoteType); bool Provide(ContentParent* aParent); + void Erase(ContentParent* aParent); private: static const char* const kObserverTopics[]; - static mozilla::StaticRefPtr sSingleton; + static StaticRefPtr sSingleton; PreallocatedProcessManagerImpl(); ~PreallocatedProcessManagerImpl(); @@ -54,21 +59,31 @@ class PreallocatedProcessManagerImpl final : public nsIObserver { void AllocateNow(); void RereadPrefs(); - void Enable(); + void Enable(uint32_t aProcesses); void Disable(); - void CloseProcess(); + void CloseProcesses(); void ObserveProcessShutdown(nsISupports* aSubject); + bool IsEmpty() const { + return mPreallocatedProcesses.empty() && !mLaunchInProgress; + } + bool mEnabled; bool mShutdown; bool mLaunchInProgress; - RefPtr mPreallocatedProcess; - nsTHashtable mBlockers; - - bool IsEmpty() const { return !mPreallocatedProcess && !mLaunchInProgress; } + uint32_t mNumberPreallocs; + std::deque> mPreallocatedProcesses; + RefPtr mPreallocatedE10SProcess; // There can be only one + // Even if we have multiple PreallocatedProcessManagerImpls, we'll have + // one blocker counter + static uint32_t sNumBlockers; + TimeStamp mBlockingStartTime; }; +/* static */ +uint32_t PreallocatedProcessManagerImpl::sNumBlockers = 0; + const char* const PreallocatedProcessManagerImpl::kObserverTopics[] = { "ipc:content-shutdown", "memory-pressure", @@ -84,18 +99,22 @@ StaticRefPtr PreallocatedProcessManagerImpl* PreallocatedProcessManagerImpl::Singleton() { MOZ_ASSERT(NS_IsMainThread()); if (!sSingleton) { - sSingleton = new PreallocatedProcessManagerImpl(); + sSingleton = new PreallocatedProcessManagerImpl; sSingleton->Init(); - ClearOnShutdown(&sSingleton); + ClearOnShutdown(&sSingleton, + ShutdownPhase::ShutdownPostLastCycleCollection); } - return sSingleton; + // PreallocatedProcessManagers live until shutdown } NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() - : mEnabled(false), mShutdown(false), mLaunchInProgress(false) {} + : mEnabled(false), + mShutdown(false), + mLaunchInProgress(false), + mNumberPreallocs(1) {} PreallocatedProcessManagerImpl::~PreallocatedProcessManagerImpl() { // This shouldn't happen, because the promise callbacks should @@ -108,6 +127,11 @@ void PreallocatedProcessManagerImpl::Init() { // We have to respect processCount at all time. This is especially important // for testing. Preferences::AddStrongObserver(this, "dom.ipc.processCount"); + // A StaticPref, but we need to adjust the number of preallocated processes + // if the value goes up or down, so we need to run code on change. + Preferences::AddStrongObserver(this, + "dom.ipc.processPrelaunch.fission.number"); + nsCOMPtr os = services::GetObserverService(); MOZ_ASSERT(os); for (auto topic : kObserverTopics) { @@ -129,6 +153,9 @@ PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, !strcmp("profile-change-teardown", aTopic)) { Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled"); Preferences::RemoveObserver(this, "dom.ipc.processCount"); + Preferences::RemoveObserver(this, + "dom.ipc.processPrelaunch.fission.number"); + nsCOMPtr os = services::GetObserverService(); MOZ_ASSERT(os); for (auto topic : kObserverTopics) { @@ -136,11 +163,11 @@ PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, } // Let's prevent any new preallocated processes from starting. ContentParent // will handle the shutdown of the existing process and the - // mPreallocatedProcess reference will be cleared by the ClearOnShutdown of - // the manager singleton. + // mPreallocatedProcesses reference will be cleared by the ClearOnShutdown + // of the manager singleton. mShutdown = true; } else if (!strcmp("memory-pressure", aTopic)) { - CloseProcess(); + CloseProcesses(); } else { MOZ_ASSERT_UNREACHABLE("Unknown topic"); } @@ -151,47 +178,84 @@ PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, void PreallocatedProcessManagerImpl::RereadPrefs() { if (mozilla::BrowserTabsRemoteAutostart() && Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { - Enable(); + int32_t number = 1; + if (StaticPrefs::fission_autostart()) { + number = StaticPrefs::dom_ipc_processPrelaunch_fission_number(); + } + if (number >= 0) { + Enable(number); + // We have one prealloc queue for all types except File now + if (static_cast(number) < mPreallocatedProcesses.size()) { + CloseProcesses(); + } + } } else { Disable(); } - - if (ContentParent::IsMaxProcessCountReached( - NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) { - CloseProcess(); - } } -already_AddRefed PreallocatedProcessManagerImpl::Take() { +already_AddRefed PreallocatedProcessManagerImpl::Take( + const nsAString& aRemoteType) { if (!mEnabled || mShutdown) { return nullptr; } - - if (mPreallocatedProcess) { - // The preallocated process is taken. Let's try to start up a new one soon. - ProcessPriorityManager::SetProcessPriority(mPreallocatedProcess, - PROCESS_PRIORITY_FOREGROUND); - AllocateOnIdle(); + RefPtr process; + if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE)) { + // we can recycle processes via Provide() for e10s only + process = mPreallocatedE10SProcess.forget(); + if (process) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Reuse " DEFAULT_REMOTE_TYPE " process %p", + mPreallocatedE10SProcess.get())); + } } - - return mPreallocatedProcess.forget(); + if (!process && !mPreallocatedProcesses.empty()) { + process = mPreallocatedProcesses.front().forget(); + mPreallocatedProcesses.pop_front(); // holds a nullptr + // We took a preallocated process. Let's try to start up a new one + // soon. + AllocateOnIdle(); + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Use " PREALLOC_REMOTE_TYPE " process %p", process.get())); + } + if (process) { + ProcessPriorityManager::SetProcessPriority(process, + PROCESS_PRIORITY_FOREGROUND); + } + return process.forget(); } bool PreallocatedProcessManagerImpl::Provide(ContentParent* aParent) { + MOZ_DIAGNOSTIC_ASSERT( + aParent->GetRemoteType().EqualsLiteral(DEFAULT_REMOTE_TYPE)); + // This will take the already-running process even if there's a // launch in progress; if that process hasn't been taken by the // time the launch completes, the new process will be shut down. - if (mEnabled && !mShutdown && !mPreallocatedProcess) { - mPreallocatedProcess = aParent; + if (mEnabled && !mShutdown && !mPreallocatedE10SProcess) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Store for reuse " DEFAULT_REMOTE_TYPE " process %p", aParent)); + ProcessPriorityManager::SetProcessPriority(aParent, + PROCESS_PRIORITY_BACKGROUND); + mPreallocatedE10SProcess = aParent; + return true; } // We might get a call from both NotifyTabDestroying and NotifyTabDestroyed // with the same ContentParent. Returning true here for both calls is // important to avoid the cached process to be destroyed. - return aParent == mPreallocatedProcess; + return aParent == mPreallocatedE10SProcess; } -void PreallocatedProcessManagerImpl::Enable() { +void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) { + // Ensure this ContentParent isn't cached + if (mPreallocatedE10SProcess == aParent) { + mPreallocatedE10SProcess = nullptr; + } +} + +void PreallocatedProcessManagerImpl::Enable(uint32_t aProcesses) { + mNumberPreallocs = aProcesses; if (mEnabled) { return; } @@ -201,13 +265,13 @@ void PreallocatedProcessManagerImpl::Enable() { } void PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent) { - uint64_t childID = aParent->ChildID(); - MOZ_ASSERT(!mBlockers.Contains(childID)); - mBlockers.PutEntry(childID); + if (sNumBlockers == 0) { + mBlockingStartTime = TimeStamp::Now(); + } + sNumBlockers++; } void PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent) { - uint64_t childID = aParent->ChildID(); // This used to assert that the blocker existed, but preallocated // processes aren't blockers anymore because it's not useful and // interferes with async launch, and it's simpler if content @@ -215,16 +279,28 @@ void PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent) { // (And preallocated processes can't AddBlocker when taken, because // it's possible for a short-lived process to be recycled through // Provide() and Take() before reaching RecvFirstIdle.) - mBlockers.RemoveEntry(childID); - if (IsEmpty() && mBlockers.IsEmpty()) { - AllocateAfterDelay(); + + MOZ_DIAGNOSTIC_ASSERT(sNumBlockers > 0); + sNumBlockers--; + if (sNumBlockers == 0) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("Blocked preallocation for %fms", + (TimeStamp::Now() - mBlockingStartTime).ToMilliseconds())); + PROFILER_ADD_TEXT_MARKER( + "Process", NS_LITERAL_CSTRING("Blocked preallocation"), + JS::ProfilingCategoryPair::DOM, mBlockingStartTime, TimeStamp::Now()); + if (IsEmpty()) { + AllocateAfterDelay(); + } } } bool PreallocatedProcessManagerImpl::CanAllocate() { - return mEnabled && mBlockers.IsEmpty() && IsEmpty() && !mShutdown && - !ContentParent::IsMaxProcessCountReached( - NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); + return mEnabled && sNumBlockers == 0 && + mPreallocatedProcesses.size() < mNumberPreallocs && !mShutdown && + (StaticPrefs::fission_autostart() || + !ContentParent::IsMaxProcessCountReached( + NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))); } void PreallocatedProcessManagerImpl::AllocateAfterDelay() { @@ -251,7 +327,7 @@ void PreallocatedProcessManagerImpl::AllocateOnIdle() { void PreallocatedProcessManagerImpl::AllocateNow() { if (!CanAllocate()) { - if (mEnabled && !mShutdown && IsEmpty() && !mBlockers.IsEmpty()) { + if (mEnabled && !mShutdown && IsEmpty() && sNumBlockers > 0) { // If it's too early to allocate a process let's retry later. AllocateAfterDelay(); } @@ -267,7 +343,22 @@ void PreallocatedProcessManagerImpl::AllocateNow() { [self, this](const RefPtr& process) { mLaunchInProgress = false; if (CanAllocate()) { - mPreallocatedProcess = process; + // slight perf reason for push_back - while the cpu cache + // probably has stack/etc associated with the most recent + // process created, we don't know that it has finished startup. + // If we added it to the queue on completion of startup, we + // could push_front it, but that would require a bunch more + // logic. + mPreallocatedProcesses.push_back(process); + MOZ_LOG( + ContentParent::GetLog(), LogLevel::Debug, + ("Preallocated = %lu of %d processes", + (unsigned long)mPreallocatedProcesses.size(), mNumberPreallocs)); + + // Continue prestarting processes if needed + if (mPreallocatedProcesses.size() < mNumberPreallocs) { + AllocateOnIdle(); + } } else { process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE); } @@ -284,13 +375,20 @@ void PreallocatedProcessManagerImpl::Disable() { } mEnabled = false; - CloseProcess(); + CloseProcesses(); } -void PreallocatedProcessManagerImpl::CloseProcess() { - if (mPreallocatedProcess) { - mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE); - mPreallocatedProcess = nullptr; +void PreallocatedProcessManagerImpl::CloseProcesses() { + while (!mPreallocatedProcesses.empty()) { + RefPtr process(mPreallocatedProcesses.front().forget()); + mPreallocatedProcesses.pop_front(); + process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE); + // drop ref and let it free + } + if (mPreallocatedE10SProcess) { + mPreallocatedE10SProcess->ShutDownProcess( + ContentParent::SEND_SHUTDOWN_MESSAGE); + mPreallocatedE10SProcess = nullptr; } } @@ -303,11 +401,14 @@ void PreallocatedProcessManagerImpl::ObserveProcessShutdown( props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); - if (mPreallocatedProcess && childID == mPreallocatedProcess->ChildID()) { - mPreallocatedProcess = nullptr; + for (auto it = mPreallocatedProcesses.begin(); + it != mPreallocatedProcesses.end(); it++) { + if (childID == (*it)->ChildID()) { + mPreallocatedProcesses.erase(it); + break; + } } - - mBlockers.RemoveEntry(childID); + // The ContentParent is responsible for removing itself as a blocker } inline PreallocatedProcessManagerImpl* GetPPMImpl() { @@ -315,18 +416,29 @@ inline PreallocatedProcessManagerImpl* GetPPMImpl() { } /* static */ -void PreallocatedProcessManager::AddBlocker(ContentParent* aParent) { +void PreallocatedProcessManager::AddBlocker(const nsAString& aRemoteType, + ContentParent* aParent) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("AddBlocker: %s %p (sNumBlockers=%d)", + NS_ConvertUTF16toUTF8(aRemoteType).get(), aParent, + PreallocatedProcessManagerImpl::sNumBlockers)); GetPPMImpl()->AddBlocker(aParent); } /* static */ -void PreallocatedProcessManager::RemoveBlocker(ContentParent* aParent) { +void PreallocatedProcessManager::RemoveBlocker(const nsAString& aRemoteType, + ContentParent* aParent) { + MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, + ("RemoveBlocker: %s %p (sNumBlockers=%d)", + NS_ConvertUTF16toUTF8(aRemoteType).get(), aParent, + PreallocatedProcessManagerImpl::sNumBlockers)); GetPPMImpl()->RemoveBlocker(aParent); } /* static */ -already_AddRefed PreallocatedProcessManager::Take() { - return GetPPMImpl()->Take(); +already_AddRefed PreallocatedProcessManager::Take( + const nsAString& aRemoteType) { + return GetPPMImpl()->Take(aRemoteType); } /* static */ @@ -334,4 +446,9 @@ bool PreallocatedProcessManager::Provide(ContentParent* aParent) { return GetPPMImpl()->Provide(aParent); } +/* static */ +void PreallocatedProcessManager::Erase(ContentParent* aParent) { + GetPPMImpl()->Erase(aParent); +} + } // namespace mozilla diff --git a/dom/ipc/PreallocatedProcessManager.h b/dom/ipc/PreallocatedProcessManager.h index a4896f609e04..d22a3900a3a8 100644 --- a/dom/ipc/PreallocatedProcessManager.h +++ b/dom/ipc/PreallocatedProcessManager.h @@ -37,23 +37,30 @@ class PreallocatedProcessManager final { * background. To avoid that, the PreallocatedProcessManager won't start up * any processes while there is a blocker active. */ - static void AddBlocker(ContentParent* aParent); - static void RemoveBlocker(ContentParent* aParent); + static void AddBlocker(const nsAString& aRemoteType, ContentParent* aParent); + static void RemoveBlocker(const nsAString& aRemoteType, + ContentParent* aParent); /** - * Take the preallocated process, if we have one. If we don't have one, this - * returns null. + * Take the preallocated process, if we have one, or a recycled + * process cached via Provide(). Currently we only cache + * DEFAULT_REMOTE_TYPE ('web') processes and only reuse them for that + * type. If we don't have a process to return (cached or preallocated), + * 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. + * If we use a preallocated process, it will schedule the start of + * another on Idle (AllocateOnIdle()). */ - static already_AddRefed Take(); + static already_AddRefed Take(const nsAString& aRemoteType); + /** + * Cache a process (currently only DEFAULT_REMOTE_TYPE) for reuse later + * via Take(). Returns true if we cached the process, and false if + * another process is already cached (so the caller knows to destroy it). + * This takes a reference to the ContentParent if it is cached. + */ static bool Provide(ContentParent* aParent); + static void Erase(ContentParent* aParent); private: PreallocatedProcessManager(); diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp index 30c9980eb450..be982357ec8b 100644 --- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -808,6 +808,8 @@ void ParticularProcessPriorityManager::TabActivityChanged( void ParticularProcessPriorityManager::ShutDown() { MOZ_ASSERT(mContentParent); + LOGP("shutdown for %p (mContentParent %p)", this, mContentParent); + UnregisterWakeLockObserver(this); if (mResetPriorityTimer) { @@ -942,6 +944,7 @@ void ProcessPriorityManager::Init() { void ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent, ProcessPriority aPriority) { MOZ_ASSERT(aContentParent); + MOZ_ASSERT(aContentParent->Pid() != -1); ProcessPriorityManagerImpl* singleton = ProcessPriorityManagerImpl::GetSingleton(); diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 5269dd2dc11a..e55b33d86dd9 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -1904,7 +1904,7 @@ value: false mirror: always -# Process launch delay (im milliseconds). +# Process launch delay (in milliseconds). - name: dom.ipc.processPrelaunch.delayMs type: uint32_t # This number is fairly arbitrary ... the intention is to put off @@ -1913,6 +1913,13 @@ value: 1000 mirror: always +# Process preallocation cache +# Only used in fission; in e10s we use 1 always +- name: dom.ipc.processPrelaunch.fission.number + type: uint32_t + value: 3 + mirror: always + - name: dom.ipc.processPriorityManager.enabled type: bool value: false diff --git a/widget/ProcInfo.h b/widget/ProcInfo.h index 5d5fe18dbc41..4628b9e81650 100644 --- a/widget/ProcInfo.h +++ b/widget/ProcInfo.h @@ -18,7 +18,8 @@ class GeckoChildProcessHost; } // Process types. When updating this enum, please make sure to update -// WebIDLProcType and ProcTypeToWebIDL to mirror the changes. +// WebIDLProcType, ChromeUtils::RequestProcInfo and ProcTypeToWebIDL to +// mirror the changes. enum class ProcType { // These must match the ones in ContentParent.h, and E10SUtils.jsm Web, @@ -42,6 +43,7 @@ enum class ProcType { #ifdef MOZ_ENABLE_FORKSERVER ForkServer, #endif + Preallocated, // Unknown type of process Unknown, Max = Unknown,