зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1602757: add preallocation cache for webIsolated (fission) processes r=nika,smaug
Differential Revision: https://phabricator.services.mozilla.com/D69589
This commit is contained in:
Родитель
3d17f898c9
Коммит
4899b7707e
|
@ -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<Promise> 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");
|
||||
}
|
||||
|
|
|
@ -517,6 +517,7 @@ enum WebIDLProcType {
|
|||
#ifdef MOZ_ENABLE_FORKSERVER
|
||||
"forkServer",
|
||||
#endif
|
||||
"preallocated",
|
||||
"unknown",
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<nsStringHashKey, nsTArray<ContentParent*>>*
|
||||
ContentParent::sBrowserContentParents;
|
||||
|
||||
|
@ -632,8 +644,10 @@ static const char* sObserverTopics[] = {
|
|||
/*static*/ RefPtr<ContentParent::LaunchPromise>
|
||||
ContentParent::PreallocateProcess() {
|
||||
RefPtr<ContentParent> 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<ContentParent*>& contentParents =
|
||||
GetOrCreatePool(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
||||
#ifdef DEBUG
|
||||
int num = 0;
|
||||
for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsTArray<ContentParent*>* 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<ContentParent*> toRelease;
|
||||
for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsTArray<ContentParent*>* contentParents = iter.Data().get();
|
||||
|
||||
// Shuting down these processes will change the array so let's use another
|
||||
// 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) {
|
||||
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> ContentParent::GetUsedBrowserProcess(
|
|||
// If the provider returned an existing ContentParent, use that one.
|
||||
if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
|
||||
RefPtr<ContentParent> 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> ContentParent::GetUsedBrowserProcess(
|
|||
RefPtr<ContentParent> 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<ContentParent> 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<ContentParent*>& 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).
|
||||
|
||||
// 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, until we don't take it back.
|
||||
// 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<nsIObserverService> 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<nsIIOService> io(do_GetIOService());
|
||||
MOZ_ASSERT(io, "No IO service?");
|
||||
DebugOnly<nsresult> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <deque>
|
||||
|
||||
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<ContentParent> Take();
|
||||
already_AddRefed<ContentParent> Take(const nsAString& aRemoteType);
|
||||
bool Provide(ContentParent* aParent);
|
||||
void Erase(ContentParent* aParent);
|
||||
|
||||
private:
|
||||
static const char* const kObserverTopics[];
|
||||
|
||||
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
|
||||
static StaticRefPtr<PreallocatedProcessManagerImpl> 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<ContentParent> mPreallocatedProcess;
|
||||
nsTHashtable<nsUint64HashKey> mBlockers;
|
||||
|
||||
bool IsEmpty() const { return !mPreallocatedProcess && !mLaunchInProgress; }
|
||||
uint32_t mNumberPreallocs;
|
||||
std::deque<RefPtr<ContentParent>> mPreallocatedProcesses;
|
||||
RefPtr<ContentParent> 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* 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<nsIObserverService> 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<nsIObserverService> 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<uint64_t>(number) < mPreallocatedProcesses.size()) {
|
||||
CloseProcesses();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
|
||||
if (ContentParent::IsMaxProcessCountReached(
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
|
||||
CloseProcess();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take() {
|
||||
already_AddRefed<ContentParent> 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<ContentParent> 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()) {
|
||||
|
||||
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 &&
|
||||
return mEnabled && sNumBlockers == 0 &&
|
||||
mPreallocatedProcesses.size() < mNumberPreallocs && !mShutdown &&
|
||||
(StaticPrefs::fission_autostart() ||
|
||||
!ContentParent::IsMaxProcessCountReached(
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
||||
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<ContentParent>& 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<ContentParent> 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<ContentParent> PreallocatedProcessManager::Take() {
|
||||
return GetPPMImpl()->Take();
|
||||
already_AddRefed<ContentParent> 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
|
||||
|
|
|
@ -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<ContentParent> Take();
|
||||
static already_AddRefed<ContentParent> 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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче