Bug 1352430 - Add zeal mode to exercise incremental foreground finalization r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 26 Apr 2017 11:19:06 +0100
changeset 403183 3c1eb5975702efe1bec454665947ec5b5f39abe4
parent 403182 8de58bbbb392fdca9416fd0febe27c7d62547973
child 403184 e4b8f7c680ad1d88d2eeefb2ad63c656b435fc8e
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1352430
milestone55.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 1352430 - Add zeal mode to exercise incremental foreground finalization r=sfink
js/src/gc/GCRuntime.h
js/src/jit-test/tests/gc/incremental-state.js
js/src/jsgc.cpp
js/src/jsgc.h
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -976,18 +976,18 @@ class GCRuntime
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
-    void beginSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
-    void groupZonesForSweeping(AutoLockForExclusiveAccess& lock);
+    void beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
+    void groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool findInterZoneEdges();
     void getNextSweepGroup();
     void endMarkingSweepGroup();
     void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock);
     bool shouldReleaseObservedTypes();
     void endSweepingSweepGroup();
     IncrementalProgress performSweepActions(SliceBudget& sliceBudget,
                                             AutoLockForExclusiveAccess& lock);
@@ -1438,17 +1438,18 @@ GCRuntime::upcomingZealousGC() {
 inline bool
 GCRuntime::needZealousGC() {
     if (nextScheduled > 0 && --nextScheduled == 0) {
         if (hasZealMode(ZealMode::Alloc) ||
             hasZealMode(ZealMode::GenerationalGC) ||
             hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
             hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
             hasZealMode(ZealMode::IncrementalMultipleSlices) ||
-            hasZealMode(ZealMode::Compact))
+            hasZealMode(ZealMode::Compact) ||
+            hasZealMode(ZealMode::IncrementalSweepThenFinish))
         {
             nextScheduled = zealFrequency;
         }
         return true;
     }
     return false;
 }
 #else
--- a/js/src/jit-test/tests/gc/incremental-state.js
+++ b/js/src/jit-test/tests/gc/incremental-state.js
@@ -22,42 +22,45 @@ gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1000000);
 assertEq(gcstate(), "Mark");
 gcslice(1000000);
 while (gcstate() == "Finalize") { gcslice(1); }
 while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
-// Zeal mode 8: Incremental GC in two main slices:
+// Zeal mode 8: Incremental GC in two slices:
 //   1) mark roots
 //   2) mark and sweep
-//   *) finalize.
 gczeal(8, 0);
 gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1);
-while (gcstate() == "Finalize") { gcslice(1); }
-while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
-// Zeal mode 9: Incremental GC in two main slices:
+// Zeal mode 9: Incremental GC in two slices:
 //   1) mark roots and marking
 //   2) new marking and sweeping
-//   *) finalize.
 gczeal(9, 0);
 gcslice(1);
 assertEq(gcstate(), "Mark");
 gcslice(1);
-while (gcstate() == "Finalize") { gcslice(1); }
-while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
 
 // Zeal mode 10: Incremental GC in multiple slices (always yeilds before
 // sweeping). This test uses long slices to prove that this zeal mode yields
 // in sweeping, where normal IGC (above) does not.
 gczeal(10, 0);
 gcslice(1000000);
 assertEq(gcstate(), "Sweep");
 gcslice(1000000);
 while (gcstate() == "Finalize") { gcslice(1); }
 while (gcstate() == "Decommit") { gcslice(1); }
 assertEq(gcstate(), "NotActive");
+
+// Zeal mode 17: Incremental GC in two slices:
+//   1) mark everything and start sweeping
+//   2) finish sweeping
+gczeal(17, 0);
+gcslice(1);
+assertEq(gcstate(), "Sweep");
+gcslice(1);
+assertEq(gcstate(), "NotActive");
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -962,17 +962,27 @@ const char* gc::ZealModeHelpText =
     "    8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n"
     "    9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
     "   10: (IncrementalMultipleSlices) Incremental GC in multiple slices\n"
     "   11: (IncrementalMarkingValidator) Verify incremental marking\n"
     "   12: (ElementsBarrier) Always use the individual element post-write barrier, regardless of elements size\n"
     "   13: (CheckHashTablesOnMinorGC) Check internal hashtables on minor GC\n"
     "   14: (Compact) Perform a shrinking collection every N allocations\n"
     "   15: (CheckHeapAfterGC) Walk the heap to check its integrity after every GC\n"
-    "   16: (CheckNursery) Check nursery integrity on minor GC\n";
+    "   16: (CheckNursery) Check nursery integrity on minor GC\n"
+    "   17: (IncrementalSweepThenFinish) Incremental GC in two slices: 1) start sweeping 2) finish collection\n";
+
+// The set of zeal modes that control incremental slices. These modes are
+// mutually exclusive.
+static const mozilla::EnumSet<ZealMode> IncrementalSliceZealModes = {
+    ZealMode::IncrementalRootsThenFinish,
+    ZealMode::IncrementalMarkAllThenFinish,
+    ZealMode::IncrementalMultipleSlices,
+    ZealMode::IncrementalSweepThenFinish
+};
 
 void
 GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
 {
     MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
 
     if (verifyPreData)
         VerifyBarriers(rt, PreBarrierVerifier);
@@ -983,24 +993,21 @@ GCRuntime::setZeal(uint8_t zeal, uint32_
     }
 
     ZealMode zealMode = ZealMode(zeal);
     if (zealMode == ZealMode::GenerationalGC) {
         for (ZoneGroupsIter group(rt); !group.done(); group.next())
             group->nursery().enterZealMode();
     }
 
-    // Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
-    // we first reset all of them.
-    if (zealMode >= ZealMode::IncrementalRootsThenFinish &&
-        zealMode <= ZealMode::IncrementalMultipleSlices)
-    {
-        clearZealMode(ZealMode::IncrementalRootsThenFinish);
-        clearZealMode(ZealMode::IncrementalMarkAllThenFinish);
-        clearZealMode(ZealMode::IncrementalMultipleSlices);
+    // Some modes are mutually exclusive. If we're setting one of those, we
+    // first reset all of them.
+    if (IncrementalSliceZealModes.contains(zealMode)) {
+        for (auto mode : IncrementalSliceZealModes)
+            clearZealMode(mode);
     }
 
     bool schedule = zealMode >= ZealMode::Alloc;
     if (zeal != 0)
         zealModeBits |= 1 << unsigned(zeal);
     else
         zealModeBits = 0;
     zealFrequency = frequency;
@@ -4548,28 +4555,37 @@ GCRuntime::findInterZoneEdges()
                 zone->setHasDeadProxies(false);
         }
     }
 
     return true;
 }
 
 void
-GCRuntime::groupZonesForSweeping(AutoLockForExclusiveAccess& lock)
+GCRuntime::groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcSweepGroupEdges().empty());
 #endif
 
     JSContext* cx = TlsContext.get();
     ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode], lock);
     if (!isIncremental || !findInterZoneEdges())
         finder.useOneComponent();
 
+#ifdef JS_GC_ZEAL
+    // Use one component for IncrementalSweepThenFinish zeal mode.
+    if (isIncremental && reason == JS::gcreason::DEBUG_GC &&
+        hasZealMode(ZealMode::IncrementalSweepThenFinish))
+    {
+        finder.useOneComponent();
+    }
+#endif
+
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarking());
         finder.addNode(zone);
     }
     sweepGroups = finder.getResultsList();
     currentSweepGroup = sweepGroups;
     sweepGroupIndex = 0;
 
@@ -5308,17 +5324,17 @@ GCRuntime::endSweepingSweepGroup()
     /* Reset the list of arenas marked as being allocated during sweep phase. */
     while (Arena* arena = arenasAllocatedDuringSweep) {
         arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
 void
-GCRuntime::beginSweepPhase(bool destroyingRuntime, AutoLockForExclusiveAccess& lock)
+GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
 {
     /*
      * Sweep phase.
      *
      * Finalize as we sweep, outside of lock but with CurrentThreadIsHeapBusy()
      * true so that any attempt to allocate a GC-thing from a finalizer will
      * fail, rather than nest badly and leave the unmarked newborn to be swept.
      */
@@ -5329,24 +5345,24 @@ GCRuntime::beginSweepPhase(bool destroyi
 
     releaseHeldRelocatedArenas();
 
     computeNonIncrementalMarkingForValidation(lock);
 
     gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP);
 
     sweepOnBackgroundThread =
-        !destroyingRuntime && !TraceEnabled() && CanUseExtraThreads();
+        reason != JS::gcreason::DESTROY_RUNTIME && !TraceEnabled() && CanUseExtraThreads();
 
     releaseObservedTypes = shouldReleaseObservedTypes();
 
     AssertNoWrappersInGrayList(rt);
     DropStringWrappers(rt);
 
-    groupZonesForSweeping(lock);
+    groupZonesForSweeping(reason, lock);
     endMarkingSweepGroup();
     beginSweepingSweepGroup(lock);
 }
 
 bool
 ArenaLists::foregroundFinalize(FreeOp* fop, AllocKind thingKind, SliceBudget& sliceBudget,
                                SortedArenaList& sweepList)
 {
@@ -6077,17 +6093,18 @@ GCRuntime::incrementalCollectSlice(Slice
         useZeal = true;
     }
 #endif
 
     MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
     isIncremental = !budget.isUnlimited();
 
     if (useZeal && (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
-                    hasZealMode(ZealMode::IncrementalMarkAllThenFinish)))
+                    hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
+                    hasZealMode(ZealMode::IncrementalSweepThenFinish)))
     {
         /*
          * Yields between slices occurs at predetermined points in these modes;
          * the budget is not used.
          */
         budget.makeUnlimited();
     }
 
