зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
cbf69a75c1
Коммит
499c211405
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче