Bug 1914191 - Part 2: Introduce the dom.ipc.processReuse.unusedGraceMs pref, r=smaug

If set to a non-zero value, this pref will change the process shutdown logic to
use an IdleTaskRunner to clean up a process when it is no longer in use, rather
than immediately marking the process as unable to be re-used.

This is most beneficial when runnning tests, which can sometimes rapidly
cycle between processes, inefficiently starting up and shutting them
down very rapidly.

Due some test failures due to tests depending on the old process re-use
behaviour, this pref is being landed disabled such that it can be enabled for
specific test suites.

Differential Revision: https://phabricator.services.mozilla.com/D220192
This commit is contained in:
Nika Layzell 2024-09-16 21:11:30 +00:00
Родитель 0a6112d539
Коммит f6d683ff8e
3 изменённых файлов: 93 добавлений и 13 удалений

Просмотреть файл

@ -747,7 +747,9 @@ void ContentParent::ReleaseCachedProcesses() {
}
for (const auto& cp : fixArray) {
if (cp->MaybeBeginShutDown(/* aIgnoreKeepAlivePref */ true)) {
cp->MaybeBeginShutDown(/* aImmediate */ true,
/* aIgnoreKeepAlivePref */ true);
if (cp->IsDead()) {
// Make sure that this process is no longer accessible from JS by its
// message manager.
cp->ShutDownMessageManager();
@ -2093,8 +2095,19 @@ void ContentParent::ActorDestroy(ActorDestroyReason why) {
UniqueContentParentKeepAlive ContentParent::TryAddKeepAlive(
uint64_t aBrowserId) {
return UniqueContentParentKeepAliveFromThreadsafe(
mThreadsafeHandle->TryAddKeepAlive(aBrowserId));
UniqueContentParentKeepAlive keepAlive =
UniqueContentParentKeepAliveFromThreadsafe(
mThreadsafeHandle->TryAddKeepAlive(aBrowserId));
// If we successfully added a KeepAlive, we can cancel any pending
// MaybeBeginShutDown call (as it will no longer begin process shutdown due to
// outstanding KeepAlives).
// This is just an optimization and the MaybeBeginShutDown call will be a
// no-op if it is called with the KeepAlive held.
if (keepAlive && mMaybeBeginShutdownRunner) {
mMaybeBeginShutdownRunner->Cancel();
mMaybeBeginShutdownRunner = nullptr;
}
return keepAlive;
}
UniqueContentParentKeepAlive ContentParent::AddKeepAlive(uint64_t aBrowserId) {
@ -2118,8 +2131,26 @@ void ContentParent::RemoveKeepAlive(uint64_t aBrowserId) {
MaybeBeginShutDown();
}
bool ContentParent::MaybeBeginShutDown(bool aIgnoreKeepAlivePref) {
void ContentParent::MaybeBeginShutDown(bool aImmediate,
bool aIgnoreKeepAlivePref) {
AssertIsOnMainThread();
MOZ_ASSERT(!aIgnoreKeepAlivePref || aImmediate,
"aIgnoreKeepAlivePref requires aImmediate");
// Don't bother waiting, even if `aImmediate` is not true, if the process
// can no longer be re-used (e.g. because it is dead, or we're in shutdown).
bool immediate =
aImmediate || IsDead() ||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) ||
StaticPrefs::dom_ipc_processReuse_unusedGraceMs() == 0;
// Clean up any scheduled idle task unless we schedule a new one.
auto cancelIdleTask = MakeScopeExit([&] {
if (mMaybeBeginShutdownRunner) {
mMaybeBeginShutdownRunner->Cancel();
mMaybeBeginShutdownRunner = nullptr;
}
});
{
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
@ -2127,7 +2158,7 @@ bool ContentParent::MaybeBeginShutDown(bool aIgnoreKeepAlivePref) {
// down. Return.
if (IsLaunching() ||
!mThreadsafeHandle->mKeepAlivesPerBrowserId.IsEmpty()) {
return false;
return;
}
// If we're not in main process shutdown, we might want to keep some content
@ -2151,14 +2182,51 @@ bool ContentParent::MaybeBeginShutDown(bool aIgnoreKeepAlivePref) {
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;
return;
}
}
// We're not keeping this process alive, begin shutdown.
mThreadsafeHandle->mShutdownStarted = true;
if (immediate) {
// We're not keeping this process alive, begin shutdown.
mThreadsafeHandle->mShutdownStarted = true;
}
}
// If we're not beginning shutdown immediately, make sure an idle task runner
// is scheduled to call us back. This delay is intended to avoid unnecessary
// process churn when a process becomes momentarily unused (which can happen
// frequently when running tests).
if (!immediate) {
// We want an idle task to call us back, don't cancel it.
cancelIdleTask.release();
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("MaybeBeginShutDown(%d) would begin shutdown, %s", OtherChildID(),
mMaybeBeginShutdownRunner ? "already delayed" : "delaying"));
if (!mMaybeBeginShutdownRunner) {
TimeDuration startDelay = TimeDuration::FromMilliseconds(
StaticPrefs::dom_ipc_processReuse_unusedGraceMs());
TimeDuration maxDelay = startDelay + TimeDuration::FromSeconds(1);
mMaybeBeginShutdownRunner = IdleTaskRunner::Create(
[self = RefPtr{this}](TimeStamp) -> bool {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("MaybeBeginShutDown(%d) resuming after delay",
self->OtherChildID()));
self->MaybeBeginShutDown(/* aImmediate */ true);
return true;
},
"ContentParent::IdleMaybeBeginShutdown", startDelay, maxDelay,
/* aMinimumUsefulBudget */ TimeDuration::FromMilliseconds(3),
/* aRepeating */ false, [] { return false; });
}
return;
}
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("MaybeBeginShutDown(%d) shutdown starting (%u bps)", OtherChildID(),
ManagedPBrowserParent().Count()));
MarkAsDead();
SignalImpendingShutdownToContentJS();
@ -2171,7 +2239,6 @@ bool ContentParent::MaybeBeginShutDown(bool aIgnoreKeepAlivePref) {
// All tabs are dead, we can fully begin shutting down.
AsyncSendShutDownMessage();
}
return true;
}
void ContentParent::StartSendShutdownTimer() {

Просмотреть файл

@ -25,6 +25,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DataMutex.h"
#include "mozilla/HalTypes.h"
#include "mozilla/IdleTaskRunner.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReportingProcess.h"
@ -398,14 +399,17 @@ class ContentParent final : public PContentParent,
* 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.
* By default when a process becomes unused, it will be kept alive for a short
* time, potentially allowing the process to be re-used.
*
* @param aImmediate If true, immediately begins shutdown if the process is
* eligible, without any grace period for process re-use.
* @param aIgnoreKeepAlivePref If true, the dom.ipc.keepProcessesAlive.*
* preferences will be ignored, for clean-up of
* cached processes.
* cached processes. Requires aImmediate.
*/
bool MaybeBeginShutDown(bool aIgnoreKeepAlivePref = false);
void MaybeBeginShutDown(bool aImmediate = false,
bool aIgnoreKeepAlivePref = false);
TestShellParent* CreateTestShell();
@ -1615,6 +1619,8 @@ class ContentParent final : public PContentParent,
// Cleared once startup is complete.
UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer;
RefPtr<IdleTaskRunner> mMaybeBeginShutdownRunner;
static uint32_t sMaxContentProcesses;
static uint32_t sPageLoadEventCounter;

Просмотреть файл

@ -3010,6 +3010,13 @@
value: false
mirror: always
# If non-zero, a grace delay (in milliseconds) during which unused content
# processes are kept available for re-use to avoid unnecessary process churn.
- name: dom.ipc.processReuse.unusedGraceMs
type: uint32_t
value: 0
mirror: always
# Process launch delay (in milliseconds).
- name: dom.ipc.processPrelaunch.delayMs
type: uint32_t