зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1703443 - pt 14. Pass aSuspectedCCObjects into CC scheduling code r=smaug
Pass this value in also and remove the need for duplicate inlined definitions between Firefox and gtest. Differential Revision: https://phabricator.services.mozilla.com/D119363
This commit is contained in:
Родитель
bfe8bd0b67
Коммит
ae50acaba7
|
@ -10,11 +10,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
// This definition must match the one in nsJSEnvironment.cpp
|
||||
uint32_t CCGCScheduler::SuspectedCCObjects() {
|
||||
return nsCycleCollector_suspectedCount();
|
||||
}
|
||||
|
||||
void CCGCScheduler::FullGCTimerFired(nsITimer* aTimer) {
|
||||
KillFullGCTimer();
|
||||
|
||||
|
@ -330,12 +325,12 @@ void CCGCScheduler::EnsureCCRunner(TimeDuration aDelay, TimeDuration aBudget) {
|
|||
}
|
||||
}
|
||||
|
||||
void CCGCScheduler::MaybePokeCC(TimeStamp aNow) {
|
||||
void CCGCScheduler::MaybePokeCC(TimeStamp aNow, uint32_t aSuspectedCCObjects) {
|
||||
if (mCCRunner || mDidShutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldScheduleCC(aNow)) {
|
||||
if (ShouldScheduleCC(aNow, aSuspectedCCObjects)) {
|
||||
// We can kill some objects before running forgetSkippable.
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
|
||||
|
|
|
@ -108,15 +108,6 @@ struct CCRunnerStep {
|
|||
|
||||
class CCGCScheduler {
|
||||
public:
|
||||
// Mockable functions to interface with the code being scheduled. They
|
||||
// are unused in CCGCScheduler.cpp but must be defined there anyway. Keep
|
||||
// the definitions synchronised.
|
||||
|
||||
// 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();
|
||||
|
||||
static bool CCRunnerFired(TimeStamp aDeadline);
|
||||
|
||||
// Parameter setting
|
||||
|
@ -147,7 +138,7 @@ class CCGCScheduler {
|
|||
void PokeGC(JS::GCReason aReason, JSObject* aObj, uint32_t aDelay = 0);
|
||||
void PokeShrinkingGC();
|
||||
void PokeFullGC();
|
||||
void MaybePokeCC(TimeStamp aNow);
|
||||
void MaybePokeCC(TimeStamp aNow, uint32_t aSuspectedCCObjects);
|
||||
|
||||
void UserIsInactive();
|
||||
void UserIsActive();
|
||||
|
@ -265,13 +256,13 @@ class CCGCScheduler {
|
|||
void UnblockCC() { mCCBlockStart = TimeStamp(); }
|
||||
|
||||
// Returns the number of purple buffer items that were processed and removed.
|
||||
uint32_t NoteForgetSkippableComplete(
|
||||
TimeStamp aNow, uint32_t aSuspectedBeforeForgetSkippable) {
|
||||
uint32_t NoteForgetSkippableComplete(TimeStamp aNow,
|
||||
uint32_t aSuspectedBeforeForgetSkippable,
|
||||
uint32_t aSuspectedCCObjects) {
|
||||
mLastForgetSkippableEndTime = aNow;
|
||||
uint32_t suspected = SuspectedCCObjects();
|
||||
mPreviousSuspectedCount = suspected;
|
||||
mPreviousSuspectedCount = aSuspectedCCObjects;
|
||||
mCleanupsSinceLastGC++;
|
||||
return aSuspectedBeforeForgetSkippable - suspected;
|
||||
return aSuspectedBeforeForgetSkippable - aSuspectedCCObjects;
|
||||
}
|
||||
|
||||
// After collecting cycles, record the results that are used in scheduling
|
||||
|
@ -318,10 +309,10 @@ class CCGCScheduler {
|
|||
inline TimeDuration ComputeInterSliceGCBudget(TimeStamp aDeadline,
|
||||
TimeStamp aNow) const;
|
||||
|
||||
bool ShouldForgetSkippable() const {
|
||||
bool ShouldForgetSkippable(uint32_t aSuspectedCCObjects) 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) <= SuspectedCCObjects()) ||
|
||||
return ((mPreviousSuspectedCount + 100) <= aSuspectedCCObjects) ||
|
||||
mCleanupsSinceLastGC < kMajorForgetSkippableCalls;
|
||||
}
|
||||
|
||||
|
@ -329,17 +320,17 @@ 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(TimeStamp aNow) const {
|
||||
bool IsCCNeeded(TimeStamp aNow, uint32_t aSuspectedCCObjects) const {
|
||||
if (mNeedsFullCC) {
|
||||
return true;
|
||||
}
|
||||
uint32_t suspected = SuspectedCCObjects();
|
||||
return suspected > kCCPurpleLimit ||
|
||||
(suspected > kCCForcedPurpleLimit && mLastCCEndTime &&
|
||||
return aSuspectedCCObjects > kCCPurpleLimit ||
|
||||
(aSuspectedCCObjects > kCCForcedPurpleLimit && mLastCCEndTime &&
|
||||
aNow - mLastCCEndTime > kCCForced);
|
||||
}
|
||||
|
||||
inline bool ShouldScheduleCC(TimeStamp aNow) const;
|
||||
inline bool ShouldScheduleCC(TimeStamp aNow,
|
||||
uint32_t aSuspectedCCObjects) const;
|
||||
|
||||
// If we collected a substantial amount of cycles, poke the GC since more
|
||||
// objects might be unreachable now.
|
||||
|
@ -394,7 +385,8 @@ class CCGCScheduler {
|
|||
|
||||
inline GCRunnerStep GetNextGCRunnerAction(TimeStamp aDeadline);
|
||||
|
||||
inline CCRunnerStep AdvanceCCRunner(TimeStamp aDeadline, TimeStamp aNow);
|
||||
inline CCRunnerStep AdvanceCCRunner(TimeStamp aDeadline, TimeStamp aNow,
|
||||
uint32_t aSuspectedCCObjects);
|
||||
|
||||
// aStartTimeStamp : when the ForgetSkippable timer fired. This may be some
|
||||
// time ago, if an incremental GC needed to be finished.
|
||||
|
@ -525,7 +517,8 @@ inline TimeDuration CCGCScheduler::ComputeInterSliceGCBudget(
|
|||
return std::max(budget, maxSliceGCBudget.MultDouble(percentOfBlockedTime));
|
||||
}
|
||||
|
||||
bool CCGCScheduler::ShouldScheduleCC(TimeStamp aNow) const {
|
||||
bool CCGCScheduler::ShouldScheduleCC(TimeStamp aNow,
|
||||
uint32_t aSuspectedCCObjects) const {
|
||||
if (!mHasRunGC) {
|
||||
return false;
|
||||
}
|
||||
|
@ -547,11 +540,11 @@ bool CCGCScheduler::ShouldScheduleCC(TimeStamp aNow) const {
|
|||
}
|
||||
}
|
||||
|
||||
return IsCCNeeded(aNow);
|
||||
return IsCCNeeded(aNow, aSuspectedCCObjects);
|
||||
}
|
||||
|
||||
CCRunnerStep CCGCScheduler::AdvanceCCRunner(TimeStamp aDeadline,
|
||||
TimeStamp aNow) {
|
||||
CCRunnerStep CCGCScheduler::AdvanceCCRunner(TimeStamp aDeadline, TimeStamp aNow,
|
||||
uint32_t aSuspectedCCObjects) {
|
||||
struct StateDescriptor {
|
||||
// When in this state, should we first check to see if we still have
|
||||
// enough reason to CC?
|
||||
|
@ -625,7 +618,7 @@ CCRunnerStep CCGCScheduler::AdvanceCCRunner(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(aNow)) {
|
||||
if (desc.mCanAbortCC && !IsCCNeeded(aNow, aSuspectedCCObjects)) {
|
||||
// If we don't pass the threshold for wanting to cycle collect, stop now
|
||||
// (after possibly doing a final ForgetSkippable).
|
||||
mCCRunnerState = CCRunnerState::Canceled;
|
||||
|
@ -633,7 +626,8 @@ CCRunnerStep CCGCScheduler::AdvanceCCRunner(TimeStamp aDeadline,
|
|||
|
||||
// Preserve the previous code's idea of when to check whether a
|
||||
// ForgetSkippable should be fired.
|
||||
if (desc.mTryFinalForgetSkippable && ShouldForgetSkippable()) {
|
||||
if (desc.mTryFinalForgetSkippable &&
|
||||
ShouldForgetSkippable(aSuspectedCCObjects)) {
|
||||
// The Canceled state will make us StopRunning after this action is
|
||||
// performed (see conditional at top of function).
|
||||
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
|
||||
|
@ -653,7 +647,7 @@ CCRunnerStep CCGCScheduler::AdvanceCCRunner(TimeStamp aDeadline,
|
|||
mCCRunnerState = CCRunnerState::CleanupChildless;
|
||||
}
|
||||
|
||||
if (ShouldForgetSkippable()) {
|
||||
if (ShouldForgetSkippable(aSuspectedCCObjects)) {
|
||||
return {CCRunnerAction::ForgetSkippable, Yield, KeepChildless};
|
||||
}
|
||||
|
||||
|
|
|
@ -101,11 +101,6 @@ static bool sShuttingDown;
|
|||
|
||||
static CCGCScheduler sScheduler;
|
||||
|
||||
// This definition must match the one in CCGCScheduler.cpp
|
||||
uint32_t mozilla::CCGCScheduler::SuspectedCCObjects() {
|
||||
return nsCycleCollector_suspectedCount();
|
||||
}
|
||||
|
||||
struct CycleCollectorStats {
|
||||
constexpr CycleCollectorStats() = default;
|
||||
void Init();
|
||||
|
@ -1103,8 +1098,8 @@ static void FireForgetSkippable(bool aRemoveChildless, TimeStamp aDeadline) {
|
|||
nsCycleCollector_forgetSkippable(budget, aRemoveChildless,
|
||||
earlyForgetSkippable);
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
uint32_t removedPurples =
|
||||
sScheduler.NoteForgetSkippableComplete(now, suspectedBefore);
|
||||
uint32_t removedPurples = sScheduler.NoteForgetSkippableComplete(
|
||||
now, suspectedBefore, nsCycleCollector_suspectedCount());
|
||||
|
||||
TimeDuration duration = now - startTimeStamp;
|
||||
|
||||
|
@ -1522,7 +1517,8 @@ 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:
|
||||
break;
|
||||
|
@ -1669,7 +1665,9 @@ void nsJSContext::LowMemoryGC() {
|
|||
}
|
||||
|
||||
// static
|
||||
void nsJSContext::MaybePokeCC() { sScheduler.MaybePokeCC(TimeStamp::Now()); }
|
||||
void nsJSContext::MaybePokeCC() {
|
||||
sScheduler.MaybePokeCC(TimeStamp::Now(), nsCycleCollector_suspectedCount());
|
||||
}
|
||||
|
||||
static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
|
||||
const JS::GCDescription& aDesc) {
|
||||
|
@ -1716,7 +1714,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
|
|||
sScheduler.KillGCRunner();
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
sScheduler.MaybePokeCC(now);
|
||||
sScheduler.MaybePokeCC(now, nsCycleCollector_suspectedCount());
|
||||
|
||||
if (aDesc.isZone_) {
|
||||
sScheduler.PokeFullGC();
|
||||
|
@ -1725,7 +1723,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
|
|||
sScheduler.KillFullGCTimer();
|
||||
}
|
||||
|
||||
if (sScheduler.IsCCNeeded(now)) {
|
||||
if (sScheduler.IsCCNeeded(now, nsCycleCollector_suspectedCount())) {
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
|
||||
|
@ -1750,7 +1748,8 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
|
|||
sScheduler.EnsureGCRunner(0);
|
||||
}
|
||||
|
||||
if (sScheduler.IsCCNeeded(TimeStamp::Now())) {
|
||||
if (sScheduler.IsCCNeeded(TimeStamp::Now(),
|
||||
nsCycleCollector_suspectedCount())) {
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,7 @@ static TimeStamp Now() { return sNow; }
|
|||
|
||||
static uint32_t sSuspected = 0;
|
||||
|
||||
inline uint32_t mozilla::CCGCScheduler::SuspectedCCObjects() {
|
||||
return sSuspected;
|
||||
}
|
||||
static uint32_t SuspectedCCObjects() { return sSuspected; }
|
||||
static void SetNumSuspected(uint32_t n) { sSuspected = n; }
|
||||
static void SuspectMore(uint32_t n) { sSuspected += n; }
|
||||
|
||||
|
@ -54,7 +52,7 @@ void TestGC::Run(int aNumSlices) {
|
|||
// Running the GC should not influence whether a CC is currently seen as
|
||||
// needed. But the first time we run GC, it will be false; later, we will
|
||||
// have run a GC and set it to true.
|
||||
bool neededCCAtStartOfGC = mScheduler.IsCCNeeded(Now());
|
||||
bool neededCCAtStartOfGC = mScheduler.IsCCNeeded(Now(), SuspectedCCObjects());
|
||||
|
||||
mScheduler.NoteGCBegin();
|
||||
|
||||
|
@ -67,7 +65,8 @@ void TestGC::Run(int aNumSlices) {
|
|||
// Pretend the GC took exactly the budget.
|
||||
AdvanceTime(budget);
|
||||
|
||||
EXPECT_EQ(mScheduler.IsCCNeeded(Now()), neededCCAtStartOfGC);
|
||||
EXPECT_EQ(mScheduler.IsCCNeeded(Now(), SuspectedCCObjects()),
|
||||
neededCCAtStartOfGC);
|
||||
|
||||
// Mutator runs for 1 second.
|
||||
AdvanceTime(kOneSecond);
|
||||
|
@ -105,7 +104,7 @@ class TestCC {
|
|||
|
||||
void TestCC::MaybePokeCC() {
|
||||
// nsJSContext::MaybePokeCC
|
||||
EXPECT_TRUE(mScheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_TRUE(mScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
|
||||
mScheduler.InitCCRunnerStateMachine(CCRunnerState::ReducePurple);
|
||||
EXPECT_TRUE(mScheduler.IsEarlyForgetSkippable());
|
||||
|
@ -118,7 +117,8 @@ void TestCC::TimerFires(int aNumSlices) {
|
|||
while (true) {
|
||||
SuspectMore(1000);
|
||||
TimeStamp idleDeadline = Now() + kOneSecond;
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now());
|
||||
step =
|
||||
mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
|
||||
// Should first see a series of ForgetSkippable actions.
|
||||
if (step.mAction != CCRunnerAction::ForgetSkippable ||
|
||||
step.mRemoveChildless != KeepChildless) {
|
||||
|
@ -130,16 +130,17 @@ void TestCC::TimerFires(int aNumSlices) {
|
|||
|
||||
while (step.mYield == Continue) {
|
||||
TimeStamp idleDeadline = Now() + kOneSecond;
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now());
|
||||
step =
|
||||
mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
|
||||
}
|
||||
EXPECT_EQ(step.mAction, CCRunnerAction::ForgetSkippable);
|
||||
EXPECT_EQ(step.mRemoveChildless, RemoveChildless);
|
||||
ForgetSkippable();
|
||||
|
||||
TimeStamp idleDeadline = Now() + kOneSecond;
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now());
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
|
||||
EXPECT_EQ(step.mAction, CCRunnerAction::CleanupContentUnbinder);
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now());
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
|
||||
EXPECT_EQ(step.mAction, CCRunnerAction::CleanupDeferred);
|
||||
|
||||
RunSlices(aNumSlices);
|
||||
|
@ -152,7 +153,8 @@ void TestCC::ForgetSkippable() {
|
|||
mScheduler.ComputeForgetSkippableBudget(Now(), Now() + kTenthSecond);
|
||||
EXPECT_NEAR(budget.timeBudget(), kTenthSecond.ToMilliseconds(), 1);
|
||||
AdvanceTime(kTenthSecond);
|
||||
mScheduler.NoteForgetSkippableComplete(Now(), suspectedBefore);
|
||||
mScheduler.NoteForgetSkippableComplete(Now(), suspectedBefore,
|
||||
SuspectedCCObjects());
|
||||
}
|
||||
|
||||
void TestCC::RunSlices(int aNumSlices) {
|
||||
|
@ -201,7 +203,7 @@ void TestIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd,
|
|||
TimeStamp idleDeadline = Now() + kTenthSecond;
|
||||
|
||||
// The scheduler should request a CycleCollect slice.
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now());
|
||||
step = mScheduler.AdvanceCCRunner(idleDeadline, Now(), SuspectedCCObjects());
|
||||
EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect);
|
||||
|
||||
// nsJSContext::RunCycleCollectorSlice
|
||||
|
@ -239,7 +241,7 @@ void TestNonIdleCC::RunSlice(TimeStamp aCCStartTime, TimeStamp aPrevSliceEnd,
|
|||
TimeStamp nullDeadline;
|
||||
|
||||
// The scheduler should tell us to run a slice of cycle collection.
|
||||
step = mScheduler.AdvanceCCRunner(nullDeadline, Now());
|
||||
step = mScheduler.AdvanceCCRunner(nullDeadline, Now(), SuspectedCCObjects());
|
||||
EXPECT_EQ(step.mAction, CCRunnerAction::CycleCollect);
|
||||
|
||||
// nsJSContext::RunCycleCollectorSlice
|
||||
|
@ -292,25 +294,25 @@ static bool BasicScenario(CCGCScheduler& aScheduler, TestGC* aTestGC,
|
|||
// After a GC, the scheduler should decide to do a full CC regardless of the
|
||||
// number of purple buffer entries.
|
||||
SetNumSuspected(3);
|
||||
EXPECT_TRUE(aScheduler.IsCCNeeded(Now()));
|
||||
EXPECT_TRUE(aScheduler.IsCCNeeded(Now(), SuspectedCCObjects()));
|
||||
|
||||
// Now we should want to CC.
|
||||
EXPECT_TRUE(aScheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_TRUE(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
|
||||
// Do a 5-slice CC.
|
||||
aTestCC->Run(5);
|
||||
|
||||
// Not enough suspected objects to deserve a CC.
|
||||
EXPECT_FALSE(aScheduler.IsCCNeeded(Now()));
|
||||
EXPECT_FALSE(aScheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_FALSE(aScheduler.IsCCNeeded(Now(), SuspectedCCObjects()));
|
||||
EXPECT_FALSE(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
SetNumSuspected(10000);
|
||||
|
||||
// We shouldn't want to CC again yet, it's too soon.
|
||||
EXPECT_FALSE(aScheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_FALSE(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
AdvanceTime(mozilla::kCCDelay);
|
||||
|
||||
// *Now* it's time for another CC.
|
||||
EXPECT_TRUE(aScheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_TRUE(aScheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
|
||||
// Run a 3-slice incremental GC.
|
||||
EXPECT_TRUE(!aScheduler.InIncrementalGC());
|
||||
|
@ -327,7 +329,7 @@ static TestNonIdleCC ccNonIdle(scheduler);
|
|||
TEST(TestScheduler, Idle)
|
||||
{
|
||||
// Cannot CC until we GC once.
|
||||
EXPECT_FALSE(scheduler.ShouldScheduleCC(Now()));
|
||||
EXPECT_FALSE(scheduler.ShouldScheduleCC(Now(), SuspectedCCObjects()));
|
||||
|
||||
EXPECT_TRUE(BasicScenario(scheduler, &gc, &ccIdle));
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче