Bug 1099152 - Separate out methods to start and continue incrmental GC r=terrence
☠☠ backed out by 06ec26af14f3 ☠ ☠
authorJon Coppeard <jcoppeard@mozilla.com>
Fri, 02 Jan 2015 17:19:43 +0000
changeset 247831 8b891ebcd21f2761b07b4add7861c75d28818112
parent 247830 24dae3ce853db3851f98647b88d24adc8a8379f5
child 247832 9192d432d87e5718443f09a6f9fe336e61fd075f
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1099152
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1099152 - Separate out methods to start and continue incrmental GC r=terrence
js/src/devtools/rootAnalysis/loadCallgraph.js
js/src/gc/GCRuntime.h
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
--- a/js/src/devtools/rootAnalysis/loadCallgraph.js
+++ b/js/src/devtools/rootAnalysis/loadCallgraph.js
@@ -155,17 +155,17 @@ function loadCallgraph(file)
         if (name in suppressedFunctions)
             delete gcFunctions[name];
     }
 
     for (var name in suppressedFieldCalls) {
         suppressedFunctions[name] = true;
     }
 
-    for (var gcName of [ 'void js::gc::GCRuntime::collect(uint8, js::SliceBudget*, uint32, uint32)',
+    for (var gcName of [ 'void js::gc::GCRuntime::collect(uint8, js::SliceBudget, uint32)',
                          'void js::gc::GCRuntime::minorGC(uint32)',
                          'void js::gc::GCRuntime::minorGC(uint32)' ])
     {
         assert(gcName in mangledName, "GC function not found: " + gcName);
         addGCFunction(mangledName[gcName], "GC");
     }
 
     // Initialize the worklist to all known gcFunctions.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -321,18 +321,19 @@ class GCRuntime
     }
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_EVICT_NURSERY);
         minorGCImpl(reason, nullptr);
     }
     bool gcIfNeeded(JSContext *cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
-    void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
-    void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
+    void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
+    void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0);
+    void finishGC(JS::gcreason::Reason reason);
     void gcDebugSlice(SliceBudget &budget);
 
     void runDebugGC();
     inline void poke();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
@@ -562,20 +563,19 @@ class GCRuntime
     void releaseChunk(Chunk *chunk);
 
     friend class BackgroundAllocTask;
     friend class AutoMaybeStartBackgroundAllocation;
     inline bool wantBackgroundAllocation(const AutoLockGC &lock) const;
     void startBackgroundAllocTaskIfIdle();
 
     void requestMajorGC(JS::gcreason::Reason reason);
-    void collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
-                 JS::gcreason::Reason reason);
-    bool gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
-                 JS::gcreason::Reason reason);
+    SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis);
+    void collect(bool incremental, SliceBudget budget, JS::gcreason::Reason reason);
+    bool gcCycle(bool incremental, SliceBudget &budget, JS::gcreason::Reason reason);
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void budgetIncrementalGC(SliceBudget &budget);
     void resetIncrementalGC(const char *reason);
     void incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason reason);
     void pushZealSelectedObjects();
     void purgeRuntime();
     bool beginMarkPhase(JS::gcreason::Reason reason);
     bool shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime,
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -205,23 +205,26 @@ JS_FRIEND_API(void)
 JS::ShrinkingGC(JSRuntime *rt, gcreason::Reason reason)
 {
     rt->gc.gc(GC_SHRINK, reason);
 }
 
 JS_FRIEND_API(void)
 JS::IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis)
 {
-    rt->gc.gcSlice(GC_NORMAL, reason, millis);
+    if (!rt->gc.isIncrementalGCInProgress())
+        rt->gc.startGC(GC_NORMAL, reason, millis);
+    else
+        rt->gc.gcSlice(reason, millis);
 }
 
 JS_FRIEND_API(void)
 JS::FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason)
 {
-    rt->gc.gcFinalSlice(GC_NORMAL, reason);
+    rt->gc.finishGC(reason);
 }
 
 JS_FRIEND_API(JSPrincipals *)
 JS_GetCompartmentPrincipals(JSCompartment *compartment)
 {
     return compartment->principals;
 }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5780,19 +5780,26 @@ GCRuntime::resetIncrementalGC(const char
       case SWEEP: {
         marker.reset();
 
         for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
             c->scheduledForDestruction = false;
 
         /* Finish sweeping the current zone group, then abort. */
         abortSweepAfterCurrentGroup = true;
+
+        /* Don't perform any compaction after sweeping. */
+        JSGCInvocationKind oldInvocationKind = invocationKind;
+        invocationKind = GC_NORMAL;
+
         SliceBudget budget;
         incrementalCollectSlice(budget, JS::gcreason::RESET);
 
+        invocationKind = oldInvocationKind;
+
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
             rt->gc.waitBackgroundSweepOrAllocEnd();
         }
         break;
       }
 
 #ifdef JSGC_COMPACTING
@@ -6105,18 +6112,17 @@ class AutoDisableStoreBuffer
  * non-incremental GC. We disable inlining to ensure that the bottom of the
  * stack with possible GC roots recorded in MarkRuntime excludes any pointers we
  * use during the marking implementation.
  *
  * Returns true if we "reset" an existing incremental GC, which would force us
  * to run another cycle.
  */
 MOZ_NEVER_INLINE bool
-GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
-                   JS::gcreason::Reason reason)
+GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JS::gcreason::Reason reason)
 {
     evictNursery(reason);
 
     /*
      * Marking can trigger many incidental post barriers, some of them for
      * objects which are not going to be live after the GC.
      */
     AutoDisableStoreBuffer adsb(this);
@@ -6169,20 +6175,16 @@ GCRuntime::gcCycle(bool incremental, Sli
     }
 
     /* The GC was reset, so we need a do-over. */
     if (prevState != NO_INCREMENTAL && !isIncrementalGCInProgress())
         return true;
 
     TraceMajorGCStart();
 
-    /* Set the invocation kind in the first slice. */
-    if (!isIncrementalGCInProgress())
-        invocationKind = gckind;
-
     incrementalCollectSlice(budget, reason);
 
 #ifndef JS_MORE_DETERMINISTIC
     nextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 #endif
 
     chunkAllocationSinceLastGC = false;
 
@@ -6257,18 +6259,17 @@ GCRuntime::scanZonesBeforeGC()
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next())
         zoneStats.compartmentCount++;
 
     return zoneStats;
 }
 
 void
-GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
-                   JS::gcreason::Reason reason)
+GCRuntime::collect(bool incremental, SliceBudget budget, JS::gcreason::Reason reason)
 {
     /* GC shouldn't be running in parallel execution mode */
     MOZ_ALWAYS_TRUE(!InParallelSection());
 
     JS_AbortIfWrongThread(rt);
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
     MOZ_ALWAYS_TRUE(!rt->isHeapBusy());
@@ -6285,34 +6286,34 @@ GCRuntime::collect(bool incremental, Sli
 #ifdef JS_GC_ZEAL
     if (deterministicOnly && !IsDeterministicGCReason(reason))
         return;
 #endif
 
     AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC ||
                                      reason == JS::gcreason::DESTROY_RUNTIME);
 
-    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), gckind, reason);
-
-    cleanUpEverything = ShouldCleanUpEverything(reason, gckind);
+    gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, reason);
+
+    cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
 
     bool repeat = false;
     do {
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context). Invoke the callback regardless.
          */
         if (!isIncrementalGCInProgress()) {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
             if (gcCallback.op)
                 gcCallback.op(rt, JSGC_BEGIN, gcCallback.data);
         }
 
         poked = false;
-        bool wasReset = gcCycle(incremental, budget, gckind, reason);
+        bool wasReset = gcCycle(incremental, budget, reason);
 
         if (!isIncrementalGCInProgress()) {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
             if (gcCallback.op)
                 gcCallback.op(rt, JSGC_END, gcCallback.data);
         }
 
         /* Need to re-schedule all zones for GC. */
@@ -6344,44 +6345,58 @@ GCRuntime::collect(bool incremental, Sli
          */
         repeat = (poked && cleanUpEverything) || wasReset || repeatForDeadZone;
     } while (repeat);
 
     if (!isIncrementalGCInProgress())
         EnqueuePendingParseTasksAfterGC(rt);
 }
 
