Bug 1859820 - Remove Global CCGCScheduler initialization r=jonco

Current static variable is just used as a placeholder for in-place
initialization. So use an appropriate static storage instead.

This has the advantage of *not* requiring global initialization,
removing the useless current one (which was overridden by the in-place
new anyway).

It also fixes an initialization order dependency \o/

Differential Revision: https://phabricator.services.mozilla.com/D191489
This commit is contained in:
serge-sans-paille 2023-10-23 17:23:14 +00:00
parent 1f518394a3
commit 612f27597c

View File

@ -106,7 +106,8 @@ static bool sIncrementalCC = false;
static bool sIsInitialized;
static bool sShuttingDown;
static CCGCScheduler sScheduler;
static CCGCScheduler* sScheduler = nullptr;
static std::aligned_storage_t<sizeof(*sScheduler)> sSchedulerStorage;
struct CycleCollectorStats {
constexpr CycleCollectorStats() = default;
@ -310,14 +311,14 @@ nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
} else if (!nsCRT::strcmp(aTopic, "memory-pressure-stop")) {
nsJSContext::SetLowMemoryState(false);
} else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
sScheduler.UserIsInactive();
sScheduler->UserIsInactive();
} else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
sScheduler.UserIsActive();
sScheduler->UserIsActive();
} else if (!nsCRT::strcmp(aTopic, "quit-application") ||
!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) ||
!nsCRT::strcmp(aTopic, "content-child-will-shutdown")) {
sShuttingDown = true;
sScheduler.Shutdown();
sScheduler->Shutdown();
}
return NS_OK;
@ -551,7 +552,7 @@ nsJSContext::~nsJSContext() {
void nsJSContext::Destroy() {
if (mGCOnDestruction) {
sScheduler.PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY, mWindowProxy);
sScheduler->PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY, mWindowProxy);
}
DropJSObjects(this);
@ -1052,7 +1053,7 @@ static void GarbageCollectImpl(JS::GCReason aReason,
return;
}
if (sScheduler.InIncrementalGC() && wantIncremental) {
if (sScheduler->InIncrementalGC() && wantIncremental) {
// We're in the middle of incremental GC. Do another slice.
JS::PrepareForIncrementalGC(cx);
JS::IncrementalGCSlice(cx, aReason, aBudget);
@ -1064,10 +1065,10 @@ static void GarbageCollectImpl(JS::GCReason aReason,
: JS::GCOptions::Normal;
if (!wantIncremental || aReason == JS::GCReason::FULL_GC_TIMER) {
sScheduler.SetNeedsFullGC();
sScheduler->SetNeedsFullGC();
}
if (sScheduler.NeedsFullGC()) {
if (sScheduler->NeedsFullGC()) {
JS::PrepareForFullGC(cx);
}
@ -1098,7 +1099,7 @@ void nsJSContext::RunIncrementalGCSlice(JS::GCReason aReason,
static void FinishAnyIncrementalGC() {
AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC);
if (sScheduler.InIncrementalGC()) {
if (sScheduler->InIncrementalGC()) {
AutoJSAPI jsapi;
jsapi.Init();
@ -1136,12 +1137,12 @@ static void FireForgetSkippable(bool aRemoveChildless, TimeStamp aDeadline) {
uint32_t suspectedBefore = nsCycleCollector_suspectedCount();
js::SliceBudget budget =
sScheduler.ComputeForgetSkippableBudget(startTimeStamp, aDeadline);
bool earlyForgetSkippable = sScheduler.IsEarlyForgetSkippable();
sScheduler->ComputeForgetSkippableBudget(startTimeStamp, aDeadline);
bool earlyForgetSkippable = sScheduler->IsEarlyForgetSkippable();
nsCycleCollector_forgetSkippable(budget, aRemoveChildless,
earlyForgetSkippable);
TimeStamp now = TimeStamp::Now();
uint32_t removedPurples = sScheduler.NoteForgetSkippableComplete(
uint32_t removedPurples = sScheduler->NoteForgetSkippableComplete(
now, suspectedBefore, nsCycleCollector_suspectedCount());
TimeDuration duration = now - startTimeStamp;
@ -1339,9 +1340,9 @@ void CycleCollectorStats::MaybeLogStats(const CycleCollectorResults& aResults,
mTotalSliceTime.ToMilliseconds(), aResults.mNumSlices, mSuspected,
aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
aResults.mFreedRefCounted, aResults.mFreedGCed,
sScheduler.mCCollectedWaitingForGC,
sScheduler.mCCollectedZonesWaitingForGC,
sScheduler.mLikelyShortLivingObjectsNeedingGC, gcMsg.get(),
sScheduler->mCCollectedWaitingForGC,
sScheduler->mCCollectedZonesWaitingForGC,
sScheduler->mLikelyShortLivingObjectsNeedingGC, gcMsg.get(),
mForgetSkippableBeforeCC, mMinForgetSkippableTime.ToMilliseconds(),
mMaxForgetSkippableTime.ToMilliseconds(),
mTotalForgetSkippableTime.ToMilliseconds() / aCleanups,
@ -1400,9 +1401,9 @@ void CycleCollectorStats::MaybeNotifyStats(
mMaxGCDuration.ToMilliseconds(), mMaxSkippableDuration.ToMilliseconds(),
mSuspected, aResults.mVisitedRefCounted, aResults.mVisitedGCed,
aResults.mFreedRefCounted, aResults.mFreedGCed,
sScheduler.mCCollectedWaitingForGC,
sScheduler.mCCollectedZonesWaitingForGC,
sScheduler.mLikelyShortLivingObjectsNeedingGC, aResults.mForcedGC,
sScheduler->mCCollectedWaitingForGC,
sScheduler->mCCollectedZonesWaitingForGC,
sScheduler->mLikelyShortLivingObjectsNeedingGC, aResults.mForcedGC,
mForgetSkippableBeforeCC, mMinForgetSkippableTime.ToMilliseconds(),
mMaxForgetSkippableTime.ToMilliseconds(),
mTotalForgetSkippableTime.ToMilliseconds() / aCleanups,
@ -1436,14 +1437,14 @@ void nsJSContext::PrepareForCycleCollectionSlice(CCReason aReason,
// Before we begin the cycle collection, make sure there is no active GC.
TimeStamp afterGCTime;
if (sScheduler.InIncrementalGC()) {
if (sScheduler->InIncrementalGC()) {
FinishAnyIncrementalGC();
afterGCTime = TimeStamp::Now();
}
if (!sScheduler.IsCollectingCycles()) {
if (!sScheduler->IsCollectingCycles()) {
sCCStats.PrepareForCycleCollection(beginTime);
sScheduler.NoteCCBegin(aReason, beginTime,
sScheduler->NoteCCBegin(aReason, beginTime,
sCCStats.mForgetSkippableBeforeCC,
sCCStats.mSuspected, sCCStats.mRemovedPurples);
}
@ -1464,7 +1465,7 @@ void nsJSContext::RunCycleCollectorSlice(CCReason aReason,
// Decide how long we want to budget for this slice.
if (sIncrementalCC) {
bool preferShorterSlices;
js::SliceBudget budget = sScheduler.ComputeCCSliceBudget(
js::SliceBudget budget = sScheduler->ComputeCCSliceBudget(
aDeadline, sCCStats.mBeginTime, sCCStats.mEndSliceTime,
TimeStamp::Now(), &preferShorterSlices);
nsCycleCollector_collectSlice(budget, aReason, preferShorterSlices);
@ -1509,8 +1510,8 @@ void nsJSContext::BeginCycleCollectionCallback(CCReason aReason) {
// Run forgetSkippable synchronously to reduce the size of the CC graph. This
// is particularly useful if we recently finished a GC.
if (sScheduler.IsEarlyForgetSkippable()) {
while (sScheduler.IsEarlyForgetSkippable()) {
if (sScheduler->IsEarlyForgetSkippable()) {
while (sScheduler->IsEarlyForgetSkippable()) {
FireForgetSkippable(false, TimeStamp());
}
sCCStats.AfterSyncForgetSkippable(startTime);
@ -1520,9 +1521,9 @@ void nsJSContext::BeginCycleCollectionCallback(CCReason aReason) {
return;
}
sScheduler.InitCCRunnerStateMachine(
sScheduler->InitCCRunnerStateMachine(
mozilla::CCGCScheduler::CCRunnerState::CycleCollecting, aReason);
sScheduler.EnsureCCRunner(kICCIntersliceDelay, kIdleICCSliceBudget);
sScheduler->EnsureCCRunner(kICCIntersliceDelay, kIdleICCSliceBudget);
}
// static
@ -1530,7 +1531,7 @@ void nsJSContext::EndCycleCollectionCallback(
const CycleCollectorResults& aResults) {
MOZ_ASSERT(NS_IsMainThread());
sScheduler.KillCCRunner();
sScheduler->KillCCRunner();
// Update timing information for the current slice before we log it, if
// we previously called PrepareForCycleCollectionSlice(). During shutdown
@ -1539,9 +1540,9 @@ void nsJSContext::EndCycleCollectionCallback(
TimeStamp endCCTimeStamp = TimeStamp::Now();
TimeDuration ccNowDuration = TimeBetween(sCCStats.mBeginTime, endCCTimeStamp);
TimeStamp prevCCEnd = sScheduler.GetLastCCEndTime();
TimeStamp prevCCEnd = sScheduler->GetLastCCEndTime();
sScheduler.NoteCCEnd(aResults, endCCTimeStamp, sCCStats.mMaxSliceTime);
sScheduler->NoteCCEnd(aResults, endCCTimeStamp, sCCStats.mMaxSliceTime);
// Log information about the CC via telemetry, JSON and the console.
@ -1559,13 +1560,13 @@ void nsJSContext::EndCycleCollectionCallback(
// If we need a GC after this CC (typically because lots of GCed objects or
// zones have been collected in the CC), schedule it.
if (sScheduler.NeedsGCAfterCC()) {
if (sScheduler->NeedsGCAfterCC()) {
MOZ_ASSERT(
TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration,
"A max duration ICC shouldn't reduce GC delay to 0");
sScheduler.PokeGC(JS::GCReason::CC_FINISHED, nullptr,
sScheduler->PokeGC(JS::GCReason::CC_FINISHED, nullptr,
TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay()) -
std::min(ccNowDuration, kMaxICCDuration));
@ -1594,7 +1595,7 @@ bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) {
// `Yield` in step.mYield.
CCRunnerStep step;
do {
step = sScheduler.AdvanceCCRunner(aDeadline, TimeStamp::Now(),
step = sScheduler->AdvanceCCRunner(aDeadline, TimeStamp::Now(),
nsCycleCollector_suspectedCount());
switch (step.mAction) {
case CCRunnerAction::None:
@ -1603,7 +1604,7 @@ bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) {
case CCRunnerAction::MinorGC:
JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
step.mParam.mReason);
sScheduler.NoteMinorGCEnd();
sScheduler->NoteMinorGCEnd();
break;
case CCRunnerAction::ForgetSkippable:
@ -1629,7 +1630,7 @@ bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) {
case CCRunnerAction::StopRunning:
// End this CC, either because we have run a cycle collection slice, or
// because a CC is no longer needed.
sScheduler.KillCCRunner();
sScheduler->KillCCRunner();
break;
}
@ -1643,13 +1644,13 @@ bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) {
// static
bool nsJSContext::HasHadCleanupSinceLastGC() {
return sScheduler.IsEarlyForgetSkippable(1);
return sScheduler->IsEarlyForgetSkippable(1);
}
// static
void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason,
mozilla::TimeStamp aDeadline) {
sScheduler.RunNextCollectorTimer(aReason, aDeadline);
sScheduler->RunNextCollectorTimer(aReason, aDeadline);
}
// static
@ -1692,13 +1693,13 @@ void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
return;
}
if (!sScheduler.IsUserActive()) {
if (sScheduler.InIncrementalGC() || sScheduler.IsCollectingCycles()) {
if (!sScheduler->IsUserActive()) {
if (sScheduler->InIncrementalGC() || sScheduler->IsCollectingCycles()) {
Maybe<TimeStamp> next = nsRefreshDriver::GetNextTickHint();
if (next.isSome()) {
// Try to not delay the next RefreshDriver tick, so give a reasonable
// deadline for collectors.
sScheduler.RunNextCollectorTimer(aReason, next.value());
sScheduler->RunNextCollectorTimer(aReason, next.value());
}
} else {
nsCOMPtr<nsIDocShell> shell = aDocShell;
@ -1729,7 +1730,7 @@ void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
// static
void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
TimeDuration aDelay) {
sScheduler.PokeGC(aReason, aObj, aDelay);
sScheduler->PokeGC(aReason, aObj, aDelay);
}
// static
@ -1742,7 +1743,7 @@ void nsJSContext::MaybePokeGC() {
JS::GCReason reason = JS::WantEagerMinorGC(rt);
if (reason != JS::GCReason::NO_REASON) {
MOZ_ASSERT(reason == JS::GCReason::EAGER_NURSERY_COLLECTION);
sScheduler.PokeMinorGC(reason);
sScheduler->PokeMinorGC(reason);
}
// Bug 1772638: For now, only do eager minor GCs. Eager major GCs regress some
@ -1757,7 +1758,7 @@ void nsJSContext::DoLowMemoryGC() {
nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
nsJSContext::ShrinkingGC);
nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE);
if (sScheduler.NeedsGCAfterCC()) {
if (sScheduler->NeedsGCAfterCC()) {
nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
nsJSContext::ShrinkingGC);
}
@ -1780,7 +1781,7 @@ void nsJSContext::LowMemoryGC() {
// static
void nsJSContext::MaybePokeCC() {
sScheduler.MaybePokeCC(TimeStamp::NowLoRes(),
sScheduler->MaybePokeCC(TimeStamp::NowLoRes(),
nsCycleCollector_suspectedCount());
}
@ -1793,7 +1794,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
switch (aProgress) {
case JS::GC_CYCLE_BEGIN: {
// Prevent cycle collections and shrinking during incremental GC.
sScheduler.NoteGCBegin(aDesc.reason_);
sScheduler->NoteGCBegin(aDesc.reason_);
sCurrentGCStartTime = TimeStamp::Now();
break;
}
@ -1816,10 +1817,10 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
}
}
sScheduler.NoteGCEnd();
sScheduler->NoteGCEnd();
// May need to kill the GC runner
sScheduler.KillGCRunner();
sScheduler->KillGCRunner();
nsJSContext::MaybePokeCC();
@ -1827,16 +1828,16 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
bool freeDirty = false;
#endif
if (aDesc.isZone_) {
sScheduler.PokeFullGC();
sScheduler->PokeFullGC();
} else {
#if defined(MOZ_MEMORY)
freeDirty = true;
#endif
sScheduler.SetNeedsFullGC(false);
sScheduler.KillFullGCTimer();
sScheduler->SetNeedsFullGC(false);
sScheduler->KillFullGCTimer();
}
if (sScheduler.IsCCNeeded(TimeStamp::Now(),
if (sScheduler->IsCCNeeded(TimeStamp::Now(),
nsCycleCollector_suspectedCount()) !=
CCReason::NO_REASON) {
#if defined(MOZ_MEMORY)
@ -1863,19 +1864,19 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
break;
case JS::GC_SLICE_END:
sScheduler.NoteGCSliceEnd(aDesc.lastSliceStart(aCx),
sScheduler->NoteGCSliceEnd(aDesc.lastSliceStart(aCx),
aDesc.lastSliceEnd(aCx));
if (sShuttingDown) {
sScheduler.KillGCRunner();
sScheduler->KillGCRunner();
} else {
// If incremental GC wasn't triggered by GCTimerFired, we may not have a
// runner to ensure all the slices are handled. So, create the runner
// here.
sScheduler.EnsureGCRunner(0);
sScheduler->EnsureGCRunner(0);
}
if (sScheduler.IsCCNeeded(TimeStamp::Now(),
if (sScheduler->IsCCNeeded(TimeStamp::Now(),
nsCycleCollector_suspectedCount()) !=
CCReason::NO_REASON) {
nsCycleCollector_dispatchDeferredDeletion();
@ -1913,14 +1914,13 @@ void nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
JSObject* nsJSContext::GetWindowProxy() { return mWindowProxy; }
void nsJSContext::LikelyShortLivingObjectCreated() {
++sScheduler.mLikelyShortLivingObjectsNeedingGC;
++sScheduler->mLikelyShortLivingObjectsNeedingGC;
}
void mozilla::dom::StartupJSEnvironment() {
// initialize all our statics, so that we can restart XPCOM
sIsInitialized = false;
sShuttingDown = false;
new (&sScheduler) CCGCScheduler(); // Reset the scheduler state.
sCCStats.Init();
}
@ -1982,7 +1982,7 @@ static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName,
int32_t pref = Preferences::GetInt(aPrefName, -1);
// handle overflow and negative pref values
if (pref > 0 && pref < 100000) {
sScheduler.SetActiveIntersliceGCBudget(
sScheduler->SetActiveIntersliceGCBudget(
TimeDuration::FromMilliseconds(pref));
SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS, pref);
} else {
@ -2055,7 +2055,7 @@ static bool ConsumeStream(JSContext* aCx, JS::Handle<JSObject*> aObj,
static js::SliceBudget CreateGCSliceBudget(JS::GCReason aReason,
int64_t aMillis) {
return sScheduler.CreateGCSliceBudget(
return sScheduler->CreateGCSliceBudget(
mozilla::TimeDuration::FromMilliseconds(aMillis), false, false);
}
@ -2070,6 +2070,9 @@ void nsJSContext::EnsureStatics() {
// Let's make sure that our main thread is the same as the xpcom main thread.
MOZ_ASSERT(NS_IsMainThread());
sScheduler =
new (&sSchedulerStorage) CCGCScheduler(); // Reset the scheduler state.
AutoJSAPI jsapi;
jsapi.Init();
@ -2228,7 +2231,7 @@ void nsJSContext::EnsureStatics() {
void mozilla::dom::ShutdownJSEnvironment() {
sShuttingDown = true;
sScheduler.Shutdown();
sScheduler->Shutdown();
}
AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport* aReport)