@@ -6155,26 +6172,30 @@ GCRuntime::incrementalCollectSlice(Slice
         }
 
         incrementalState = State::Sweep;
 
         /*
          * This runs to completion, but we don't continue if the budget is
          * now exhasted.
          */
-        beginSweepPhase(destroyingRuntime, lock);
+        beginSweepPhase(reason, lock);
         if (budget.isOverBudget())
             break;
 
         /*
          * Always yield here when running in incremental multi-slice zeal
          * mode, so RunDebugGC can reset the slice buget.
          */
-        if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices))
+        if (isIncremental && useZeal &&
+            (hasZealMode(ZealMode::IncrementalMultipleSlices) ||
+             hasZealMode(ZealMode::IncrementalSweepThenFinish)))
+        {
             break;
+        }
 
         MOZ_FALLTHROUGH;
 
       case State::Sweep:
         if (performSweepActions(budget, lock) == NotFinished)
             break;
 
         endSweepPhase(destroyingRuntime, lock);
@@ -6183,17 +6204,17 @@ GCRuntime::incrementalCollectSlice(Slice
 
         MOZ_FALLTHROUGH;
 
       case State::Finalize:
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
             // Yield until background finalization is done.
-            if (isIncremental) {
+            if (!budget.isUnlimited()) {
                 // Poll for end of background sweeping
                 AutoLockGC lock(rt);
                 if (isBackgroundSweeping())
                     break;
             } else {
                 waitBackgroundSweepEnd();
             }
         }
@@ -6207,17 +6228,17 @@ GCRuntime::incrementalCollectSlice(Slice
             FreeOp fop(rt);
             sweepZoneGroups(&fop, destroyingRuntime);
         }
 
         MOZ_ASSERT(!startedCompacting);
         incrementalState = State::Compact;
 
         // Always yield before compacting since it is not incremental.
-        if (isCompacting && isIncremental)
+        if (isCompacting && !budget.isUnlimited())
             break;
 
         MOZ_FALLTHROUGH;
 
       case State::Compact:
         if (isCompacting) {
             if (!startedCompacting)
                 beginCompactPhase();
@@ -6233,17 +6254,17 @@ GCRuntime::incrementalCollectSlice(Slice
 
         MOZ_FALLTHROUGH;
 
       case State::Decommit:
         {
             gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD);
 
             // Yield until background decommit is done.
-            if (isIncremental && decommitTask.isRunning())
+            if (!budget.isUnlimited() && decommitTask.isRunning())
                 break;
 
             decommitTask.join();
         }
 
         finishCollection(reason);
         incrementalState = State::NotActive;
         break;
@@ -7117,17 +7138,18 @@ GCRuntime::runDebugGC()
     if (hasZealMode(ZealMode::GenerationalGC))
         return minorGC(JS::gcreason::DEBUG_GC);
 
     PrepareForDebugGC(rt);
 
     auto budget = SliceBudget::unlimited();
     if (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
         hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
-        hasZealMode(ZealMode::IncrementalMultipleSlices))
+        hasZealMode(ZealMode::IncrementalMultipleSlices) ||
+        hasZealMode(ZealMode::IncrementalSweepThenFinish))
     {
         js::gc::State initialState = incrementalState;
         if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
             /*
              * Start with a small slice limit and double it every slice. This
              * ensure that we get multiple slices, and collection runs to
              * completion.
              */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1176,23 +1176,24 @@ inline void CheckValueAfterMovingGC(cons
             D(IncrementalRootsThenFinish, 8)   \
             D(IncrementalMarkAllThenFinish, 9) \
             D(IncrementalMultipleSlices, 10)   \
             D(IncrementalMarkingValidator, 11) \
             D(ElementsBarrier, 12)             \
             D(CheckHashTablesOnMinorGC, 13)    \
             D(Compact, 14)                     \
             D(CheckHeapAfterGC, 15)            \
-            D(CheckNursery, 16)
+            D(CheckNursery, 16)                \
+            D(IncrementalSweepThenFinish, 17)
 
 enum class ZealMode {
 #define ZEAL_MODE(name, value) name = value,
     JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
-    Limit = 16
+    Limit = 17
 };
 
 enum VerifierType {
     PreBarrierVerifier
 };
 
 #ifdef JS_GC_ZEAL