From 890aa014bfd675921735aa3d01f015b098b0529e Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Wed, 18 Dec 2013 11:42:16 -0800 Subject: [PATCH] Bug 937960, part 3 - Pass in a small time budget with ICC. r=smaug --- dom/base/nsJSEnvironment.cpp | 33 +++++++++++++++++++++++++++++---- dom/base/nsJSEnvironment.h | 8 +++++++- xpcom/base/nsCycleCollector.cpp | 14 ++++++++++---- xpcom/base/nsCycleCollector.h | 6 +++++- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 28a687e96cd6..2df77ebc17e8 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -124,6 +124,12 @@ static PRLogModuleInfo* gJSDiagnostics; // Maximum amount of time that should elapse between incremental CC slices static const int64_t kICCIntersliceDelay = 32; // ms +// Time budget for an incremental CC slice +static const int64_t kICCSliceBudget = 10; // ms + +// Maximum total duration for an ICC +static const uint32_t kMaxICCDuration = 2000; // ms + // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT // objects in the purple buffer. #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min @@ -2019,6 +2025,22 @@ struct CycleCollectorStats CycleCollectorStats gCCStats; +static int64_t +ICCSliceTime() +{ + // If CC is not incremental, use an unlimited budget. + if (!sIncrementalCC) { + return -1; + } + + // If an ICC is in progress and is taking too long, finish it off. + if (gCCStats.mBeginTime != 0 && + TimeBetween(gCCStats.mBeginTime, PR_Now()) >= kMaxICCDuration) { + return -1; + } + + return kICCSliceBudget; +} static void PrepareForCycleCollection(int32_t aExtraForgetSkippableCalls = 0) @@ -2076,15 +2098,18 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, //static void -nsJSContext::ScheduledCycleCollectNow() +nsJSContext::ScheduledCycleCollectNow(int64_t aSliceTime) { if (!NS_IsMainThread()) { return; } PROFILER_LABEL("CC", "ScheduledCycleCollectNow"); + + // Ideally, the slice time would be decreased by the amount of + // time spent on PrepareForCycleCollection(). PrepareForCycleCollection(); - nsCycleCollector_scheduledCollect(); + nsCycleCollector_scheduledCollect(aSliceTime); } static void @@ -2108,7 +2133,7 @@ ICCTimerFired(nsITimer* aTimer, void* aClosure) } } - nsJSContext::ScheduledCycleCollectNow(); + nsJSContext::ScheduledCycleCollectNow(ICCSliceTime()); } //static @@ -2359,7 +2384,7 @@ CCTimerFired(nsITimer *aTimer, void *aClosure) // We are in the final timer fire and still meet the conditions for // triggering a CC. Let CycleCollectNow finish the current IGC, if any, // because that will allow us to include the GC time in the CC pause. - nsJSContext::ScheduledCycleCollectNow(); + nsJSContext::ScheduledCycleCollectNow(ICCSliceTime()); } } else if ((sPreviousSuspectedCount + 100) <= suspected) { // Only do a forget skippable if there are more than a few new objects. diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index cbfbd1559f30..e945b8ccc63a 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -101,11 +101,17 @@ public: IsShrinking aShrinking = NonShrinkingGC, int64_t aSliceMillis = 0); static void ShrinkGCBuffersNow(); + // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be // called even if the previous collection was GC. static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr, int32_t aExtraForgetSkippableCalls = 0); - static void ScheduledCycleCollectNow(); + + // If aSliceTime is negative, the CC will run to completion. If aSliceTime + // is 0, only a minimum quantum of work will be done. Otherwise, aSliceTime + // will be used as the time budget for the slice, in ms. + static void ScheduledCycleCollectNow(int64_t aSliceTime); + static void BeginCycleCollectionCallback(); static void EndCycleCollectionCallback(mozilla::CycleCollectorResults &aResults); diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 3638de37f762..1c02134da4df 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -3056,7 +3056,8 @@ nsCycleCollector::Collect(ccType aCCType, // don't want to abandon the current CC, because the graph contains // information about purple roots. So we synchronously finish off // the current CC. -void nsCycleCollector::PrepareForGarbageCollection() +void +nsCycleCollector::PrepareForGarbageCollection() { if (mIncrementalPhase == IdlePhase) { MOZ_ASSERT(mGraph.IsEmpty(), "Non-empty graph when idle"); @@ -3551,7 +3552,7 @@ nsCycleCollector_collect(nsICycleCollectorListener *aManualListener) } void -nsCycleCollector_scheduledCollect() +nsCycleCollector_scheduledCollect(int64_t aSliceTime) { CollectorData *data = sCollectorData.get(); @@ -3560,8 +3561,13 @@ nsCycleCollector_scheduledCollect() MOZ_ASSERT(data->mCollector); PROFILER_LABEL("CC", "nsCycleCollector_scheduledCollect"); - SliceBudget unlimitedBudget; - data->mCollector->Collect(ScheduledCC, unlimitedBudget, nullptr); + SliceBudget budget; + if (aSliceTime > 0) { + budget = SliceBudget::TimeBudget(aSliceTime); + } else if (aSliceTime == 0) { + budget = SliceBudget::WorkBudget(1); + } + data->mCollector->Collect(ScheduledCC, budget, nullptr); } void diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h index 5037fbd8ab36..012a79f2b28c 100644 --- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -41,7 +41,11 @@ void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false); bool nsCycleCollector_doDeferredDeletion(); void nsCycleCollector_collect(nsICycleCollectorListener *aManualListener); -void nsCycleCollector_scheduledCollect(); + +// If aSliceTime is negative, the CC will run to completion. If aSliceTime +// is 0, only a minimum quantum of work will be done. Otherwise, aSliceTime +// will be used as the time budget for the slice, in ms. +void nsCycleCollector_scheduledCollect(int64_t aSliceTime); uint32_t nsCycleCollector_suspectedCount(); void nsCycleCollector_shutdown();