Bug 1678416 - Create CCGCScheduler static methods to allow providing values on-demand to the scheduler, as well as provide a way to mock up the host for testing (see next patch) r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D95952
This commit is contained in:
Steve Fink 2020-12-16 00:12:15 +00:00
Родитель cbf69a75c1
Коммит 499c211405
2 изменённых файлов: 46 добавлений и 30 удалений

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

@ -94,6 +94,17 @@ struct CCRunnerStep {
};
class CCGCScheduler {
// Mockable functions to interface with the code being scheduled.
// Current time. In real usage, this will just return TimeStamp::Now(), but
// tests can reimplement it to return a value controlled by the test.
static inline mozilla::TimeStamp Now();
// Number of entries in the purple buffer (those objects whose ref counts
// have been decremented since the previous CC, roughly), and are therefore
// "suspected" of being members of cyclic garbage.
static inline uint32_t SuspectedCCObjects();
public:
// Parameter setting
@ -165,7 +176,7 @@ class CCGCScheduler {
uint32_t NoteForgetSkippableComplete(
TimeStamp aNow, uint32_t aSuspectedBeforeForgetSkippable) {
mLastForgetSkippableEndTime = aNow;
uint32_t suspected = nsCycleCollector_suspectedCount();
uint32_t suspected = SuspectedCCObjects();
mPreviousSuspectedCount = suspected;
mCleanupsSinceLastGC++;
return aSuspectedBeforeForgetSkippable - suspected;
@ -193,7 +204,7 @@ class CCGCScheduler {
// The CC was abandoned without running a slice, so we only did forget
// skippables. Prevent running another cycle soon.
void NoteForgetSkippableOnlyCycle() {
mLastForgetSkippableCycleEndTime = TimeStamp::Now();
mLastForgetSkippableCycleEndTime = Now();
}
void Shutdown() { mDidShutdown = true; }
@ -211,10 +222,10 @@ class CCGCScheduler {
inline TimeDuration ComputeInterSliceGCBudget(TimeStamp aDeadline,
TimeStamp aNow) const;
bool ShouldForgetSkippable(uint32_t aSuspected) const {
bool ShouldForgetSkippable() const {
// Only do a forget skippable if there are more than a few new objects
// or we're doing the initial forget skippables.
return ((mPreviousSuspectedCount + 100) <= aSuspected) ||
return ((mPreviousSuspectedCount + 100) <= SuspectedCCObjects()) ||
mCleanupsSinceLastGC < kMajorForgetSkippableCalls;
}
@ -222,10 +233,13 @@ class CCGCScheduler {
// garbage to cycle collect: either we just finished a GC, or the purple
// buffer is getting really big, or it's getting somewhat big and it has been
// too long since the last CC.
bool IsCCNeeded(uint32_t aSuspected,
TimeStamp aNow = TimeStamp::Now()) const {
return mNeedsFullCC || aSuspected > kCCPurpleLimit ||
(aSuspected > kCCForcedPurpleLimit && mLastCCEndTime &&
bool IsCCNeeded(TimeStamp aNow = Now()) const {
if (mNeedsFullCC) {
return true;
}
uint32_t suspected = SuspectedCCObjects();
return suspected > kCCPurpleLimit ||
(suspected > kCCForcedPurpleLimit && mLastCCEndTime &&
aNow - mLastCCEndTime > kCCForced);
}
@ -278,8 +292,7 @@ class CCGCScheduler {
void DeactivateCCRunner() { mCCRunnerState = CCRunnerState::Inactive; }
inline CCRunnerStep GetNextCCRunnerAction(TimeStamp aDeadline,
uint32_t aSuspected);
inline CCRunnerStep GetNextCCRunnerAction(TimeStamp aDeadline);
// aStartTimeStamp : when the ForgetSkippable timer fired. This may be some
// time ago, if an incremental GC needed to be finished.
@ -333,7 +346,7 @@ class CCGCScheduler {
js::SliceBudget CCGCScheduler::ComputeCCSliceBudget(
TimeStamp aDeadline, TimeStamp aCCBeginTime, TimeStamp aPrevSliceEndTime,
bool* aPreferShorterSlices) const {
TimeStamp now = TimeStamp::Now();
TimeStamp now = Now();
*aPreferShorterSlices =
aDeadline.IsNull() || (aDeadline - now) < kICCSliceBudget;
@ -398,7 +411,7 @@ bool CCGCScheduler::ShouldScheduleCC() const {
return false;
}
TimeStamp now = TimeStamp::Now();
TimeStamp now = Now();
// Don't run consecutive CCs too often.
if (mCleanupsSinceLastGC && !mLastCCEndTime.IsNull()) {
@ -417,11 +430,10 @@ bool CCGCScheduler::ShouldScheduleCC() const {
}
}
return IsCCNeeded(nsCycleCollector_suspectedCount(), now);
return IsCCNeeded(now);
}
CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
uint32_t aSuspected) {
CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline) {
struct StateDescriptor {
// When in this state, should we first check to see if we still have
// enough reason to CC?
@ -461,7 +473,7 @@ CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
return {CCRunnerAction::StopRunning, Yield};
}
TimeStamp now = TimeStamp::Now();
TimeStamp now = Now();
if (InIncrementalGC()) {
if (mCCBlockStart.IsNull()) {
@ -497,7 +509,7 @@ CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
// For states that aren't just continuations of previous states, check
// whether a CC is still needed (after doing various things to reduce the
// purple buffer).
if (desc.mCanAbortCC && !IsCCNeeded(aSuspected, now)) {
if (desc.mCanAbortCC && !IsCCNeeded(now)) {
// If we don't pass the threshold for wanting to cycle collect, stop now
// (after possibly doing a final ForgetSkippable).
mCCRunnerState = CCRunnerState::Canceled;
@ -505,7 +517,7 @@ CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
// Preserve the previous code's idea of when to check whether a
// ForgetSkippable should be fired.
if (desc.mTryFinalForgetSkippable && ShouldForgetSkippable(aSuspected)) {
if (desc.mTryFinalForgetSkippable && ShouldForgetSkippable()) {
// The Canceled state will make us StopRunning after this action is
// performed (see conditional at top of function).
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
@ -525,7 +537,7 @@ CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
mCCRunnerState = CCRunnerState::CleanupChildless;
}
if (ShouldForgetSkippable(aSuspected)) {
if (ShouldForgetSkippable()) {
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
}
@ -604,7 +616,7 @@ CCRunnerStep CCGCScheduler::GetNextCCRunnerAction(TimeStamp aDeadline,
};
}
js::SliceBudget CCGCScheduler::ComputeForgetSkippableBudget(
inline js::SliceBudget CCGCScheduler::ComputeForgetSkippableBudget(
TimeStamp aStartTimeStamp, TimeStamp aDeadline) {
if (mForgetSkippableFrequencyStartTime.IsNull()) {
mForgetSkippableFrequencyStartTime = aStartTimeStamp;

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

@ -114,6 +114,12 @@ static TimeDuration sGCUnnotifiedTotalTime;
static CCGCScheduler sScheduler;
inline TimeStamp mozilla::CCGCScheduler::Now() { return TimeStamp::Now(); }
inline uint32_t mozilla::CCGCScheduler::SuspectedCCObjects() {
return nsCycleCollector_suspectedCount();
}
struct CycleCollectorStats {
constexpr CycleCollectorStats() = default;
void Init();
@ -1120,14 +1126,14 @@ static void FinishAnyIncrementalGC() {
}
}
static void FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
TimeStamp aDeadline) {
static void FireForgetSkippable(bool aRemoveChildless, TimeStamp aDeadline) {
AUTO_PROFILER_TRACING_MARKER(
"CC", aDeadline.IsNull() ? "ForgetSkippable" : "IdleForgetSkippable",
GCCC);
TimeStamp startTimeStamp = TimeStamp::Now();
FinishAnyIncrementalGC();
uint32_t suspectedBefore = nsCycleCollector_suspectedCount();
js::SliceBudget budget =
sScheduler.ComputeForgetSkippableBudget(startTimeStamp, aDeadline);
bool earlyForgetSkippable = sScheduler.IsEarlyForgetSkippable();
@ -1135,7 +1141,7 @@ static void FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
earlyForgetSkippable);
TimeStamp now = TimeStamp::Now();
uint32_t removedPurples =
sScheduler.NoteForgetSkippableComplete(now, aSuspected);
sScheduler.NoteForgetSkippableComplete(now, suspectedBefore);
TimeDuration duration = now - startTimeStamp;
@ -1485,8 +1491,7 @@ void nsJSContext::BeginCycleCollectionCallback() {
// is particularly useful if we recently finished a GC.
if (sScheduler.IsEarlyForgetSkippable()) {
while (sScheduler.IsEarlyForgetSkippable()) {
FireForgetSkippable(nsCycleCollector_suspectedCount(), false,
TimeStamp());
FireForgetSkippable(false, TimeStamp());
}
sCCStats.AfterSyncForgetSkippable(startTime);
}
@ -1628,15 +1633,14 @@ static bool CCRunnerFired(TimeStamp aDeadline) {
// `Yield` in step.mYield.
CCRunnerStep step;
do {
uint32_t suspected = nsCycleCollector_suspectedCount();
step = sScheduler.GetNextCCRunnerAction(aDeadline, suspected);
step = sScheduler.GetNextCCRunnerAction(aDeadline);
switch (step.mAction) {
case CCRunnerAction::None:
break;
case CCRunnerAction::ForgetSkippable:
// 'Forget skippable' only, then end this invocation.
FireForgetSkippable(suspected, bool(step.mRemoveChildless), aDeadline);
FireForgetSkippable(bool(step.mRemoveChildless), aDeadline);
break;
case CCRunnerAction::CleanupContentUnbinder:
@ -1952,7 +1956,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
sScheduler.SetNeedsFullGC(false);
}
if (sScheduler.IsCCNeeded(nsCycleCollector_suspectedCount())) {
if (sScheduler.IsCCNeeded()) {
nsCycleCollector_dispatchDeferredDeletion();
}
@ -1984,7 +1988,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
[] { return sShuttingDown; });
}
if (sScheduler.IsCCNeeded(nsCycleCollector_suspectedCount())) {
if (sScheduler.IsCCNeeded()) {
nsCycleCollector_dispatchDeferredDeletion();
}