mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1728331 - Part 4: Make ContentParent KeepAlives explicit with RAII references, r=smaug,dom-worker-reviewers,asuth
This is a fairly significant patch, however it would be difficult to break it down into smaller patches: 1) The various mechanisms used to manage ContentParent lifecycles have been merged together into a common "KeepAlive" system. A process will begin shutdown when its keepalive count reaches 0. (though it will still wait for all BrowserParents to also be dead before sending the actual shutdown message as before). This replaces a number of bespoke systems for tracking BrowserParent instances in different lifecycle states, remote workers, ongoing process switches, and preallocated processes. 2) KeepAlives are now managed automatically by a UniquePtr variant (Unique[Threadsafe]ContentParentKeepAlive). This makes the hand-off over KeepAlive lifecycles explicit, even for workers. 3) All KeepAlives are now keyed by a BrowserId, which will be 0 for keepalives not associated with a specific tab. This allows the new process selection logic to count all tabs other than the one being navigated when deciding which process to use. 4) The process switching logic now tracks it's KeepAlive with a BrowserId, meaning that ongoing process switches are considered when performing process selection, even if the BrowserParent hasn't been created yet. Differential Revision: https://phabricator.services.mozilla.com/D213338
This commit is contained in:
parent
5ce53b86d7
commit
d6e6375a87
@ -186,8 +186,8 @@ add_task(async function navigate_around() {
|
||||
// This pref is only accessed in automation to speed up tests.
|
||||
knownProblematicPrefs["dom.ipc.keepProcessesAlive.webIsolated.perOrigin"] =
|
||||
{
|
||||
min: 100,
|
||||
max: 102,
|
||||
min: 50,
|
||||
max: 51,
|
||||
};
|
||||
if (AppConstants.platform == "linux") {
|
||||
// The following sandbox pref is covered by
|
||||
|
@ -1738,10 +1738,10 @@ void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mContentParent) {
|
||||
if (mContentParentKeepAlive) {
|
||||
// If our new content process is still unloading from a previous process
|
||||
// switch, wait for that unload to complete before continuing.
|
||||
auto found = mTarget->FindUnloadingHost(mContentParent->ChildID());
|
||||
auto found = mTarget->FindUnloadingHost(mContentParentKeepAlive->ChildID());
|
||||
if (found != mTarget->mUnloadingHosts.end()) {
|
||||
found->mCallbacks.AppendElement(
|
||||
[self = RefPtr{this}]() { self->ProcessReady(); });
|
||||
@ -1791,7 +1791,8 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
|
||||
"non-remote iframes");
|
||||
|
||||
// Abort if our ContentParent died while process switching.
|
||||
if (mContentParent && NS_WARN_IF(mContentParent->IsShuttingDown())) {
|
||||
if (mContentParentKeepAlive &&
|
||||
NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -1842,14 +1843,14 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
|
||||
// Some frontend code checks the value of the `remote` attribute on the
|
||||
// browser to determine if it is remote, so update the value.
|
||||
browserElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote,
|
||||
mContentParent ? u"true"_ns : u"false"_ns,
|
||||
mContentParentKeepAlive ? u"true"_ns : u"false"_ns,
|
||||
/* notify */ true);
|
||||
|
||||
// The process has been created, hand off to nsFrameLoaderOwner to finish
|
||||
// the process switch.
|
||||
ErrorResult error;
|
||||
frameLoaderOwner->ChangeRemotenessToProcess(mContentParent, mOptions,
|
||||
mSpecificGroup, error);
|
||||
frameLoaderOwner->ChangeRemotenessToProcess(mContentParentKeepAlive.get(),
|
||||
mOptions, mSpecificGroup, error);
|
||||
if (error.Failed()) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
@ -1865,7 +1866,7 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
|
||||
RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
|
||||
RefPtr<BrowserParent> newBrowser = frameLoader->GetBrowserParent();
|
||||
if (!newBrowser) {
|
||||
if (mContentParent) {
|
||||
if (mContentParentKeepAlive) {
|
||||
// Failed to create the BrowserParent somehow! Abort the process switch
|
||||
// attempt.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
@ -1904,7 +1905,7 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mContentParent)) {
|
||||
if (NS_WARN_IF(!mContentParentKeepAlive)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -1920,8 +1921,8 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
|
||||
|
||||
// If we're creating a new remote browser, and the host process is already
|
||||
// dead, abort the process switch.
|
||||
if (mContentParent != embedderBrowser->Manager() &&
|
||||
NS_WARN_IF(mContentParent->IsShuttingDown())) {
|
||||
if (mContentParentKeepAlive != embedderBrowser->Manager() &&
|
||||
NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -1947,11 +1948,11 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
|
||||
}
|
||||
|
||||
// Update which process is considered the current owner
|
||||
target->SetOwnerProcessId(mContentParent->ChildID());
|
||||
target->SetOwnerProcessId(mContentParentKeepAlive->ChildID());
|
||||
|
||||
// If we're switching from remote to local, we don't need to create a
|
||||
// BrowserBridge, and can instead perform the switch directly.
|
||||
if (mContentParent == embedderBrowser->Manager()) {
|
||||
if (mContentParentKeepAlive == embedderBrowser->Manager()) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(
|
||||
mPendingSwitchId,
|
||||
"We always have a PendingSwitchId, except for print-preview loads, "
|
||||
@ -1987,8 +1988,9 @@ nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
|
||||
// Create and initialize our new BrowserBridgeParent.
|
||||
TabId tabId(nsContentUtils::GenerateTabId());
|
||||
RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent();
|
||||
nsresult rv = bridge->InitWithProcess(embedderBrowser, mContentParent,
|
||||
windowInit, chromeFlags, tabId);
|
||||
nsresult rv =
|
||||
bridge->InitWithProcess(embedderBrowser, mContentParentKeepAlive.get(),
|
||||
windowInit, chromeFlags, tabId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// If we've already destroyed our previous document, make a best-effort
|
||||
// attempt to recover from this failure and show the crashed tab UI. We only
|
||||
@ -2053,11 +2055,8 @@ void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
|
||||
}
|
||||
|
||||
// When this PendingRemotenessChange was created, it was given a
|
||||
// `mContentParent`.
|
||||
if (mContentParent) {
|
||||
mContentParent->RemoveKeepAlive();
|
||||
mContentParent = nullptr;
|
||||
}
|
||||
// `mContentParentKeepAlive`.
|
||||
mContentParentKeepAlive = nullptr;
|
||||
|
||||
// If we were given a specific group, stop keeping that group alive manually.
|
||||
if (mSpecificGroup) {
|
||||
@ -2078,8 +2077,9 @@ CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
|
||||
mOptions(aOptions) {}
|
||||
|
||||
CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
|
||||
MOZ_ASSERT(!mPromise && !mTarget && !mContentParent && !mSpecificGroup,
|
||||
"should've already been Cancel() or Complete()-ed");
|
||||
MOZ_ASSERT(
|
||||
!mPromise && !mTarget && !mContentParentKeepAlive && !mSpecificGroup,
|
||||
"should've already been Cancel() or Complete()-ed");
|
||||
}
|
||||
|
||||
BrowserParent* CanonicalBrowsingContext::GetBrowserParent() const {
|
||||
@ -2226,8 +2226,8 @@ CanonicalBrowsingContext::ChangeRemoteness(
|
||||
|
||||
// Switching to local, so we don't need to create a new process, and will
|
||||
// instead use our embedder process.
|
||||
change->mContentParent = embedderBrowser->Manager();
|
||||
change->mContentParent->AddKeepAlive();
|
||||
change->mContentParentKeepAlive =
|
||||
embedderBrowser->Manager()->AddKeepAlive(BrowserId());
|
||||
change->ProcessLaunched();
|
||||
return promise.forget();
|
||||
}
|
||||
@ -2245,8 +2245,8 @@ CanonicalBrowsingContext::ChangeRemoteness(
|
||||
if (existingProcess && !existingProcess->IsShuttingDown() &&
|
||||
aOptions.mReplaceBrowsingContext &&
|
||||
aOptions.mRemoteType == existingProcess->GetRemoteType()) {
|
||||
change->mContentParent = existingProcess;
|
||||
change->mContentParent->AddKeepAlive();
|
||||
change->mContentParentKeepAlive =
|
||||
existingProcess->AddKeepAlive(BrowserId());
|
||||
change->ProcessLaunched();
|
||||
return promise.forget();
|
||||
}
|
||||
@ -2265,25 +2265,28 @@ CanonicalBrowsingContext::ChangeRemoteness(
|
||||
bool preferUsed =
|
||||
StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop();
|
||||
|
||||
change->mContentParent = ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
/* aRemoteType = */ aOptions.mRemoteType,
|
||||
/* aGroup = */ finalGroup,
|
||||
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
|
||||
/* aPreferUsed = */ preferUsed);
|
||||
if (!change->mContentParent) {
|
||||
change->mContentParentKeepAlive =
|
||||
ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
/* aRemoteType = */ aOptions.mRemoteType,
|
||||
/* aGroup = */ finalGroup,
|
||||
/* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND,
|
||||
/* aPreferUsed = */ preferUsed,
|
||||
/* aBrowserId */ BrowserId());
|
||||
if (!change->mContentParentKeepAlive) {
|
||||
change->Cancel(NS_ERROR_FAILURE);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Add a KeepAlive used by this ContentParent, which will be cleared when
|
||||
// the change is complete. This should prevent the process dying before
|
||||
// we're ready to use it.
|
||||
change->mContentParent->AddKeepAlive();
|
||||
if (change->mContentParent->IsLaunching()) {
|
||||
change->mContentParent->WaitForLaunchAsync()->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[change](ContentParent*) { change->ProcessLaunched(); },
|
||||
[change]() { change->Cancel(NS_ERROR_FAILURE); });
|
||||
if (change->mContentParentKeepAlive->IsLaunching()) {
|
||||
change->mContentParentKeepAlive
|
||||
->WaitForLaunchAsync(/* aPriority */ hal::PROCESS_PRIORITY_FOREGROUND,
|
||||
/* aBrowserId */ BrowserId())
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[change](UniqueContentParentKeepAlive) {
|
||||
change->ProcessLaunched();
|
||||
},
|
||||
[change]() { change->Cancel(NS_ERROR_FAILURE); });
|
||||
} else {
|
||||
change->ProcessLaunched();
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/dom/SessionHistoryEntry.h"
|
||||
#include "mozilla/dom/SessionStoreRestoreData.h"
|
||||
#include "mozilla/dom/SessionStoreUtils.h"
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
@ -460,7 +461,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
||||
|
||||
RefPtr<CanonicalBrowsingContext> mTarget;
|
||||
RefPtr<RemotenessPromise::Private> mPromise;
|
||||
RefPtr<ContentParent> mContentParent;
|
||||
UniqueContentParentKeepAlive mContentParentKeepAlive;
|
||||
RefPtr<BrowsingContextGroup> mSpecificGroup;
|
||||
|
||||
bool mProcessReady = false;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||
#include "mozilla/dom/PaymentRequestParent.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/dom/PointerEventHandler.h"
|
||||
#include "mozilla/dom/BrowserBridgeParent.h"
|
||||
#include "mozilla/dom/RemoteDragStartData.h"
|
||||
@ -64,6 +65,7 @@
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsFocusManager.h"
|
||||
@ -328,6 +330,10 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
|
||||
// so need to initialize it before IPC does.
|
||||
SetManager(aManager);
|
||||
|
||||
// Add a KeepAlive for this BrowserParent upon creation.
|
||||
mContentParentKeepAlive =
|
||||
aManager->TryAddKeepAlive(aBrowsingContext->BrowserId());
|
||||
|
||||
RequestingAccessKeyEventData::OnBrowserParentCreated();
|
||||
|
||||
// When the input event queue is disabled, we don't need to handle the case
|
||||
@ -736,25 +742,28 @@ void BrowserParent::Destroy() {
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
// The following sequence assumes that the keepalive state does not change
|
||||
// between the calls, but our ThreadsafeHandle might be accessed from other
|
||||
// threads in the meantime.
|
||||
RecursiveMutexAutoLock lock(Manager()->ThreadsafeHandleMutex());
|
||||
// If this fails, it's most likely due to a content-process crash, and
|
||||
// auto-cleanup will kick in. Otherwise, the child side will destroy itself
|
||||
// and send back __delete__().
|
||||
(void)SendDestroy();
|
||||
mIsDestroyed = true;
|
||||
|
||||
// If we are shutting down everything or we know to be the last
|
||||
// BrowserParent, signal the impending shutdown early to the content process
|
||||
// to avoid to run the SendDestroy before we know we are ExpectingShutdown.
|
||||
Manager()->NotifyTabWillDestroy();
|
||||
|
||||
// If this fails, it's most likely due to a content-process crash, and
|
||||
// auto-cleanup will kick in. Otherwise, the child side will destroy itself
|
||||
// and send back __delete__().
|
||||
(void)SendDestroy();
|
||||
mIsDestroyed = true;
|
||||
|
||||
Manager()->NotifyTabDestroying();
|
||||
}
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
// We're beginning to destroy this BrowserParent. Immediately drop the
|
||||
// keepalive. This can start the shutdown timer, however the ShutDown message
|
||||
// will wait for the BrowserParent to be fully destroyed.
|
||||
//
|
||||
// NOTE: We intentionally skip this step on Android, keeping the KeepAlive
|
||||
// active until the BrowserParent is fully destroyed:
|
||||
// 1. Android has a fixed upper bound on the number of content processes, so
|
||||
// we prefer to re-use them whenever possible (as opposed to letting an
|
||||
// old process wind down while we launch a new one). This restriction will
|
||||
// be relaxed after bug 1565196.
|
||||
// 2. GeckoView always hard-kills content processes (and if it does not,
|
||||
// Android itself will), so we don't concern ourselves with the ForceKill
|
||||
// timer either.
|
||||
mContentParentKeepAlive = nullptr;
|
||||
#endif
|
||||
|
||||
// This `AddKeepAlive` will be cleared if `mMarkedDestroying` is set in
|
||||
// `ActorDestroy`. Out of caution, we don't add the `KeepAlive` if our IPC
|
||||
@ -788,7 +797,20 @@ mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected(
|
||||
}
|
||||
|
||||
void BrowserParent::ActorDestroy(ActorDestroyReason why) {
|
||||
Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying);
|
||||
// Need to close undeleted ContentPermissionRequestParents before tab is
|
||||
// closed.
|
||||
// FIXME: Why is PContentPermissionRequest not managed by PBrowser?
|
||||
nsTArray<PContentPermissionRequestParent*> parentArray =
|
||||
nsContentPermissionUtils::GetContentPermissionRequestParentById(mTabId);
|
||||
for (auto& permissionRequestParent : parentArray) {
|
||||
Unused << PContentPermissionRequestParent::Send__delete__(
|
||||
permissionRequestParent);
|
||||
}
|
||||
|
||||
// Ensure the ContentParentKeepAlive has been cleared when the actor is
|
||||
// destroyed, and re-check if it's time to send the ShutDown message.
|
||||
mContentParentKeepAlive = nullptr;
|
||||
Manager()->MaybeBeginShutDown();
|
||||
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
if (cpm) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/dom/BrowserBridgeParent.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
#include "mozilla/dom/TabContext.h"
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
#include "mozilla/dom/VsyncParent.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "mozilla/layout/RemoteLayerTreeOwner.h"
|
||||
@ -872,6 +873,12 @@ class BrowserParent final : public PBrowserParent,
|
||||
// non-null.
|
||||
BrowserHost* mBrowserHost;
|
||||
|
||||
// KeepAlive for the containing process.
|
||||
// NOTE: While this is a strong reference to ContentParent, which is
|
||||
// cycle-collected, it is cleared as the BrowserParent's IPC connection is
|
||||
// destroyed, so does not need to be cycle-collected.
|
||||
UniqueContentParentKeepAlive mContentParentKeepAlive;
|
||||
|
||||
ContentCacheInParent mContentCache;
|
||||
|
||||
layout::RemoteLayerTreeOwner mRemoteLayerTreeOwner;
|
||||
|
@ -588,10 +588,13 @@ void ContentParent_NotifyUpdatedDictionaries() {
|
||||
|
||||
// PreallocateProcess is called by the PreallocatedProcessManager.
|
||||
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::MakePreallocProcess() {
|
||||
/*static*/ UniqueContentParentKeepAlive ContentParent::MakePreallocProcess() {
|
||||
RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
|
||||
return process.forget();
|
||||
if (NS_WARN_IF(!process->BeginSubprocessLaunch(PROCESS_PRIORITY_PREALLOC))) {
|
||||
process->LaunchSubprocessReject();
|
||||
return nullptr;
|
||||
}
|
||||
return process->AddKeepAlive(/* aBrowserId */ 0);
|
||||
}
|
||||
|
||||
/*static*/
|
||||
@ -745,54 +748,48 @@ void ContentParent::ReleaseCachedProcesses() {
|
||||
}
|
||||
|
||||
for (const auto& cp : fixArray) {
|
||||
// Ensure the process cannot be claimed between check and MarkAsDead.
|
||||
RecursiveMutexAutoLock lock(cp->ThreadsafeHandleMutex());
|
||||
|
||||
if (cp->ManagedPBrowserParent().Count() == 0 && !cp->HasActiveWorker() &&
|
||||
cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
(" Shutdown %p (%s)", cp.get(), cp->mRemoteType.get()));
|
||||
|
||||
PreallocatedProcessManager::Erase(cp);
|
||||
// Make sure we don't select this process for new tabs or workers.
|
||||
cp->MarkAsDead();
|
||||
// Start a soft shutdown.
|
||||
cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
|
||||
if (cp->MaybeBeginShutDown(/* aIgnoreKeepAlivePref */ true)) {
|
||||
// Make sure that this process is no longer accessible from JS by its
|
||||
// message manager.
|
||||
cp->ShutDownMessageManager();
|
||||
} else {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
(" Skipping %p (%s), count %d, HasActiveWorker %d", cp.get(),
|
||||
cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
|
||||
cp->HasActiveWorker()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<ContentParent> ContentParent::MinTabSelect(
|
||||
const nsTArray<ContentParent*>& aContentParents,
|
||||
int32_t aMaxContentParents) {
|
||||
const nsTArray<ContentParent*>& aContentParents, int32_t aMaxContentParents,
|
||||
uint64_t aBrowserId) {
|
||||
uint32_t maxSelectable =
|
||||
std::min(static_cast<uint32_t>(aContentParents.Length()),
|
||||
static_cast<uint32_t>(aMaxContentParents));
|
||||
uint32_t min = INT_MAX;
|
||||
RefPtr<ContentParent> candidate;
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
|
||||
for (uint32_t i = 0; i < maxSelectable; i++) {
|
||||
ContentParent* p = aContentParents[i];
|
||||
MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
|
||||
if (p->IsShuttingDown()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore processes that were slated for removal but not yet removed from
|
||||
// the pool (see also GetUsedBrowserProcess and BlockShutdown).
|
||||
if (!p->IsShuttingDown()) {
|
||||
uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
|
||||
if (tabCount < min) {
|
||||
candidate = p;
|
||||
min = tabCount;
|
||||
}
|
||||
// Check how many other tabs are already hosted by this process. Ignore
|
||||
// keepalives without a BrowserId as well as keepalives corresponding to
|
||||
// `aBrowserId` when doing this calculation.
|
||||
ThreadsafeContentParentHandle* handle = p->ThreadsafeHandle();
|
||||
RecursiveMutexAutoLock lock(handle->mMutex);
|
||||
uint32_t keepAliveCount = handle->mKeepAlivesPerBrowserId.Count();
|
||||
if (handle->mKeepAlivesPerBrowserId.Contains(0)) {
|
||||
--keepAliveCount;
|
||||
}
|
||||
if (aBrowserId != 0 &&
|
||||
handle->mKeepAlivesPerBrowserId.Contains(aBrowserId)) {
|
||||
--keepAliveCount;
|
||||
}
|
||||
|
||||
if (keepAliveCount < min) {
|
||||
candidate = p;
|
||||
min = keepAliveCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,9 +825,10 @@ ContentParent::CreateRemoteTypeIsolationPrincipal(
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||
UniqueContentParentKeepAlive ContentParent::GetUsedBrowserProcess(
|
||||
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
|
||||
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority) {
|
||||
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority,
|
||||
uint64_t aBrowserId) {
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
AutoRestore ar(sInProcessSelector);
|
||||
sInProcessSelector = true;
|
||||
@ -848,7 +846,8 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||
// has been disabled.
|
||||
RefPtr<ContentParent> selected;
|
||||
if (!StaticPrefs::dom_ipc_disableContentProcessReuse() &&
|
||||
(selected = MinTabSelect(aContentParents, aMaxContentParents))) {
|
||||
(selected =
|
||||
MinTabSelect(aContentParents, aMaxContentParents, aBrowserId))) {
|
||||
if (profiler_thread_is_being_profiled_for_markers()) {
|
||||
nsPrintfCString marker("Reused process %u",
|
||||
(unsigned int)selected->ChildID());
|
||||
@ -859,12 +858,12 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||
(unsigned int)selected->ChildID(),
|
||||
PromiseFlatCString(aRemoteType).get()));
|
||||
selected->AssertAlive();
|
||||
return selected.forget();
|
||||
return selected->AddKeepAlive(aBrowserId);
|
||||
}
|
||||
|
||||
// Try to take a preallocated process except for certain remote types.
|
||||
// Note: this process may not have finished launching yet
|
||||
RefPtr<ContentParent> preallocated;
|
||||
UniqueContentParentKeepAlive preallocated;
|
||||
if (aRemoteType != FILE_REMOTE_TYPE &&
|
||||
aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
|
||||
aRemoteType != EXTENSION_REMOTE_TYPE && // Bug 1638119
|
||||
@ -912,22 +911,24 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
|
||||
if (obs) {
|
||||
nsAutoString cpId;
|
||||
cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
|
||||
obs->NotifyObservers(static_cast<nsIObserver*>(preallocated),
|
||||
obs->NotifyObservers(static_cast<nsIObserver*>(preallocated.get()),
|
||||
"process-type-set", cpId.get());
|
||||
preallocated->AssertAlive();
|
||||
}
|
||||
}
|
||||
return preallocated.forget();
|
||||
// NOTE: Make sure to return a keepalive for the requested aBrowserId. The
|
||||
// keepalive used by the preallocated process manager will be released upon
|
||||
// returning.
|
||||
return preallocated->AddKeepAlive(aBrowserId);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<ContentParent>
|
||||
ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
|
||||
ProcessPriority aPriority, bool aPreferUsed) {
|
||||
ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("GetNewOrUsedProcess for type %s",
|
||||
PromiseFlatCString(aRemoteType).get()));
|
||||
@ -942,25 +943,27 @@ ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
// If we have an existing host process attached to this BrowsingContextGroup,
|
||||
// always return it, as we can never have multiple host processes within a
|
||||
// single BrowsingContextGroup.
|
||||
RefPtr<ContentParent> contentParent;
|
||||
UniqueContentParentKeepAlive contentParent;
|
||||
if (aGroup) {
|
||||
contentParent = aGroup->GetHostProcess(aRemoteType);
|
||||
Unused << NS_WARN_IF(contentParent && contentParent->IsShuttingDown());
|
||||
if (contentParent && !contentParent->IsShuttingDown()) {
|
||||
if (RefPtr<ContentParent> candidate = aGroup->GetHostProcess(aRemoteType)) {
|
||||
Unused << NS_WARN_IF(candidate->IsShuttingDown());
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("GetNewOrUsedProcess: Existing host process %p (launching %d)",
|
||||
contentParent.get(), contentParent->IsLaunching()));
|
||||
contentParent->AssertAlive();
|
||||
return contentParent.forget();
|
||||
candidate.get(), candidate->IsLaunching()));
|
||||
contentParent = candidate->TryAddKeepAlive(aBrowserId);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
|
||||
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
|
||||
|
||||
// Let's try and reuse an existing process.
|
||||
contentParent = GetUsedBrowserProcess(
|
||||
aRemoteType, contentParents, maxContentParents, aPreferUsed, aPriority);
|
||||
if (!contentParent) {
|
||||
// No host process. Let's try to re-use an existing process.
|
||||
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
|
||||
|
||||
contentParent =
|
||||
GetUsedBrowserProcess(aRemoteType, contentParents, maxContentParents,
|
||||
aPreferUsed, aPriority, aBrowserId);
|
||||
}
|
||||
|
||||
if (!contentParent) {
|
||||
// No reusable process. Let's create and launch one.
|
||||
@ -969,17 +972,19 @@ ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
("Launching new process immediately for type %s",
|
||||
PromiseFlatCString(aRemoteType).get()));
|
||||
|
||||
contentParent = new ContentParent(aRemoteType);
|
||||
if (NS_WARN_IF(!contentParent->BeginSubprocessLaunch(aPriority))) {
|
||||
RefPtr<ContentParent> newCp = new ContentParent(aRemoteType);
|
||||
if (NS_WARN_IF(!newCp->BeginSubprocessLaunch(aPriority))) {
|
||||
// Launch aborted because of shutdown. Bailout.
|
||||
contentParent->LaunchSubprocessReject();
|
||||
newCp->LaunchSubprocessReject();
|
||||
return nullptr;
|
||||
}
|
||||
contentParent = newCp->AddKeepAlive(aBrowserId);
|
||||
|
||||
// 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.
|
||||
contentParent->mIsAPreallocBlocker = true;
|
||||
PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent);
|
||||
PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent.get());
|
||||
|
||||
// Store this process for future reuse.
|
||||
contentParent->AddToPool(contentParents);
|
||||
@ -993,9 +998,9 @@ ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
||||
|
||||
contentParent->AssertAlive();
|
||||
if (aGroup) {
|
||||
aGroup->EnsureHostProcess(contentParent);
|
||||
aGroup->EnsureHostProcess(contentParent.get());
|
||||
}
|
||||
return contentParent.forget();
|
||||
return contentParent;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
@ -1003,38 +1008,43 @@ RefPtr<ContentParent::LaunchPromise>
|
||||
ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
|
||||
BrowsingContextGroup* aGroup,
|
||||
ProcessPriority aPriority,
|
||||
bool aPreferUsed) {
|
||||
bool aPreferUsed,
|
||||
uint64_t aBrowserId) {
|
||||
// Obtain a `ContentParent` launched asynchronously.
|
||||
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
|
||||
aRemoteType, aGroup, aPriority, aPreferUsed);
|
||||
UniqueContentParentKeepAlive contentParent =
|
||||
GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
|
||||
aPreferUsed, aBrowserId);
|
||||
if (!contentParent) {
|
||||
// In case of launch error, stop here.
|
||||
return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
__func__);
|
||||
}
|
||||
return contentParent->WaitForLaunchAsync(aPriority);
|
||||
return contentParent->WaitForLaunchAsync(aPriority, aBrowserId);
|
||||
}
|
||||
|
||||
/*static*/
|
||||
already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
|
||||
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedBrowserProcess(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
|
||||
ProcessPriority aPriority, bool aPreferUsed) {
|
||||
RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
|
||||
aRemoteType, aGroup, aPriority, aPreferUsed);
|
||||
ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
|
||||
UniqueContentParentKeepAlive contentParent =
|
||||
GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
|
||||
aPreferUsed, aBrowserId);
|
||||
if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
|
||||
// In case of launch error, stop here.
|
||||
return nullptr;
|
||||
}
|
||||
return contentParent.forget();
|
||||
return contentParent;
|
||||
}
|
||||
|
||||
RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
|
||||
ProcessPriority aPriority) {
|
||||
ProcessPriority aPriority, uint64_t aBrowserId) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
|
||||
UniqueContentParentKeepAlive self = AddKeepAlive(aBrowserId);
|
||||
|
||||
if (!IsLaunching()) {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("WaitForLaunchAsync: launched"));
|
||||
return LaunchPromise::CreateAndResolve(this, __func__);
|
||||
return LaunchPromise::CreateAndResolve(std::move(self), __func__);
|
||||
}
|
||||
|
||||
// We've started an async content process launch.
|
||||
@ -1046,18 +1056,16 @@ RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
|
||||
// other `WaitForLaunchAsync` callbacks.
|
||||
return mSubprocess->WhenProcessHandleReady()->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self = RefPtr{this}, aPriority]() {
|
||||
if (self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
|
||||
[self = std::move(self), aPriority](
|
||||
const ProcessHandlePromise::ResolveOrRejectValue& aValue) mutable {
|
||||
if (aValue.IsResolve() &&
|
||||
self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("WaitForLaunchAsync: async, now launched"));
|
||||
self->mActivateTS = TimeStamp::Now();
|
||||
return LaunchPromise::CreateAndResolve(self, __func__);
|
||||
return LaunchPromise::CreateAndResolve(std::move(self), __func__);
|
||||
}
|
||||
|
||||
self->LaunchSubprocessReject();
|
||||
return LaunchPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__);
|
||||
},
|
||||
[self = RefPtr{this}]() {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("WaitForLaunchAsync: async, rejected"));
|
||||
self->LaunchSubprocessReject();
|
||||
@ -1360,14 +1368,20 @@ already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
|
||||
openerTabId = BrowserParent::GetTabIdFrom(docShell);
|
||||
}
|
||||
|
||||
RefPtr<ContentParent> constructorSender;
|
||||
// Hold a KeepAlive on our ContentParent throughout this function. Once the
|
||||
// `BrowserParent` has been created, it can be cleared, as that BrowserParent
|
||||
// will establish its own KeepAlive.
|
||||
UniqueContentParentKeepAlive constructorSender;
|
||||
MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
|
||||
"Cannot allocate BrowserParent in content process");
|
||||
if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
|
||||
constructorSender = aOpenerContentParent;
|
||||
constructorSender =
|
||||
aOpenerContentParent->AddKeepAlive(aBrowsingContext->BrowserId());
|
||||
} else {
|
||||
constructorSender = GetNewOrUsedBrowserProcess(
|
||||
remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND);
|
||||
remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND,
|
||||
/* aPreferUsed */ false,
|
||||
/* aBrowserId */ aBrowsingContext->BrowserId());
|
||||
if (!constructorSender) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1377,7 +1391,7 @@ already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
|
||||
|
||||
// Ensure that the process which we're using to launch is set as the host
|
||||
// process for this BrowsingContextGroup.
|
||||
aBrowsingContext->Group()->EnsureHostProcess(constructorSender);
|
||||
aBrowsingContext->Group()->EnsureHostProcess(constructorSender.get());
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
@ -1411,7 +1425,7 @@ already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
|
||||
constructorSender->ChildID());
|
||||
|
||||
RefPtr<BrowserParent> browserParent =
|
||||
new BrowserParent(constructorSender, tabId, aContext,
|
||||
new BrowserParent(constructorSender.get(), tabId, aContext,
|
||||
aBrowsingContext->Canonical(), chromeFlags);
|
||||
|
||||
// Open a remote endpoint for our PBrowser actor.
|
||||
@ -1594,79 +1608,6 @@ void ContentParent::Init() {
|
||||
Unused << SendInitNextGenLocalStorageEnabled(NextGenLocalStorageEnabled());
|
||||
}
|
||||
|
||||
bool ContentParent::CheckTabDestroyWillKeepAlive(
|
||||
uint32_t aExpectedBrowserCount) {
|
||||
return ManagedPBrowserParent().Count() != aExpectedBrowserCount ||
|
||||
ShouldKeepProcessAlive();
|
||||
}
|
||||
|
||||
RecursiveMutex& ContentParent::ThreadsafeHandleMutex() {
|
||||
return mThreadsafeHandle->mMutex;
|
||||
}
|
||||
|
||||
void ContentParent::NotifyTabWillDestroy() {
|
||||
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
/* on Android we keep processes alive more agressively, see
|
||||
NotifyTabDestroying where we omit MaybeBeginShutdown */
|
||||
|| (/* we cannot trust CheckTabDestroyWillKeepAlive in E10S mode */
|
||||
mozilla::FissionAutostart() &&
|
||||
!CheckTabDestroyWillKeepAlive(mNumDestroyingTabs + 1))
|
||||
#endif
|
||||
) {
|
||||
// Once we notify the impending shutdown, the content process will stop
|
||||
// to process content JS on interrupt (among other things), so we need to
|
||||
// be sure that the process will not be re-used after this point.
|
||||
// The inverse is harmless, that is if we decide later to shut it down
|
||||
// but did not notify here, it will be just notified later (but in rare
|
||||
// cases too late to avoid a hang).
|
||||
NotifyImpendingShutdown();
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
mNotifiedImpendingShutdownOnTabWillDestroy = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ContentParent::MaybeBeginShutDown(uint32_t aExpectedBrowserCount,
|
||||
bool aSendShutDown) {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
||||
("MaybeBeginShutdown %p, %u vs %u", this,
|
||||
ManagedPBrowserParent().Count(), aExpectedBrowserCount));
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// We need to lock our mutex here to ensure the state does not change
|
||||
// between the check and the MarkAsDead.
|
||||
// Note that if we come through BrowserParent::Destroy our mutex is
|
||||
// already locked.
|
||||
// TODO: We want to get rid of the ThreadsafeHandle, see bug 1683595.
|
||||
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
||||
|
||||
// CheckTabDestroyWillKeepAlive will return false if
|
||||
// IsInOrBeyond(AppShutdownConfirmed), so if the parent shuts down we will
|
||||
// always shutdown the child.
|
||||
if (CheckTabDestroyWillKeepAlive(aExpectedBrowserCount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(
|
||||
ContentParent::GetLog(), LogLevel::Debug,
|
||||
("Beginning ContentParent Shutdown %p (%s)", this, mRemoteType.get()));
|
||||
|
||||
// We're dying now, prevent anything from re-using this process.
|
||||
MarkAsDead();
|
||||
SignalImpendingShutdownToContentJS();
|
||||
|
||||
if (aSendShutDown) {
|
||||
AsyncSendShutDownMessage();
|
||||
} else {
|
||||
// aSendShutDown is false only when we get called from
|
||||
// NotifyTabDestroying where we expect a subsequent call from
|
||||
// NotifyTabDestroyed triggered by a Browser actor destroy
|
||||
// roundtrip through the content process that might never arrive.
|
||||
StartSendShutdownTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void ContentParent::AsyncSendShutDownMessage() {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
||||
("AsyncSendShutDownMessage %p", this));
|
||||
@ -1846,7 +1787,6 @@ void ContentParent::AssertNotInPool() {
|
||||
}
|
||||
|
||||
void ContentParent::AssertAlive() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mNotifiedImpendingShutdownOnTabWillDestroy);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsSignaledImpendingShutdown);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
|
||||
}
|
||||
@ -2120,112 +2060,95 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) {
|
||||
mPendingLoadStates.Clear();
|
||||
}
|
||||
|
||||
bool ContentParent::HasActiveWorker() {
|
||||
// If we have active workers, we need to stay alive.
|
||||
UniqueContentParentKeepAlive ContentParent::TryAddKeepAlive(
|
||||
uint64_t aBrowserId) {
|
||||
return UniqueContentParentKeepAliveFromThreadsafe(
|
||||
mThreadsafeHandle->TryAddKeepAlive(aBrowserId));
|
||||
}
|
||||
|
||||
UniqueContentParentKeepAlive ContentParent::AddKeepAlive(uint64_t aBrowserId) {
|
||||
UniqueContentParentKeepAlive keepAlive = TryAddKeepAlive(aBrowserId);
|
||||
MOZ_DIAGNOSTIC_ASSERT(keepAlive, "ContentParent is already dead");
|
||||
return keepAlive;
|
||||
}
|
||||
|
||||
void ContentParent::RemoveKeepAlive(uint64_t aBrowserId) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
{
|
||||
// Most of the times we'll get here with the mutex acquired, but still.
|
||||
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
||||
if (mThreadsafeHandle->mRemoteWorkerActorCount) {
|
||||
return true;
|
||||
auto entry = mThreadsafeHandle->mKeepAlivesPerBrowserId.Lookup(aBrowserId);
|
||||
MOZ_RELEASE_ASSERT(entry, "No KeepAlive for this BrowserId");
|
||||
if (!--entry.Data()) {
|
||||
entry.Remove();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContentParent::ShouldKeepProcessAlive() {
|
||||
if (HasActiveWorker()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mNumKeepaliveCalls > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsLaunching()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have already been marked as dead, don't prevent shutdown.
|
||||
if (IsDead()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If everything is going down, there is no need to keep us alive, neither.
|
||||
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sBrowserContentParents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* contentParents = sBrowserContentParents->Get(mRemoteType);
|
||||
if (!contentParents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We might want to keep some content processes alive for performance reasons.
|
||||
// e.g. test runs and privileged content process for some about: pages.
|
||||
// We don't want to alter behavior if the pref is not set, so default to 0.
|
||||
int32_t processesToKeepAlive = 0;
|
||||
|
||||
nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
|
||||
|
||||
if (StringBeginsWith(mRemoteType, FISSION_WEB_REMOTE_TYPE) &&
|
||||
xpc::IsInAutomation()) {
|
||||
keepAlivePref.Append(FISSION_WEB_REMOTE_TYPE);
|
||||
keepAlivePref.AppendLiteral(".perOrigin");
|
||||
} else {
|
||||
keepAlivePref.Append(mRemoteType);
|
||||
}
|
||||
if (NS_FAILED(
|
||||
Preferences::GetInt(keepAlivePref.get(), &processesToKeepAlive))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t numberOfAliveProcesses = contentParents->Length();
|
||||
|
||||
return numberOfAliveProcesses <= processesToKeepAlive;
|
||||
}
|
||||
|
||||
void ContentParent::NotifyTabDestroying() {
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("NotifyTabDestroying %p:", this));
|
||||
// There can be more than one PBrowser for a given app process
|
||||
// because of popup windows. PBrowsers can also destroy
|
||||
// concurrently. When all the PBrowsers are destroying, kick off
|
||||
// another task to ensure the child process *really* shuts down,
|
||||
// even if the PBrowsers themselves never finish destroying.
|
||||
++mNumDestroyingTabs;
|
||||
|
||||
/**
|
||||
* We intentionally skip this code on Android:
|
||||
* 1. Android has a fixed upper bound on the number of content processes, so
|
||||
* we prefer to re-use them whenever possible (as opposed to letting an
|
||||
* old process wind down while we launch a new one).
|
||||
* 2. GeckoView always hard-kills content processes (and if it does not,
|
||||
* Android itself will), so we don't concern ourselves with the ForceKill
|
||||
* timer either.
|
||||
*/
|
||||
#if !defined(MOZ_WIDGET_ANDROID)
|
||||
MaybeBeginShutDown(/* aExpectedBrowserCount */ mNumDestroyingTabs,
|
||||
/* aSendShutDown */ false);
|
||||
#endif // !defined(MOZ_WIDGET_ANDROID)
|
||||
}
|
||||
|
||||
void ContentParent::AddKeepAlive() {
|
||||
AssertAlive();
|
||||
// Something wants to keep this content process alive.
|
||||
++mNumKeepaliveCalls;
|
||||
}
|
||||
|
||||
void ContentParent::RemoveKeepAlive() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
|
||||
--mNumKeepaliveCalls;
|
||||
|
||||
MaybeBeginShutDown();
|
||||
}
|
||||
|
||||
bool ContentParent::MaybeBeginShutDown(bool aIgnoreKeepAlivePref) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
{
|
||||
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
||||
// If we still have keepalives or are still launching, we're not shutting
|
||||
// down. Return.
|
||||
if (IsLaunching() ||
|
||||
!mThreadsafeHandle->mKeepAlivesPerBrowserId.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're not in main process shutdown, we might want to keep some content
|
||||
// processes alive for performance reasons (e.g. test runs and privileged
|
||||
// content process for some about: pages). We don't want to alter behavior
|
||||
// if the pref is not set, so default to 0.
|
||||
if (!aIgnoreKeepAlivePref && mIsInPool &&
|
||||
!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
||||
auto* contentParents = sBrowserContentParents->Get(mRemoteType);
|
||||
MOZ_RELEASE_ASSERT(
|
||||
contentParents,
|
||||
"mIsInPool, yet no entry for mRemoteType in sBrowserContentParents?");
|
||||
|
||||
nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
|
||||
if (StringBeginsWith(mRemoteType, FISSION_WEB_REMOTE_TYPE) &&
|
||||
xpc::IsInAutomation()) {
|
||||
keepAlivePref.Append(FISSION_WEB_REMOTE_TYPE);
|
||||
keepAlivePref.AppendLiteral(".perOrigin");
|
||||
} else {
|
||||
keepAlivePref.Append(mRemoteType);
|
||||
}
|
||||
|
||||
int32_t processesToKeepAlive = 0;
|
||||
if (NS_SUCCEEDED(Preferences::GetInt(keepAlivePref.get(),
|
||||
&processesToKeepAlive)) &&
|
||||
contentParents->Length() <=
|
||||
static_cast<size_t>(processesToKeepAlive)) {
|
||||
// We're keeping this process alive even though there are no keepalives
|
||||
// for it due to the keepalive pref.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We're not keeping this process alive, begin shutdown.
|
||||
mThreadsafeHandle->mShutdownStarted = true;
|
||||
}
|
||||
|
||||
MarkAsDead();
|
||||
SignalImpendingShutdownToContentJS();
|
||||
|
||||
if (ManagedPBrowserParent().Count() > 0) {
|
||||
// We still have PBrowser instances which have not been shut down.
|
||||
// Wait for them to be destroyed before we follow-through and shut down this
|
||||
// process, but start a shutdown timer to kill them if this takes too long.
|
||||
StartSendShutdownTimer();
|
||||
} else {
|
||||
// All tabs are dead, we can fully begin shutting down.
|
||||
AsyncSendShutDownMessage();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContentParent::StartSendShutdownTimer() {
|
||||
if (mSendShutdownTimer || !CanSend()) {
|
||||
return;
|
||||
@ -2256,31 +2179,6 @@ void ContentParent::StartForceKillTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
|
||||
bool aNotifiedDestroying) {
|
||||
if (aNotifiedDestroying) {
|
||||
--mNumDestroyingTabs;
|
||||
}
|
||||
|
||||
nsTArray<PContentPermissionRequestParent*> parentArray =
|
||||
nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
|
||||
|
||||
// Need to close undeleted ContentPermissionRequestParents before tab is
|
||||
// closed.
|
||||
for (auto& permissionRequestParent : parentArray) {
|
||||
Unused << PContentPermissionRequestParent::Send__delete__(
|
||||
permissionRequestParent);
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
MaybeBeginShutDown();
|
||||
}
|
||||
|
||||
TestShellParent* ContentParent::CreateTestShell() {
|
||||
RefPtr<TestShellParent> actor = new TestShellParent();
|
||||
if (!SendPTestShellConstructor(actor)) {
|
||||
@ -2610,52 +2508,6 @@ bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContentParent::LaunchSubprocessSync(
|
||||
hal::ProcessPriority aInitialPriority) {
|
||||
// We've started a sync content process launch.
|
||||
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
|
||||
|
||||
if (BeginSubprocessLaunch(aInitialPriority)) {
|
||||
const bool ok = mSubprocess->WaitForProcessHandle();
|
||||
if (ok && LaunchSubprocessResolve(/* aIsSync = */ true, aInitialPriority)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LaunchSubprocessReject();
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
|
||||
hal::ProcessPriority aInitialPriority) {
|
||||
// We've started an async content process launch.
|
||||
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
|
||||
|
||||
if (!BeginSubprocessLaunch(aInitialPriority)) {
|
||||
// Launch aborted because of shutdown. Bailout.
|
||||
LaunchSubprocessReject();
|
||||
return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
__func__);
|
||||
}
|
||||
|
||||
// Otherwise, wait until the process is ready.
|
||||
RefPtr<ProcessHandlePromise> ready = mSubprocess->WhenProcessHandleReady();
|
||||
RefPtr<ContentParent> self = this;
|
||||
mLaunchYieldTS = TimeStamp::Now();
|
||||
|
||||
return ready->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self, aInitialPriority](
|
||||
const ProcessHandlePromise::ResolveOrRejectValue& aValue) {
|
||||
if (aValue.IsResolve() &&
|
||||
self->LaunchSubprocessResolve(/* aIsSync = */ false,
|
||||
aInitialPriority)) {
|
||||
return LaunchPromise::CreateAndResolve(self, __func__);
|
||||
}
|
||||
self->LaunchSubprocessReject();
|
||||
return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
ContentParent::ContentParent(const nsACString& aRemoteType)
|
||||
: mSubprocess(nullptr),
|
||||
mLaunchTS(TimeStamp::Now()),
|
||||
@ -2667,8 +2519,6 @@ ContentParent::ContentParent(const nsACString& aRemoteType)
|
||||
mGeolocationWatchID(-1),
|
||||
mThreadsafeHandle(
|
||||
new ThreadsafeContentParentHandle(this, mChildID, mRemoteType)),
|
||||
mNumDestroyingTabs(0),
|
||||
mNumKeepaliveCalls(0),
|
||||
mLifecycleState(LifecycleState::LAUNCHING),
|
||||
mIsForBrowser(!mRemoteType.IsEmpty()),
|
||||
mCalledClose(false),
|
||||
@ -7159,21 +7009,6 @@ mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void ContentParent::UnregisterRemoveWorkerActor() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
{
|
||||
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
||||
if (--mThreadsafeHandle->mRemoteWorkerActorCount) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
||||
("UnregisterRemoveWorkerActor %p", this));
|
||||
MaybeBeginShutDown();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
|
||||
const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
|
||||
if (aContext.IsNullOrDiscarded()) {
|
||||
@ -8117,15 +7952,19 @@ nsCString ThreadsafeContentParentHandle::GetRemoteType() {
|
||||
return mRemoteType;
|
||||
}
|
||||
|
||||
bool ThreadsafeContentParentHandle::MaybeRegisterRemoteWorkerActor(
|
||||
MoveOnlyFunction<bool(uint32_t, bool)> aCallback) {
|
||||
UniqueThreadsafeContentParentKeepAlive
|
||||
ThreadsafeContentParentHandle::TryAddKeepAlive(uint64_t aBrowserId) {
|
||||
RecursiveMutexAutoLock lock(mMutex);
|
||||
if (aCallback(mRemoteWorkerActorCount, mShutdownStarted)) {
|
||||
// TODO: I'd wish we could assert here that our ContentParent is alive.
|
||||
++mRemoteWorkerActorCount;
|
||||
return true;
|
||||
// If shutdown has already started, we can't keep this ContentParent alive
|
||||
// anymore.
|
||||
if (mShutdownStarted) {
|
||||
return nullptr;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Otherwise, ensure there is an entry for this BrowserId, and increment it.
|
||||
++mKeepAlivesPerBrowserId.LookupOrInsert(aBrowserId, 0);
|
||||
return UniqueThreadsafeContentParentKeepAlive{do_AddRef(this).take(),
|
||||
{.mBrowserId = aBrowserId}};
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/dom/RemoteType.h"
|
||||
#include "mozilla/dom/JSProcessActorParent.h"
|
||||
#include "mozilla/dom/ProcessActor.h"
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
#include "mozilla/dom/UserActivation.h"
|
||||
#include "mozilla/gfx/gfxVarReceiver.h"
|
||||
#include "mozilla/gfx/GPUProcessListener.h"
|
||||
@ -128,10 +129,11 @@ class ContentParent final : public PContentParent,
|
||||
friend class mozilla::PreallocatedProcessManagerImpl;
|
||||
friend class PContentParent;
|
||||
friend class mozilla::dom::RemoteWorkerManager;
|
||||
friend struct mozilla::dom::ContentParentKeepAliveDeleter;
|
||||
|
||||
public:
|
||||
using LaunchPromise =
|
||||
mozilla::MozPromise<RefPtr<ContentParent>, nsresult, false>;
|
||||
mozilla::MozPromise<UniqueContentParentKeepAlive, nsresult, true>;
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID)
|
||||
|
||||
@ -144,7 +146,7 @@ class ContentParent final : public PContentParent,
|
||||
/**
|
||||
* Create a ContentParent suitable for use later as a content process.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> MakePreallocProcess();
|
||||
static UniqueContentParentKeepAlive MakePreallocProcess();
|
||||
|
||||
/**
|
||||
* Start up the content-process machinery. This might include
|
||||
@ -168,49 +170,81 @@ class ContentParent final : public PContentParent,
|
||||
|
||||
/**
|
||||
* Picks a random content parent from |aContentParents| respecting the index
|
||||
* limit set by |aMaxContentParents|.
|
||||
* limit set by |aMaxContentParents|. If |aBrowserId| is non-zero, that tab
|
||||
* will be ignored when counting tabs in this process.
|
||||
* Returns null if non available.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> MinTabSelect(
|
||||
const nsTArray<ContentParent*>& aContentParents,
|
||||
int32_t maxContentParents);
|
||||
int32_t maxContentParents, uint64_t aBrowserId);
|
||||
|
||||
/**
|
||||
* Get or create a content process for:
|
||||
* 1. browser iframe
|
||||
* 2. remote xul <browser>
|
||||
* 3. normal iframe
|
||||
* Get or create a content process which can be used for hosting web content
|
||||
* or workers.
|
||||
*
|
||||
* This method returns a |UniqueContentParentKeepAlive|, which manages the
|
||||
* lifecycle of the process. See the documentation on |AddKeepAlive| for more
|
||||
* information about managing content process lifecycles.
|
||||
*
|
||||
* The returned ContentParent which may still be in the process of launching.
|
||||
* Use the |WaitForLaunchAsync| or |WaitForLaunchSync| methods to wait for
|
||||
* this process to finish launching.
|
||||
*
|
||||
* @param aRemoteType Required remote type for new & used processes.
|
||||
* @param aGroup If specified, the |BrowsingContextGroup| requesting process
|
||||
* selection. Used to ensure that only a single process per
|
||||
* remoteType is used for each |BrowsingContextGroup|.
|
||||
* @param aPriority Initial process priority for a new content process.
|
||||
* @param aPreferUsed If true, process selection will prefer re-using an
|
||||
* existing ContentProcess over launching a new one.
|
||||
* @param aBrowserId The |BrowserId| requesting process selection. This
|
||||
* information is used to reduce unnecessary process churn
|
||||
* when navigating (see |MinTabSelect|).
|
||||
* The returned KeepAlive will be for this BrowserId.
|
||||
*/
|
||||
static UniqueContentParentKeepAlive GetNewOrUsedLaunchingBrowserProcess(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
|
||||
bool aPreferUsed = false, uint64_t aBrowserId = 0);
|
||||
|
||||
/**
|
||||
* Like |GetNewOrUsedLaunchingBrowserProcess|, but returns a promise which
|
||||
* resolves when the process is finished launching.
|
||||
*/
|
||||
static RefPtr<ContentParent::LaunchPromise> GetNewOrUsedBrowserProcessAsync(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
|
||||
bool aPreferUsed = false);
|
||||
static already_AddRefed<ContentParent> GetNewOrUsedBrowserProcess(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
|
||||
bool aPreferUsed = false);
|
||||
bool aPreferUsed = false, uint64_t aBrowserId = 0);
|
||||
|
||||
/**
|
||||
* Get or create a content process, but without waiting for the process
|
||||
* launch to have completed. The returned `ContentParent` may still be in the
|
||||
* "Launching" state.
|
||||
*
|
||||
* Can return `nullptr` in the case of an error.
|
||||
*
|
||||
* Use the `WaitForLaunchAsync` or `WaitForLaunchSync` methods to wait for
|
||||
* the process to be fully launched.
|
||||
* Like |GetNewOrUsedLaunchingBrowserProcess|, but blocks the main thread
|
||||
* until the process process is finished launching before returning.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> GetNewOrUsedLaunchingBrowserProcess(
|
||||
static UniqueContentParentKeepAlive GetNewOrUsedBrowserProcess(
|
||||
const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
|
||||
bool aPreferUsed = false);
|
||||
bool aPreferUsed = false, uint64_t aBrowserId = 0);
|
||||
|
||||
/**
|
||||
* Asynchronously wait for this content process to finish launching, such that
|
||||
* the ContentParent actor is ready for IPC.
|
||||
*
|
||||
* @param aPriority The initial priority for the process after launching.
|
||||
* @param aBrowserId The BrowserId to hold a KeepAlive for during the async
|
||||
* launch which will be used to resolve the LaunchPromise.
|
||||
*/
|
||||
RefPtr<ContentParent::LaunchPromise> WaitForLaunchAsync(
|
||||
hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
|
||||
uint64_t aBrowserId = 0);
|
||||
|
||||
/**
|
||||
* Like `WaitForLaunchAsync`, but synchronously blocks the main thread until
|
||||
* the content process has finished launching.
|
||||
*/
|
||||
bool WaitForLaunchSync(hal::ProcessPriority aPriority =
|
||||
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
|
||||
|
||||
@ -330,21 +364,38 @@ class ContentParent final : public PContentParent,
|
||||
virtual nsresult DoSendAsyncMessage(const nsAString& aMessage,
|
||||
StructuredCloneData& aData) override;
|
||||
|
||||
RecursiveMutex& ThreadsafeHandleMutex();
|
||||
/*
|
||||
* Attempt to add a KeepAlive for the given BrowserId. A KeepAlive will try to
|
||||
* keep the process alive, though it may still die (e.g. due to a crash,
|
||||
* explicit shutdown request, or similar).
|
||||
*
|
||||
* The returned `UniqueContentParentKeepAlive` will clear this KeepAlive when
|
||||
* destroyed, and acts as a strong pointer to this `ContentParent`.
|
||||
*
|
||||
* Returns nullptr if the process is already being shut down.
|
||||
*/
|
||||
[[nodiscard]] UniqueContentParentKeepAlive TryAddKeepAlive(
|
||||
uint64_t aBrowserId);
|
||||
|
||||
/** Notify that a tab is about to send Destroy to its child. */
|
||||
void NotifyTabWillDestroy();
|
||||
/*
|
||||
* Like `TryAddKeepAlive`, but never returns `nullptr`, instead asserting if
|
||||
* the ContentParent is already shutting down.
|
||||
*/
|
||||
[[nodiscard]] UniqueContentParentKeepAlive AddKeepAlive(uint64_t aBrowserId);
|
||||
|
||||
/** Notify that a tab is beginning its destruction sequence. */
|
||||
void NotifyTabDestroying();
|
||||
|
||||
/** Notify that a tab was destroyed during normal operation. */
|
||||
void NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying);
|
||||
|
||||
// Manage the set of `KeepAlive`s on this ContentParent which are preventing
|
||||
// it from being destroyed.
|
||||
void AddKeepAlive();
|
||||
void RemoveKeepAlive();
|
||||
/**
|
||||
* Check if this process is ready to be shut down, and if it is, begin the
|
||||
* shutdown process. Automatically called whenever a KeepAlive is removed, or
|
||||
* a BrowserParent is removed.
|
||||
*
|
||||
* Returns `true` if shutdown for the process has been started, and `false`
|
||||
* otherwise.
|
||||
*
|
||||
* @param aIgnoreKeepAlivePref If true, the dom.ipc.keepProcessesAlive.*
|
||||
* preferences will be ignored, for clean-up of
|
||||
* cached processes.
|
||||
*/
|
||||
bool MaybeBeginShutDown(bool aIgnoreKeepAlivePref = false);
|
||||
|
||||
TestShellParent* CreateTestShell();
|
||||
|
||||
@ -352,13 +403,6 @@ class ContentParent final : public PContentParent,
|
||||
|
||||
TestShellParent* GetTestShellSingleton();
|
||||
|
||||
// This method can be called on any thread.
|
||||
void RegisterRemoteWorkerActor();
|
||||
|
||||
// This method _must_ be called on main-thread because it can start the
|
||||
// shutting down of the content process.
|
||||
void UnregisterRemoveWorkerActor();
|
||||
|
||||
void ReportChildAlreadyBlocked();
|
||||
|
||||
bool RequestRunToCompletion();
|
||||
@ -709,19 +753,6 @@ class ContentParent final : public PContentParent,
|
||||
|
||||
explicit ContentParent(const nsACString& aRemoteType);
|
||||
|
||||
// Launch the subprocess and associated initialization.
|
||||
// Returns false if the process fails to start.
|
||||
// Deprecated in favor of LaunchSubprocessAsync.
|
||||
bool LaunchSubprocessSync(hal::ProcessPriority aInitialPriority);
|
||||
|
||||
// Launch the subprocess and associated initialization;
|
||||
// returns a promise and signals failure by rejecting.
|
||||
// OS-level launching work is dispatched to another thread, but some
|
||||
// initialization (creating IPDL actors, etc.; see Init()) is run on
|
||||
// the main thread.
|
||||
RefPtr<LaunchPromise> LaunchSubprocessAsync(
|
||||
hal::ProcessPriority aInitialPriority);
|
||||
|
||||
// Common implementation of LaunchSubprocess{Sync,Async}.
|
||||
// Return `true` in case of success, `false` if launch was
|
||||
// aborted because of shutdown.
|
||||
@ -751,17 +782,6 @@ class ContentParent final : public PContentParent,
|
||||
*/
|
||||
void RemoveFromList();
|
||||
|
||||
/**
|
||||
* Return if the process has an active worker.
|
||||
*/
|
||||
bool HasActiveWorker();
|
||||
|
||||
/**
|
||||
* Decide whether the process should be kept alive even when it would normally
|
||||
* be shut down, for example when all its tabs are closed.
|
||||
*/
|
||||
bool ShouldKeepProcessAlive();
|
||||
|
||||
/**
|
||||
* Mark this ContentParent as dead for the purposes of Get*().
|
||||
* This method is idempotent.
|
||||
@ -775,22 +795,6 @@ class ContentParent final : public PContentParent,
|
||||
*/
|
||||
void SignalImpendingShutdownToContentJS();
|
||||
|
||||
bool CheckTabDestroyWillKeepAlive(uint32_t aExpectedBrowserCount);
|
||||
|
||||
/**
|
||||
* Check if this process is ready to be shut down, and if it is, begin the
|
||||
* shutdown process. Should be called whenever a change occurs which could
|
||||
* cause the decisions made by `ShouldKeepProcessAlive` to change.
|
||||
*
|
||||
* @param aExpectedBrowserCount The number of PBrowser actors which should
|
||||
* not block shutdown. This should usually be 0.
|
||||
* @param aSendShutDown If true, will send the shutdown message in addition
|
||||
* to marking the process as dead and starting the force
|
||||
* kill timer.
|
||||
*/
|
||||
void MaybeBeginShutDown(uint32_t aExpectedBrowserCount = 0,
|
||||
bool aSendShutDown = true);
|
||||
|
||||
/**
|
||||
* How we will shut down this ContentParent and its subprocess.
|
||||
*/
|
||||
@ -1412,14 +1416,17 @@ class ContentParent final : public PContentParent,
|
||||
|
||||
private:
|
||||
// Return an existing ContentParent if possible. Otherwise, `nullptr`.
|
||||
static already_AddRefed<ContentParent> GetUsedBrowserProcess(
|
||||
static UniqueContentParentKeepAlive GetUsedBrowserProcess(
|
||||
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
|
||||
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority);
|
||||
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority,
|
||||
uint64_t aBrowserId);
|
||||
|
||||
void AddToPool(nsTArray<ContentParent*>&);
|
||||
void RemoveFromPool(nsTArray<ContentParent*>&);
|
||||
void AssertNotInPool();
|
||||
|
||||
void RemoveKeepAlive(uint64_t aBrowserId);
|
||||
|
||||
void AssertAlive();
|
||||
|
||||
void StartRemoteWorkerService();
|
||||
@ -1460,13 +1467,6 @@ class ContentParent final : public PContentParent,
|
||||
// they're attached to.
|
||||
const RefPtr<ThreadsafeContentParentHandle> mThreadsafeHandle;
|
||||
|
||||
// How many tabs we're waiting to finish their destruction
|
||||
// sequence. Precisely, how many BrowserParents have called
|
||||
// NotifyTabDestroying() but not called NotifyTabDestroyed().
|
||||
int32_t mNumDestroyingTabs;
|
||||
|
||||
uint32_t mNumKeepaliveCalls;
|
||||
|
||||
// The process starts in the LAUNCHING state, and transitions to
|
||||
// ALIVE once it can accept IPC messages. It remains ALIVE only
|
||||
// while remote content is being actively used from this process.
|
||||
@ -1509,7 +1509,6 @@ class ContentParent final : public PContentParent,
|
||||
uint8_t mGMPCreated : 1;
|
||||
|
||||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||
bool mNotifiedImpendingShutdownOnTabWillDestroy = false;
|
||||
bool mBlockShutdownCalled;
|
||||
#endif
|
||||
|
||||
@ -1615,20 +1614,15 @@ class ThreadsafeContentParentHandle final {
|
||||
return do_AddRef(mWeakActor);
|
||||
}
|
||||
|
||||
// Calls `aCallback` with the current remote worker count and whether or not
|
||||
// shutdown has been started. If the callback returns `true`, registers a new
|
||||
// actor, and returns `true`, otherwise returns `false`.
|
||||
// Attempt to add a KeepAlive for the given BrowserId. A KeepAlive will try to
|
||||
// keep the process alive, though it may still die (e.g. due to a crash,
|
||||
// explicit shutdown request, or similar).
|
||||
//
|
||||
// NOTE: The internal mutex is held while evaluating `aCallback`.
|
||||
bool MaybeRegisterRemoteWorkerActor(
|
||||
MoveOnlyFunction<bool(uint32_t, bool)> aCallback) MOZ_EXCLUDES(mMutex);
|
||||
|
||||
// Like `MaybeRegisterRemoteWorkerActor`, but unconditional.
|
||||
void RegisterRemoteWorkerActor() MOZ_EXCLUDES(mMutex) {
|
||||
MaybeRegisterRemoteWorkerActor([](uint32_t, bool) { return true; });
|
||||
}
|
||||
|
||||
RecursiveMutex& Mutex() { return mMutex; }
|
||||
// Returns nullptr if the process is already being shut down.
|
||||
//
|
||||
// May be called from any thread.
|
||||
[[nodiscard]] UniqueThreadsafeContentParentKeepAlive TryAddKeepAlive(
|
||||
uint64_t aBrowserId = 0) MOZ_EXCLUDES(mMutex);
|
||||
|
||||
private:
|
||||
ThreadsafeContentParentHandle(ContentParent* aActor, ContentParentId aChildID,
|
||||
@ -1641,7 +1635,17 @@ class ThreadsafeContentParentHandle final {
|
||||
const ContentParentId mChildID;
|
||||
|
||||
nsCString mRemoteType MOZ_GUARDED_BY(mMutex);
|
||||
uint32_t mRemoteWorkerActorCount MOZ_GUARDED_BY(mMutex) = 0;
|
||||
|
||||
// Keepalives for this browser, keyed by BrowserId. A BrowserId of `0` is used
|
||||
// for non-tab code keeping the process alive (such as for workers).
|
||||
// Each KeepAlive increments the corresponding BrowserId's counter, and the
|
||||
// process will begin shutdown when the last KeepAlive is removed.
|
||||
// FIXME: These sets are probably quite small, so it might make sense to avoid
|
||||
// hashtable storage.
|
||||
nsTHashMap<uint64_t, uint32_t> mKeepAlivesPerBrowserId MOZ_GUARDED_BY(mMutex);
|
||||
|
||||
// If set, the browser is shutting down, and new workers or tabs should not be
|
||||
// created in this process.
|
||||
bool mShutdownStarted MOZ_GUARDED_BY(mMutex) = false;
|
||||
|
||||
// Weak reference to the actual ContentParent actor. Only touched on the main
|
||||
|
@ -41,7 +41,7 @@ class PreallocatedProcessManagerImpl final : public nsIObserver {
|
||||
// See comments on PreallocatedProcessManager for these methods.
|
||||
void AddBlocker(ContentParent* aParent);
|
||||
void RemoveBlocker(ContentParent* aParent);
|
||||
already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
|
||||
UniqueContentParentKeepAlive Take(const nsACString& aRemoteType);
|
||||
void Erase(ContentParent* aParent);
|
||||
|
||||
private:
|
||||
@ -77,7 +77,7 @@ class PreallocatedProcessManagerImpl final : public nsIObserver {
|
||||
|
||||
bool mEnabled;
|
||||
uint32_t mNumberPreallocs;
|
||||
AutoTArray<RefPtr<ContentParent>, 3> mPreallocatedProcesses;
|
||||
AutoTArray<UniqueContentParentKeepAlive, 3> mPreallocatedProcesses;
|
||||
// Even if we have multiple PreallocatedProcessManagerImpls, we'll have
|
||||
// one blocker counter
|
||||
static uint32_t sNumBlockers;
|
||||
@ -192,14 +192,14 @@ void PreallocatedProcessManagerImpl::RereadPrefs() {
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
|
||||
UniqueContentParentKeepAlive PreallocatedProcessManagerImpl::Take(
|
||||
const nsACString& aRemoteType) {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<ContentParent> process;
|
||||
UniqueContentParentKeepAlive process;
|
||||
if (!IsEmpty()) {
|
||||
process = mPreallocatedProcesses.ElementAt(0);
|
||||
process = std::move(mPreallocatedProcesses.ElementAt(0));
|
||||
mPreallocatedProcesses.RemoveElementAt(0);
|
||||
|
||||
// Don't set the priority to FOREGROUND here, since it may not have
|
||||
@ -207,7 +207,7 @@ already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
|
||||
|
||||
// We took a preallocated process. Let's try to start up a new one
|
||||
// soon.
|
||||
ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr);
|
||||
ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr).get();
|
||||
// There could be a launching process that isn't the last, but that's
|
||||
// ok (and unlikely)
|
||||
if (!last || !last->IsLaunching()) {
|
||||
@ -219,11 +219,11 @@ already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
|
||||
(unsigned long)mPreallocatedProcesses.Length()));
|
||||
}
|
||||
if (process && !process->IsLaunching()) {
|
||||
ProcessPriorityManager::SetProcessPriority(process,
|
||||
ProcessPriorityManager::SetProcessPriority(process.get(),
|
||||
PROCESS_PRIORITY_FOREGROUND);
|
||||
} // else this will get set by the caller when they call InitInternal()
|
||||
|
||||
return process.forget();
|
||||
return process;
|
||||
}
|
||||
|
||||
void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) {
|
||||
@ -315,19 +315,14 @@ void PreallocatedProcessManagerImpl::AllocateNow() {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ContentParent> process = ContentParent::MakePreallocProcess();
|
||||
mPreallocatedProcesses.AppendElement(process);
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("Preallocated = %lu of %d processes",
|
||||
(unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs));
|
||||
|
||||
RefPtr<PreallocatedProcessManagerImpl> self(this);
|
||||
process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC)
|
||||
UniqueContentParentKeepAlive process = ContentParent::MakePreallocProcess();
|
||||
process->WaitForLaunchAsync(PROCESS_PRIORITY_PREALLOC)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self, this, process](const RefPtr<ContentParent>&) {
|
||||
[self = RefPtr{this},
|
||||
process = RefPtr{process.get()}](UniqueContentParentKeepAlive) {
|
||||
if (process->IsDead()) {
|
||||
Erase(process);
|
||||
self->Erase(process);
|
||||
// Process died in startup (before we could add it). If it
|
||||
// dies after this, MarkAsDead() will Erase() this entry.
|
||||
// Shouldn't be in the sBrowserContentParents, so we don't need
|
||||
@ -335,24 +330,22 @@ void PreallocatedProcessManagerImpl::AllocateNow() {
|
||||
// preallocation here, to avoid possible looping if something is
|
||||
// causing them to consistently fail; if everything is ok on the
|
||||
// next allocation request we'll kick off creation.
|
||||
} else {
|
||||
} else if (self->CanAllocate()) {
|
||||
// Continue prestarting processes if needed
|
||||
if (CanAllocate()) {
|
||||
if (mPreallocatedProcesses.Length() < mNumberPreallocs) {
|
||||
AllocateOnIdle();
|
||||
}
|
||||
} else if (!IsEnabled()) {
|
||||
// if this has a remote type set, it's been allocated for use
|
||||
// already
|
||||
if (process->mRemoteType == PREALLOC_REMOTE_TYPE) {
|
||||
// This will Erase() it
|
||||
process->ShutDownProcess(
|
||||
ContentParent::SEND_SHUTDOWN_MESSAGE);
|
||||
}
|
||||
if (self->mPreallocatedProcesses.Length() <
|
||||
self->mNumberPreallocs) {
|
||||
self->AllocateOnIdle();
|
||||
}
|
||||
}
|
||||
},
|
||||
[self, this, process]() { Erase(process); });
|
||||
[self = RefPtr{this}, process = RefPtr{process.get()}]() {
|
||||
self->Erase(process);
|
||||
});
|
||||
|
||||
mPreallocatedProcesses.AppendElement(std::move(process));
|
||||
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
||||
("Preallocated = %lu of %d processes",
|
||||
(unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs));
|
||||
}
|
||||
|
||||
void PreallocatedProcessManagerImpl::Disable() {
|
||||
@ -365,12 +358,9 @@ void PreallocatedProcessManagerImpl::Disable() {
|
||||
}
|
||||
|
||||
void PreallocatedProcessManagerImpl::CloseProcesses() {
|
||||
while (!IsEmpty()) {
|
||||
RefPtr<ContentParent> process(mPreallocatedProcesses.ElementAt(0));
|
||||
mPreallocatedProcesses.RemoveElementAt(0);
|
||||
process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
|
||||
// drop ref and let it free
|
||||
}
|
||||
// Drop our KeepAlives on these processes. This will automatically lead to the
|
||||
// processes being shut down when no keepalives are left.
|
||||
mPreallocatedProcesses.Clear();
|
||||
}
|
||||
|
||||
inline PreallocatedProcessManagerImpl*
|
||||
@ -414,7 +404,7 @@ void PreallocatedProcessManager::RemoveBlocker(const nsACString& aRemoteType,
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<ContentParent> PreallocatedProcessManager::Take(
|
||||
UniqueContentParentKeepAlive PreallocatedProcessManager::Take(
|
||||
const nsACString& aRemoteType) {
|
||||
if (auto impl = GetPPMImpl()) {
|
||||
return impl->Take(aRemoteType);
|
||||
|
@ -8,13 +8,10 @@
|
||||
#define mozilla_PreallocatedProcessManager_h
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
} // namespace dom
|
||||
|
||||
/**
|
||||
* This class manages a ContentParent that it starts up ahead of any particular
|
||||
@ -32,7 +29,9 @@ class ContentParent;
|
||||
class PreallocatedProcessManagerImpl;
|
||||
|
||||
class PreallocatedProcessManager final {
|
||||
typedef mozilla::dom::ContentParent ContentParent;
|
||||
using ContentParent = mozilla::dom::ContentParent;
|
||||
using UniqueContentParentKeepAlive =
|
||||
mozilla::dom::UniqueContentParentKeepAlive;
|
||||
|
||||
public:
|
||||
static PreallocatedProcessManagerImpl* GetPPMImpl();
|
||||
@ -55,7 +54,7 @@ class PreallocatedProcessManager final {
|
||||
* If we use a preallocated process, it will schedule the start of
|
||||
* another on Idle (AllocateOnIdle()).
|
||||
*/
|
||||
static already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
|
||||
static UniqueContentParentKeepAlive Take(const nsACString& aRemoteType);
|
||||
|
||||
/**
|
||||
* Note that a process was shut down, and should no longer be tracked as a
|
||||
|
60
dom/ipc/UniqueContentParentKeepAlive.cpp
Normal file
60
dom/ipc/UniqueContentParentKeepAlive.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
void ContentParentKeepAliveDeleter::operator()(ContentParent* aProcess) {
|
||||
AssertIsOnMainThread();
|
||||
if (RefPtr<ContentParent> process = dont_AddRef(aProcess)) {
|
||||
process->RemoveKeepAlive(mBrowserId);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentParentKeepAliveDeleter::operator()(
|
||||
ThreadsafeContentParentHandle* aHandle) {
|
||||
if (RefPtr<ThreadsafeContentParentHandle> handle = dont_AddRef(aHandle)) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"ThreadsafeContentParentKeepAliveDeleter",
|
||||
[handle = std::move(handle), browserId = mBrowserId]() {
|
||||
AssertIsOnMainThread();
|
||||
if (RefPtr<ContentParent> process = handle->GetContentParent()) {
|
||||
process->RemoveKeepAlive(browserId);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
UniqueContentParentKeepAlive UniqueContentParentKeepAliveFromThreadsafe(
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive) {
|
||||
AssertIsOnMainThread();
|
||||
if (aKeepAlive) {
|
||||
uint64_t browserId = aKeepAlive.get_deleter().mBrowserId;
|
||||
RefPtr<ThreadsafeContentParentHandle> handle =
|
||||
dont_AddRef(aKeepAlive.release());
|
||||
RefPtr<ContentParent> process = handle->GetContentParent();
|
||||
return UniqueContentParentKeepAlive{process.forget().take(),
|
||||
{.mBrowserId = browserId}};
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniqueThreadsafeContentParentKeepAlive UniqueContentParentKeepAliveToThreadsafe(
|
||||
UniqueContentParentKeepAlive aKeepAlive) {
|
||||
AssertIsOnMainThread();
|
||||
if (aKeepAlive) {
|
||||
uint64_t browserId = aKeepAlive.get_deleter().mBrowserId;
|
||||
RefPtr<ContentParent> process = dont_AddRef(aKeepAlive.release());
|
||||
RefPtr<ThreadsafeContentParentHandle> handle = process->ThreadsafeHandle();
|
||||
return UniqueThreadsafeContentParentKeepAlive{handle.forget().take(),
|
||||
{.mBrowserId = browserId}};
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
42
dom/ipc/UniqueContentParentKeepAlive.h
Normal file
42
dom/ipc/UniqueContentParentKeepAlive.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UniqueContentParentKeepAlive_h
|
||||
#define mozilla_dom_UniqueContentParentKeepAlive_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class ContentParent;
|
||||
class ThreadsafeContentParentHandle;
|
||||
|
||||
struct ContentParentKeepAliveDeleter {
|
||||
void operator()(ContentParent* aProcess);
|
||||
void operator()(ThreadsafeContentParentHandle* aHandle);
|
||||
uint64_t mBrowserId = 0;
|
||||
};
|
||||
|
||||
// Helper for managing a ContentParent combined with the KeepAlive which is
|
||||
// keeping it alive for use by a specific BrowserId.
|
||||
//
|
||||
// This generally should not be created directly, but rather should be created
|
||||
// using `ContentParent::AddKeepAlive`.
|
||||
using UniqueContentParentKeepAlive =
|
||||
UniquePtr<ContentParent, ContentParentKeepAliveDeleter>;
|
||||
|
||||
using UniqueThreadsafeContentParentKeepAlive =
|
||||
UniquePtr<ThreadsafeContentParentHandle, ContentParentKeepAliveDeleter>;
|
||||
|
||||
UniqueContentParentKeepAlive UniqueContentParentKeepAliveFromThreadsafe(
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive);
|
||||
UniqueThreadsafeContentParentKeepAlive UniqueContentParentKeepAliveToThreadsafe(
|
||||
UniqueContentParentKeepAlive aKeepAlive);
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_UniqueContentParentKeepAlive_h
|
@ -85,6 +85,7 @@ EXPORTS.mozilla.dom += [
|
||||
"TabContext.h",
|
||||
"TabMessageTypes.h",
|
||||
"TabMessageUtils.h",
|
||||
"UniqueContentParentKeepAlive.h",
|
||||
"URLClassifierChild.h",
|
||||
"URLClassifierParent.h",
|
||||
"UserActivationIPCUtils.h",
|
||||
@ -148,6 +149,7 @@ UNIFIED_SOURCES += [
|
||||
"SharedStringMap.cpp",
|
||||
"StructuredCloneData.cpp",
|
||||
"TabContext.cpp",
|
||||
"UniqueContentParentKeepAlive.cpp",
|
||||
"URLClassifierParent.cpp",
|
||||
"WindowGlobalActor.cpp",
|
||||
"WindowGlobalChild.cpp",
|
||||
|
@ -261,18 +261,18 @@ void RemoteWorkerManager::Launch(RemoteWorkerController* aController,
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RemoteWorkerServiceParent* targetActor = SelectTargetActor(aData, aProcessId);
|
||||
TargetActorAndKeepAlive target = SelectTargetActor(aData, aProcessId);
|
||||
|
||||
// If there is not an available actor, let's store the data, and let's spawn a
|
||||
// new process.
|
||||
if (!targetActor) {
|
||||
// If there is no available actor, try to start a process, and connect to it.
|
||||
if (!target.mActor) {
|
||||
// Launching is async, so we cannot check for failures right here.
|
||||
LaunchNewContentProcess(aData)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self = RefPtr{this}, controller = RefPtr{aController},
|
||||
data = aData](const RefPtr<RemoteWorkerServiceParent>& aTargetActor) {
|
||||
if (aTargetActor->CanSend()) {
|
||||
self->LaunchInternal(controller, aTargetActor, data, false);
|
||||
data = aData](TargetActorAndKeepAlive&& aTarget) {
|
||||
if (aTarget.mActor->CanSend()) {
|
||||
self->LaunchInternal(controller, aTarget.mActor,
|
||||
std::move(aTarget.mKeepAlive), data);
|
||||
} else {
|
||||
controller->CreationFailed();
|
||||
}
|
||||
@ -283,20 +283,15 @@ void RemoteWorkerManager::Launch(RemoteWorkerController* aController,
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a target actor for the remote worker has been selected, the actor has
|
||||
* already been registered with the corresponding `ContentParent` and we
|
||||
* should not increment the `mRemoteWorkerActorData`'s `mCount` again (see
|
||||
* `SelectTargetActorForServiceWorker()` /
|
||||
* `SelectTargetActorForSharedWorker()`).
|
||||
*/
|
||||
LaunchInternal(aController, targetActor, aData, true);
|
||||
LaunchInternal(aController, target.mActor, std::move(target.mKeepAlive),
|
||||
aData);
|
||||
}
|
||||
|
||||
void RemoteWorkerManager::LaunchInternal(
|
||||
RemoteWorkerController* aController,
|
||||
RemoteWorkerServiceParent* aTargetActor, const RemoteWorkerData& aData,
|
||||
bool aRemoteWorkerAlreadyRegistered) {
|
||||
RemoteWorkerServiceParent* aTargetActor,
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive,
|
||||
const RemoteWorkerData& aData) {
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aController);
|
||||
@ -307,14 +302,13 @@ void RemoteWorkerManager::LaunchInternal(
|
||||
// We need to send permissions to content processes, but not if we're spawning
|
||||
// the worker here in the parent process.
|
||||
if (aTargetActor != mParentActor) {
|
||||
RefPtr<ThreadsafeContentParentHandle> contentHandle =
|
||||
aTargetActor->GetContentParentHandle();
|
||||
MOZ_ASSERT(aKeepAlive);
|
||||
|
||||
// This won't cause any race conditions because the content process
|
||||
// should wait for the permissions to be received before executing the
|
||||
// Service Worker.
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
__func__, [contentHandle = std::move(contentHandle),
|
||||
__func__, [contentHandle = RefPtr{aKeepAlive.get()},
|
||||
principalInfo = aData.principalInfo()] {
|
||||
AssertIsOnMainThread();
|
||||
if (RefPtr<ContentParent> contentParent =
|
||||
@ -327,14 +321,13 @@ void RemoteWorkerManager::LaunchInternal(
|
||||
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
|
||||
}
|
||||
|
||||
RefPtr<RemoteWorkerParent> workerActor = MakeAndAddRef<RemoteWorkerParent>();
|
||||
RefPtr<RemoteWorkerParent> workerActor =
|
||||
MakeAndAddRef<RemoteWorkerParent>(std::move(aKeepAlive));
|
||||
if (!aTargetActor->SendPRemoteWorkerConstructor(workerActor, aData)) {
|
||||
AsyncCreationFailed(aController);
|
||||
return;
|
||||
}
|
||||
|
||||
workerActor->Initialize(aRemoteWorkerAlreadyRegistered);
|
||||
|
||||
// This makes the link better the 2 actors.
|
||||
aController->SetWorkerActor(workerActor);
|
||||
workerActor->SetController(aController);
|
||||
@ -399,29 +392,23 @@ void RemoteWorkerManager::ForEachActor(
|
||||
* search's starting position randomly.
|
||||
*
|
||||
* - When Fission is enabled, Shared Workers may have to be spawned into
|
||||
* different child process from the one where it has been registered from, and
|
||||
* that child process may be going to be marked as dead and shutdown.
|
||||
* different child process from the one where it has been registered from, and
|
||||
* that child process may be going to be marked as dead and shutdown.
|
||||
*
|
||||
* Spawning the workers in a random process makes the process selection criteria
|
||||
* a little tricky, as a candidate process may imminently shutdown due to a
|
||||
* remove worker actor unregistering
|
||||
* (see `ContentParent::UnregisterRemoveWorkerActor`).
|
||||
*
|
||||
* In `ContentParent::MaybeBeginShutdown` we only dispatch a runnable
|
||||
* to call `ContentParent::ShutDownProcess` if there are no registered remote
|
||||
* worker actors, and we ensure that the check for the number of registered
|
||||
* actors and the dispatching of the runnable are atomic. That happens on the
|
||||
* main thread, so here on the background thread, while
|
||||
* `ContentParent::mRemoteWorkerActorData` is locked, if `mCount` > 0, we can
|
||||
* register a remote worker actor "early" and guarantee that the corresponding
|
||||
* content process will not shutdown.
|
||||
* ContentParent provides a way to add a KeepAlive, which will prevent the
|
||||
* process from being shut down, through a ThreadsafeContentParentHandle in an
|
||||
* atomic way. This call will fail if the process is already being shut down.
|
||||
* When selecting a content process on the PBackground thread, we'll acquire the
|
||||
* KeepAlive in that way.
|
||||
*/
|
||||
RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActorInternal(
|
||||
RemoteWorkerManager::TargetActorAndKeepAlive
|
||||
RemoteWorkerManager::SelectTargetActorInternal(
|
||||
const RemoteWorkerData& aData, base::ProcessId aProcessId) const {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(!mChildActors.IsEmpty());
|
||||
|
||||
RemoteWorkerServiceParent* actor = nullptr;
|
||||
UniqueThreadsafeContentParentKeepAlive keepAlive;
|
||||
|
||||
const auto& workerRemoteType = aData.remoteType();
|
||||
|
||||
@ -436,11 +423,7 @@ RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActorInternal(
|
||||
// process with a pid equal to aProcessId if any, otherwise it would
|
||||
// start from a random actor in the mChildActors array, this guarantees
|
||||
// that we will choose that actor if it does also match the remote type.
|
||||
if (aContentHandle->MaybeRegisterRemoteWorkerActor(
|
||||
[&](uint32_t count, bool shutdownStarted) -> bool {
|
||||
return (count || !shutdownStarted) &&
|
||||
(aActor->OtherPid() == aProcessId || !actor);
|
||||
})) {
|
||||
if ((keepAlive = aContentHandle->TryAddKeepAlive())) {
|
||||
actor = aActor;
|
||||
return false;
|
||||
}
|
||||
@ -449,18 +432,19 @@ RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActorInternal(
|
||||
},
|
||||
workerRemoteType, IsServiceWorker(aData) ? Nothing() : Some(aProcessId));
|
||||
|
||||
return actor;
|
||||
return {actor, std::move(keepAlive)};
|
||||
}
|
||||
|
||||
RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor(
|
||||
const RemoteWorkerData& aData, base::ProcessId aProcessId) {
|
||||
RemoteWorkerManager::TargetActorAndKeepAlive
|
||||
RemoteWorkerManager::SelectTargetActor(const RemoteWorkerData& aData,
|
||||
base::ProcessId aProcessId) {
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
// System principal workers should run on the parent process.
|
||||
if (aData.principalInfo().type() == PrincipalInfo::TSystemPrincipalInfo) {
|
||||
MOZ_ASSERT(mParentActor);
|
||||
return mParentActor;
|
||||
return {mParentActor, nullptr};
|
||||
}
|
||||
|
||||
// Extension principal workers are allowed to run on the parent process
|
||||
@ -470,20 +454,20 @@ RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor(
|
||||
!StaticPrefs::extensions_webextensions_remote() &&
|
||||
HasExtensionPrincipal(aData)) {
|
||||
MOZ_ASSERT(mParentActor);
|
||||
return mParentActor;
|
||||
return {mParentActor, nullptr};
|
||||
}
|
||||
|
||||
// If e10s is off, use the parent process.
|
||||
if (!BrowserTabsRemoteAutostart()) {
|
||||
MOZ_ASSERT(mParentActor);
|
||||
return mParentActor;
|
||||
return {mParentActor, nullptr};
|
||||
}
|
||||
|
||||
// We shouldn't have to worry about content-principal parent-process workers.
|
||||
MOZ_ASSERT(aProcessId != base::GetCurrentProcId());
|
||||
|
||||
if (mChildActors.IsEmpty()) {
|
||||
return nullptr;
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
return SelectTargetActorInternal(aData, aProcessId);
|
||||
@ -521,12 +505,15 @@ RemoteWorkerManager::LaunchNewContentProcess(const RemoteWorkerData& aData) {
|
||||
})
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[](ContentParent* aContentParent) {
|
||||
[](UniqueContentParentKeepAlive&& aContentParent) {
|
||||
RefPtr<RemoteWorkerServiceParent> actor =
|
||||
aContentParent->GetRemoteWorkerServiceParent();
|
||||
MOZ_ASSERT(actor, "RemoteWorkerServiceParent not initialized?");
|
||||
return RemoteWorkerManager::LaunchProcessPromise::CreateAndResolve(
|
||||
actor, __func__);
|
||||
TargetActorAndKeepAlive{
|
||||
actor, UniqueContentParentKeepAliveToThreadsafe(
|
||||
std::move(aContentParent))},
|
||||
__func__);
|
||||
},
|
||||
[](nsresult aError) {
|
||||
return RemoteWorkerManager::LaunchProcessPromise::CreateAndReject(
|
||||
|
@ -61,19 +61,24 @@ class RemoteWorkerManager final {
|
||||
RemoteWorkerManager();
|
||||
~RemoteWorkerManager();
|
||||
|
||||
RemoteWorkerServiceParent* SelectTargetActor(const RemoteWorkerData& aData,
|
||||
base::ProcessId aProcessId);
|
||||
struct TargetActorAndKeepAlive {
|
||||
RefPtr<RemoteWorkerServiceParent> mActor;
|
||||
UniqueThreadsafeContentParentKeepAlive mKeepAlive;
|
||||
};
|
||||
|
||||
RemoteWorkerServiceParent* SelectTargetActorInternal(
|
||||
TargetActorAndKeepAlive SelectTargetActor(const RemoteWorkerData& aData,
|
||||
base::ProcessId aProcessId);
|
||||
|
||||
TargetActorAndKeepAlive SelectTargetActorInternal(
|
||||
const RemoteWorkerData& aData, base::ProcessId aProcessId) const;
|
||||
|
||||
void LaunchInternal(RemoteWorkerController* aController,
|
||||
RemoteWorkerServiceParent* aTargetActor,
|
||||
const RemoteWorkerData& aData,
|
||||
bool aRemoteWorkerAlreadyRegistered = false);
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive,
|
||||
const RemoteWorkerData& aData);
|
||||
|
||||
using LaunchProcessPromise =
|
||||
MozPromise<RefPtr<RemoteWorkerServiceParent>, nsresult, true>;
|
||||
MozPromise<TargetActorAndKeepAlive, nsresult, true>;
|
||||
RefPtr<LaunchProcessPromise> LaunchNewContentProcess(
|
||||
const RemoteWorkerData& aData);
|
||||
|
||||
|
@ -10,9 +10,7 @@
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/PFetchEventOpProxyParent.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -20,34 +18,9 @@ using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
class UnregisterActorRunnable final : public Runnable {
|
||||
public:
|
||||
explicit UnregisterActorRunnable(
|
||||
already_AddRefed<ThreadsafeContentParentHandle> aParent)
|
||||
: Runnable("UnregisterActorRunnable"), mContentHandle(aParent) {
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override {
|
||||
AssertIsOnMainThread();
|
||||
if (RefPtr<ContentParent> contentParent =
|
||||
mContentHandle->GetContentParent()) {
|
||||
contentParent->UnregisterRemoveWorkerActor();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<ThreadsafeContentParentHandle> mContentHandle;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RemoteWorkerParent::RemoteWorkerParent() {
|
||||
RemoteWorkerParent::RemoteWorkerParent(
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive)
|
||||
: mContentParentKeepAlive(std::move(aKeepAlive)) {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
}
|
||||
@ -62,21 +35,6 @@ RemoteWorkerServiceParent* RemoteWorkerParent::Manager() const {
|
||||
PRemoteWorkerParent::Manager());
|
||||
}
|
||||
|
||||
void RemoteWorkerParent::Initialize(bool aAlreadyRegistered) {
|
||||
RefPtr<ThreadsafeContentParentHandle> parent =
|
||||
Manager()->GetContentParentHandle();
|
||||
|
||||
// Parent is null if the child actor runs on the parent process.
|
||||
if (parent) {
|
||||
if (!aAlreadyRegistered) {
|
||||
parent->RegisterRemoteWorkerActor();
|
||||
}
|
||||
|
||||
NS_ReleaseOnMainThread("RemoteWorkerParent::Initialize ContentParent",
|
||||
parent.forget());
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<PFetchEventOpProxyParent>
|
||||
RemoteWorkerParent::AllocPFetchEventOpProxyParent(
|
||||
const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
|
||||
@ -88,15 +46,7 @@ void RemoteWorkerParent::ActorDestroy(IProtocol::ActorDestroyReason) {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
RefPtr<ThreadsafeContentParentHandle> parent =
|
||||
Manager()->GetContentParentHandle();
|
||||
|
||||
// Parent is null if the child actor runs on the parent process.
|
||||
if (parent) {
|
||||
RefPtr<UnregisterActorRunnable> r =
|
||||
new UnregisterActorRunnable(parent.forget());
|
||||
SchedulerGroup::Dispatch(r.forget());
|
||||
}
|
||||
mContentParentKeepAlive = nullptr;
|
||||
|
||||
if (mController) {
|
||||
mController->NoteDeadWorkerActor();
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_RemoteWorkerParent_h
|
||||
|
||||
#include "mozilla/dom/PRemoteWorkerParent.h"
|
||||
#include "mozilla/dom/UniqueContentParentKeepAlive.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
@ -25,9 +26,8 @@ class RemoteWorkerParent final : public PRemoteWorkerParent {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(RemoteWorkerParent, override);
|
||||
|
||||
RemoteWorkerParent();
|
||||
|
||||
void Initialize(bool aAlreadyRegistered = false);
|
||||
explicit RemoteWorkerParent(
|
||||
UniqueThreadsafeContentParentKeepAlive aKeepAlive);
|
||||
|
||||
void SetController(RemoteWorkerController* aController);
|
||||
|
||||
@ -58,6 +58,7 @@ class RemoteWorkerParent final : public PRemoteWorkerParent {
|
||||
|
||||
bool mDeleteSent = false;
|
||||
RefPtr<RemoteWorkerController> mController;
|
||||
UniqueThreadsafeContentParentKeepAlive mContentParentKeepAlive;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/FilePreferences.h"
|
||||
#include "mozilla/IOInterposer.h"
|
||||
#include "mozilla/NeverDestroyed.h"
|
||||
#include "mozilla/ProcessType.h"
|
||||
#include "mozilla/RDDProcessImpl.h"
|
||||
#include "mozilla/ipc/UtilityProcessImpl.h"
|
||||
@ -140,6 +141,7 @@ using mozilla::ipc::ProcessChild;
|
||||
|
||||
using mozilla::dom::ContentParent;
|
||||
using mozilla::dom::ContentProcess;
|
||||
using mozilla::dom::UniqueContentParentKeepAlive;
|
||||
|
||||
using mozilla::gmp::GMPProcessChild;
|
||||
|
||||
@ -739,22 +741,25 @@ void XRE_ShutdownChildProcess() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
ContentParent* gContentParent; // long-lived, manually refcounted
|
||||
UniqueContentParentKeepAlive& TestShellContentParent() {
|
||||
static NeverDestroyed<UniqueContentParentKeepAlive> sContentParent;
|
||||
return *sContentParent;
|
||||
}
|
||||
|
||||
TestShellParent* GetOrCreateTestShellParent() {
|
||||
if (!gContentParent) {
|
||||
if (!TestShellContentParent()) {
|
||||
// Use a "web" child process by default. File a bug if you don't like
|
||||
// this and you're sure you wouldn't be better off writing a "browser"
|
||||
// chrome mochitest where you can have multiple types of content
|
||||
// processes.
|
||||
RefPtr<ContentParent> parent =
|
||||
TestShellContentParent() =
|
||||
ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE);
|
||||
parent.forget(&gContentParent);
|
||||
} else if (gContentParent->IsShuttingDown()) {
|
||||
} else if (TestShellContentParent()->IsShuttingDown()) {
|
||||
return nullptr;
|
||||
}
|
||||
TestShellParent* tsp = gContentParent->GetTestShellSingleton();
|
||||
TestShellParent* tsp = TestShellContentParent()->GetTestShellSingleton();
|
||||
if (!tsp) {
|
||||
tsp = gContentParent->CreateTestShell();
|
||||
tsp = TestShellContentParent()->CreateTestShell();
|
||||
}
|
||||
return tsp;
|
||||
}
|
||||
@ -784,15 +789,15 @@ bool XRE_SendTestShellCommand(JSContext* aCx, JSString* aCommand,
|
||||
}
|
||||
|
||||
bool XRE_ShutdownTestShell() {
|
||||
if (!gContentParent) {
|
||||
if (!TestShellContentParent()) {
|
||||
return true;
|
||||
}
|
||||
bool ret = true;
|
||||
if (gContentParent->IsAlive()) {
|
||||
ret = gContentParent->DestroyTestShell(
|
||||
gContentParent->GetTestShellSingleton());
|
||||
if (TestShellContentParent()->IsAlive()) {
|
||||
ret = TestShellContentParent()->DestroyTestShell(
|
||||
TestShellContentParent()->GetTestShellSingleton());
|
||||
}
|
||||
NS_RELEASE(gContentParent);
|
||||
TestShellContentParent().reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user