+SliceBudget
+GCRuntime::defaultBudget(JS::gcreason::Reason reason, int64_t millis)
+{
+    if (millis == 0) {
+        if (reason == JS::gcreason::ALLOC_TRIGGER)
+            millis = sliceBudget;
+        else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
+            millis = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
+        else
+            millis = sliceBudget;
+    }
+
+    return SliceBudget(TimeBudget(millis));
+}
+
 void
 GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
 {
-    SliceBudget budget;
-    collect(false, budget, gckind, reason);
-}
-
-void
-GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
-{
-    SliceBudget budget;
-    if (millis)
-        budget = SliceBudget(TimeBudget(millis));
-    else if (reason == JS::gcreason::ALLOC_TRIGGER)
-        budget = SliceBudget(TimeBudget(sliceBudget));
-    else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
-        budget = SliceBudget(TimeBudget(sliceBudget * IGC_MARK_SLICE_MULTIPLIER));
-    else
-        budget = SliceBudget(TimeBudget(sliceBudget));
-
-    collect(true, budget, gckind, reason);
-}
-
-void
-GCRuntime::gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
-{
-    SliceBudget budget;
-    collect(true, budget, gckind, reason);
+    invocationKind = gckind;
+    collect(false, SliceBudget(), reason);
+}
+
+void
+GCRuntime::startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
+{
+    MOZ_ASSERT(!isIncrementalGCInProgress());
+    invocationKind = gckind;
+    collect(true, defaultBudget(reason, millis), reason);
+}
+
+void
+GCRuntime::gcSlice(JS::gcreason::Reason reason, int64_t millis)
+{
+    MOZ_ASSERT(isIncrementalGCInProgress());
+    collect(true, defaultBudget(reason, millis), reason);
+}
+
+void
+GCRuntime::finishGC(JS::gcreason::Reason reason)
+{
+    MOZ_ASSERT(isIncrementalGCInProgress());
+    collect(true, SliceBudget(), reason);
 }
 
 void
 GCRuntime::notifyDidPaint()
 {
 #ifdef JS_GC_ZEAL
     if (zealMode == ZealFrameVerifierPreValue) {
         verifyPreBarriers();
@@ -6395,19 +6410,19 @@ GCRuntime::notifyDidPaint()
 
     if (zealMode == ZealFrameGCValue) {
         JS::PrepareForFullGC(rt);
         gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
         return;
     }
 #endif
 
-    if (JS::IsIncrementalGCInProgress(rt) && !interFrameGC) {
+    if (isIncrementalGCInProgress() && !interFrameGC) {
         JS::PrepareForIncrementalGC(rt);
-        gcSlice(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
+        gcSlice(JS::gcreason::REFRESH_FRAME);
     }
 
     interFrameGC = false;
 }
 
 static bool
 ZonesSelected(JSRuntime *rt)
 {
@@ -6417,22 +6432,24 @@ ZonesSelected(JSRuntime *rt)
     }
     return false;
 }
 
 void
 GCRuntime::gcDebugSlice(SliceBudget &budget)
 {
     if (!ZonesSelected(rt)) {
-        if (JS::IsIncrementalGCInProgress(rt))
+        if (isIncrementalGCInProgress())
             JS::PrepareForIncrementalGC(rt);
         else
             JS::PrepareForFullGC(rt);
     }
-    collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
+    if (!isIncrementalGCInProgress())
+        invocationKind = GC_NORMAL;
+    collect(true, budget, JS::gcreason::DEBUG_GC);
 }
 
 /* Schedule a full GC unless a zone will already be collected. */
 void
 js::PrepareForDebugGC(JSRuntime *rt)
 {
     if (!ZonesSelected(rt))
         JS::PrepareForFullGC(rt);
@@ -6541,20 +6558,24 @@ GCRuntime::gcIfNeeded(JSContext *cx /* =
     if (minorGCRequested) {
         if (cx)
             minorGC(cx, minorGCTriggerReason);
         else
             minorGC(minorGCTriggerReason);
     }
 
     if (majorGCRequested) {
-        if (rt->gc.majorGCTriggerReason == JS::gcreason::INCREMENTAL_ALLOC_TRIGGER)
-            gcSlice(GC_NORMAL, rt->gc.majorGCTriggerReason);
-        else
-            gc(GC_NORMAL, rt->gc.majorGCTriggerReason);
+        if (majorGCTriggerReason == JS::gcreason::INCREMENTAL_ALLOC_TRIGGER) {
+            if (!isIncrementalGCInProgress())
+                startGC(GC_NORMAL, majorGCTriggerReason);
+            else
+                gcSlice(majorGCTriggerReason);
+        } else {
+            gc(GC_NORMAL, majorGCTriggerReason);
+        }
         return true;
     }
 
     return false;
 }
 
 AutoFinishGC::AutoFinishGC(JSRuntime *rt)
 {
@@ -6709,31 +6730,33 @@ GCRuntime::runDebugGC()
             else
                 incrementalLimit *= 2;
             budget = SliceBudget(WorkBudget(incrementalLimit));
         } else {
             // This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
             budget = SliceBudget(WorkBudget(1));
         }
 
-        collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
+        if (!isIncrementalGCInProgress())
+            invocationKind = GC_NORMAL;
+        collect(true, budget, JS::gcreason::DEBUG_GC);
 
         /*
          * For multi-slice zeal, reset the slice size when we get to the sweep
          * phase.
          */
         if (type == ZealIncrementalMultipleSlices &&
             initialState == MARK && incrementalState == SWEEP)
         {
             incrementalLimit = zealFrequency / 2;
         }
     } else if (type == ZealCompactValue) {
-        collect(false, budget, GC_SHRINK, JS::gcreason::DEBUG_GC);
+        gc(GC_SHRINK, JS::gcreason::DEBUG_GC);
     } else {
-        collect(false, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
+        gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
     }
 
 #endif
 }
 
 void
 GCRuntime::setValidate(bool enabled)
 {