Bug 1692308 - Merge sGCTimer + sInterSliceGCRunner -> sGCRunner

Differential Revision: https://phabricator.services.mozilla.com/D104881
This commit is contained in:
Steve Fink
2021-04-29 23:12:04 +00:00
parent 320f1401ab
commit ec900623ef
3 changed files with 112 additions and 81 deletions

View File

@@ -92,11 +92,10 @@ using namespace mozilla::dom;
# undef CompareString
#endif
static nsITimer* sGCTimer;
static nsITimer* sShrinkingGCTimer;
static StaticRefPtr<IdleTaskRunner> sCCRunner;
static nsITimer* sFullGCTimer;
static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
static StaticRefPtr<IdleTaskRunner> sGCRunner;
static JS::GCSliceCallback sPrevGCSliceCallback;
@@ -285,11 +284,10 @@ static TimeDuration GetCollectionTimeDelta() {
}
static void KillTimers() {
nsJSContext::KillGCTimer();
nsJSContext::KillShrinkingGCTimer();
nsJSContext::KillCCRunner();
nsJSContext::KillFullGCTimer();
nsJSContext::KillInterSliceGCRunner();
nsJSContext::KillGCRunner();
}
class nsJSEnvironmentObserver final : public nsIObserver {
@@ -1079,8 +1077,6 @@ void nsJSContext::GarbageCollectNow(JS::GCReason aReason,
MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
KillGCTimer();
// We use danger::GetJSContext() since AutoJSAPI will assert if the current
// thread's context is null (such as during shutdown).
JSContext* cx = danger::GetJSContext();
@@ -1108,6 +1104,9 @@ void nsJSContext::GarbageCollectNow(JS::GCReason aReason,
}
if (aIncremental == IncrementalGC) {
// Incremental GC slices will be triggered by the sGCRunner. If one doesn't
// already exist, create it in the GC_SLICE_END callback for the first
// slice being executed here.
JS::StartIncrementalGC(cx, gckind, aReason, aSliceMillis);
} else {
JS::NonIncrementalGC(cx, gckind, aReason);
@@ -1549,17 +1548,30 @@ void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
}
// static
bool InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData) {
bool GCRunnerFired(TimeStamp aDeadline, void* /* aClosure */) {
MOZ_ASSERT(!sShuttingDown, "GCRunner still alive during shutdown");
GCRunnerStep step = sScheduler.GetNextGCRunnerAction(aDeadline);
switch (step.mAction) {
case GCRunnerAction::None:
nsJSContext::KillGCRunner();
return false;
case GCRunnerAction::MajorGC:
case GCRunnerAction::GCSlice:
break;
}
// Run a GC slice, possibly the first one of a major GC.
MOZ_ASSERT(sScheduler.mActiveIntersliceGCBudget);
TimeStamp startTimeStamp = TimeStamp::Now();
TimeDuration budget =
sScheduler.ComputeInterSliceGCBudget(aDeadline, startTimeStamp);
TimeDuration duration = sGCUnnotifiedTotalTime;
uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
nsJSContext::GarbageCollectNow(
aData ? static_cast<JS::GCReason>(reason) : JS::GCReason::INTER_SLICE_GC,
nsJSContext::IncrementalGC, nsJSContext::NonShrinkingGC,
budget.ToMilliseconds());
nsJSContext::GarbageCollectNow(step.mReason, nsJSContext::IncrementalGC,
nsJSContext::NonShrinkingGC,
budget.ToMilliseconds());
sGCUnnotifiedTotalTime = TimeDuration();
TimeStamp now = TimeStamp::Now();
@@ -1590,29 +1602,6 @@ bool InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData) {
return JS::IncrementalGCHasForegroundWork(cx);
}
// static
void GCTimerFired(nsITimer* aTimer, void* aClosure) {
nsJSContext::KillGCTimer();
if (sShuttingDown) {
nsJSContext::KillInterSliceGCRunner();
return;
}
if (sInterSliceGCRunner) {
return;
}
// Now start the actual GC after initial timer has fired.
sInterSliceGCRunner = IdleTaskRunner::Create(
[aClosure](TimeStamp aDeadline) {
return InterSliceGCRunnerFired(aDeadline, aClosure);
},
"GCTimerFired::InterSliceGCRunnerFired", 0,
StaticPrefs::javascript_options_gc_delay_interslice(),
sScheduler.mActiveIntersliceGCBudget.ToMilliseconds(), true,
[] { return sShuttingDown; });
}
// static
void ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure) {
nsJSContext::KillShrinkingGCTimer();
@@ -1708,20 +1697,14 @@ void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason,
return;
}
if (sGCTimer) {
if (aReason == JS::GCReason::DOM_WINDOW_UTILS) {
// Force full GCs when called from reftests so that we collect dead zones
// that have not been scheduled for collection.
sScheduler.SetNeedsFullGC();
}
GCTimerFired(nullptr, reinterpret_cast<void*>(aReason));
return;
}
RefPtr<IdleTaskRunner> runner;
if (sInterSliceGCRunner) {
sInterSliceGCRunner->SetIdleDeadline(aDeadline);
runner = sInterSliceGCRunner;
RefPtr<IdleTaskRunner> runnable;
if (sGCRunner) {
// If the GC has already started, we will just run the next slice here and
// this call will have no effect (the reason will be ignored, and the
// trigger will be cleared at the end of the GC.)
sScheduler.SetWantMajorGC(aReason);
sGCRunner->SetIdleDeadline(aDeadline);
runnable = sGCRunner;
} else {
// Check the CC timers after the GC timers, because the CC timers won't do
// anything if a GC is in progress.
@@ -1731,12 +1714,12 @@ void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason,
if (sCCRunner) {
sCCRunner->SetIdleDeadline(aDeadline);
runner = sCCRunner;
runnable = sCCRunner;
}
}
if (runner) {
runner->Run();
if (runnable) {
runnable->Run();
}
}
@@ -1812,11 +1795,13 @@ void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
sScheduler.SetNeedsFullGC();
}
if (sGCTimer || sInterSliceGCRunner) {
// There's already a timer for GC'ing, just return
if (sGCRunner) {
// There's already a runner for GC'ing, just return
return;
}
sScheduler.SetWantMajorGC(aReason);
if (sCCRunner) {
// Make sure CC is called regardless of the size of the purple buffer, and
// GC after it.
@@ -1826,14 +1811,21 @@ void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
static bool first = true;
NS_NewTimerWithFuncCallback(
&sGCTimer, GCTimerFired, reinterpret_cast<void*>(aReason),
uint32_t delay =
aDelay ? aDelay
: (first ? StaticPrefs::javascript_options_gc_delay_first()
: StaticPrefs::javascript_options_gc_delay()),
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, "GCTimerFired");
: StaticPrefs::javascript_options_gc_delay());
first = false;
sGCRunner = IdleTaskRunner::Create(
[](TimeStamp aDeadline) { return GCRunnerFired(aDeadline, nullptr); },
"GCRunnerFired",
// Wait for javascript.options.gc_delay, then start looking for idle time
// to run the initial GC slice. Wait at most the interslice GC delay
// before forcing a run.
delay, StaticPrefs::javascript_options_gc_delay_interslice(),
sScheduler.mActiveIntersliceGCBudget.ToMilliseconds(), true,
[] { return sShuttingDown; });
}
// static
@@ -1866,14 +1858,6 @@ void nsJSContext::MaybePokeCC() {
}
}
// static
void nsJSContext::KillGCTimer() {
if (sGCTimer) {
sGCTimer->Cancel();
NS_RELEASE(sGCTimer);
}
}
void nsJSContext::KillFullGCTimer() {
if (sFullGCTimer) {
sFullGCTimer->Cancel();
@@ -1881,10 +1865,10 @@ void nsJSContext::KillFullGCTimer() {
}
}
void nsJSContext::KillInterSliceGCRunner() {
if (sInterSliceGCRunner) {
sInterSliceGCRunner->Cancel();
sInterSliceGCRunner = nullptr;
void nsJSContext::KillGCRunner() {
if (sGCRunner) {
sGCRunner->Cancel();
sGCRunner = nullptr;
}
}
@@ -1941,8 +1925,8 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
sScheduler.NoteGCEnd();
sIsCompactingOnUserInactive = false;
// May need to kill the inter-slice GC runner
nsJSContext::KillInterSliceGCRunner();
// May need to kill the GC runner
nsJSContext::KillGCRunner();
nsJSContext::MaybePokeCC();
@@ -1975,17 +1959,19 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
aDesc.lastSliceEnd(aCx) - aDesc.lastSliceStart(aCx);
if (sShuttingDown || aDesc.isComplete_) {
nsJSContext::KillInterSliceGCRunner();
} else if (!sInterSliceGCRunner) {
nsJSContext::KillGCRunner();
} else if (!sGCRunner) {
// 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.
sInterSliceGCRunner = IdleTaskRunner::Create(
sGCRunner = IdleTaskRunner::Create(
[](TimeStamp aDeadline) {
return InterSliceGCRunnerFired(aDeadline, nullptr);
return GCRunnerFired(aDeadline, nullptr);
},
"DOMGCSliceCallback::InterSliceGCRunnerFired", 0,
StaticPrefs::javascript_options_gc_delay_interslice(),
"DOMGCSliceCallback::GCRunnerFired",
// Start immediately looking for idle time, waiting at most the
// interslice GC delay before forcing a run.
0, StaticPrefs::javascript_options_gc_delay_interslice(),
sScheduler.mActiveIntersliceGCBudget.ToMilliseconds(), true,
[] { return sShuttingDown; });
}
@@ -2031,7 +2017,7 @@ void nsJSContext::LikelyShortLivingObjectCreated() {
void mozilla::dom::StartupJSEnvironment() {
// initialize all our statics, so that we can restart XPCOM
sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
sShrinkingGCTimer = sFullGCTimer = nullptr;
sIsInitialized = false;
sShuttingDown = false;
new (&sScheduler) CCGCScheduler(); // Reset the scheduler state.