Bug 779183 - GC: Incremental sweeping of atoms table part 2 - Sweep atoms compartment at the end r=billm
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 22 Aug 2012 10:45:37 +0100
changeset 105058 a8785c8a603a156de71d42cc11d861fd95c409e3
parent 105057 271c3965015e4396bc27f82bdd5e2572d4adb58d
child 105059 e8e54b17e6f2dfd69a91db0d0276e49c22248034
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersbillm
bugs779183
milestone17.0a1
Bug 779183 - GC: Incremental sweeping of atoms table part 2 - Sweep atoms compartment at the end r=billm
js/src/jsgc.cpp
js/src/jsgc.h
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3236,17 +3236,17 @@ BeginMarkPhase(JSRuntime *rt)
         /* Assert that compartment state is as we expect */
         JS_ASSERT(!c->isCollecting());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
             JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
 
         /* Set up which compartments will be collected. */
         if (c->isGCScheduled()) {
             any = true;
-            if (c.get() != rt->atomsCompartment)
+            if (c != rt->atomsCompartment)
                 c->setGCState(JSCompartment::Mark);
         } else {
             rt->gcIsFull = false;
         }
 
         c->setPreservingCode(ShouldPreserveJITCode(c, currentTime));
     }
 
@@ -3413,18 +3413,16 @@ EndMarkPhase(JSRuntime *rt)
      */
     if (foundBlackGray) {
         for (CompartmentsIter c(rt); !c.done(); c.next()) {
             if (!c->isCollecting())
                 c->arenas.unmarkAll();
         }
     }
 
-    rt->gcMarker.stop();
-
     /* We do not discard JIT here code as the following sweeping does that. */
 }
 
 #ifdef DEBUG
 static void
 ValidateIncrementalMarking(JSRuntime *rt)
 {
     typedef HashMap<Chunk *, uintptr_t *, GCChunkHasher, SystemAllocPolicy> BitmapMap;
@@ -3673,48 +3671,47 @@ BeginSweepPhase(JSRuntime *rt)
     gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP);
 
     /*
      * Although there is a runtime-wide gcIsFull flag, it is set in
      * BeginMarkPhase. More compartments may have been created since then.
      */
     bool isFull = true;
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
-        if (c->isCollecting())
-            c->setGCState(JSCompartment::Sweep);
-        else
+        if (c->isCollecting()) {
+            if (c != rt->atomsCompartment)
+                c->setGCState(JSCompartment::Sweep);
+        } else {
             isFull = false;
+        }
     }
     JS_ASSERT_IF(isFull, rt->gcIsFull);
 
 #ifdef JS_THREADSAFE
     rt->gcSweepOnBackgroundThread = rt->hasContexts();
 #endif
 
     /* Purge the ArenaLists before sweeping. */
-    for (GCCompartmentsIter c(rt); !c.done(); c.next())
-        c->arenas.purge();
+    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
+        if (c->isGCSweeping())
+            c->arenas.purge();
+    }
 
     FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_START);
         if (rt->gcFinalizeCallback)
             rt->gcFinalizeCallback(&fop, JSFINALIZE_START, !isFull);
     }
 
     /* Finalize unreachable (key,value) pairs in all weak maps. */
     WeakMapBase::sweepAll(&rt->gcMarker);
     rt->debugScopes->sweep();
 
-    if (rt->atomsCompartment->wasGCStarted()) {
-        gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_ATOMS);
-        SweepAtomState(rt);
-    }
-
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
 
     /* Detach unreachable debuggers and global objects from each other. */
     Debugger::sweepAll(&fop);
 
     PartitionCompartments partition(rt);
     partition.partition();
@@ -3743,30 +3740,38 @@ BeginSweepPhase(JSRuntime *rt)
      * Queue all GC things in all compartments for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
      *
      * Objects are finalized immediately but this may change in the future.
      */
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
-        c->arenas.queueObjectsForSweep(&fop);
+        if (c->isGCSweeping()) {
+            gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
+            c->arenas.queueObjectsForSweep(&fop);
+        }
     }
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
-        c->arenas.queueStringsForSweep(&fop);
+        if (c->isGCSweeping()) {
+            gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
+            c->arenas.queueStringsForSweep(&fop);
+        }
     }
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
-        c->arenas.queueScriptsForSweep(&fop);
+        if (c->isGCSweeping()) {
+            gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
+            c->arenas.queueScriptsForSweep(&fop);
+        }
     }
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
-        c->arenas.queueShapesForSweep(&fop);
+        if (c->isGCSweeping()) {
+            gcstats::AutoSCC scc(rt->gcStats, partition.getSCC(c));
+            c->arenas.queueShapesForSweep(&fop);
+        }
     }
 
     rt->gcSweepPhase = 0;
     rt->gcSweepCompartmentIndex = 0;
     rt->gcSweepKindIndex = 0;
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_END);
@@ -3811,21 +3816,46 @@ SweepPhase(JSRuntime *rt, SliceBudget &s
         }
         rt->gcSweepCompartmentIndex = 0;
     }
 
     return true;
 }
 
 static void
+SweepAtomsCompartment(JSRuntime *rt)
+{
+    JSCompartment *c = rt->atomsCompartment;
+
+    JS_ASSERT(rt->gcMarker.isDrained());
+
+    JS_ASSERT(c->isGCMarking());
+    c->setGCState(JSCompartment::Sweep);
+
+    c->arenas.purge();
+
+    {
+        gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_ATOMS);
+        SweepAtomState(rt);
+    }
+
+    FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
+
+    c->arenas.queueStringsForSweep(&fop);
+}
+
+static void
 EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason gcReason)
 {
     gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP);
     FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
 
+    JS_ASSERT(rt->gcMarker.isDrained());
+    rt->gcMarker.stop();
+
 #ifdef DEBUG
     PropertyTree::dumpShapes(rt);
 #endif
 
     /*
      * Set up list of compartments for sweeping of background things.
      */
     JS_ASSERT(!rt->gcSweepingCompartments);
@@ -3970,42 +4000,54 @@ IncrementalCollectSlice(JSRuntime *rt,
                         JSGCInvocationKind gcKind);
 
 static void
 ResetIncrementalGC(JSRuntime *rt, const char *reason)
 {
     if (rt->gcIncrementalState == NO_INCREMENTAL)
         return;
 
-    if (rt->gcIncrementalState == SWEEP) {
-        /* If we've finished marking then sweep to completion here. */
+    /* Cancel and ongoing marking. */
+    bool wasMarking = false;
+    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
+        if (c->isGCMarking()) {
+            c->setNeedsBarrier(false);
+            c->setGCState(JSCompartment::NoGC);
+            wasMarking = true;
+        }
+    }
+
+    if (wasMarking)
+        rt->gcMarker.reset();
+
+    if (rt->gcIncrementalState >= SWEEP) {
+        /* If we had started sweeping then sweep to completion here. */
         IncrementalCollectSlice(rt, SliceBudget::Unlimited, gcreason::RESET, GC_NORMAL);
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
         rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
-        return;
+    } else {
+        JS_ASSERT(rt->gcIncrementalState == MARK);
+        rt->gcIncrementalState = NO_INCREMENTAL;
+
+        rt->gcMarker.stop();
+
+        JS_ASSERT(!rt->gcStrictCompartmentChecking);
+
+        rt->gcStats.reset(reason);
     }
 
-    JS_ASSERT(rt->gcIncrementalState == MARK);
-
-    for (CompartmentsIter c(rt); !c.done(); c.next()) {
-        c->setNeedsBarrier(false);
-        c->setGCState(JSCompartment::NoGC);
+#ifdef DEBUG
+    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
+        JS_ASSERT(c->isCollecting());
+        JS_ASSERT(!c->needsBarrier());
         JS_ASSERT(!c->gcNextCompartment);
         for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i)
             JS_ASSERT(!c->arenas.arenaListsToSweep[i]);
     }
-
-    rt->gcMarker.reset();
-    rt->gcMarker.stop();
-
-    rt->gcIncrementalState = NO_INCREMENTAL;
-
-    JS_ASSERT(!rt->gcStrictCompartmentChecking);
-
-    rt->gcStats.reset(reason);
+#endif
 }
 
 class AutoGCSlice {
   public:
     AutoGCSlice(JSRuntime *rt);
     ~AutoGCSlice();
 
   private:
@@ -4103,18 +4145,17 @@ IncrementalCollectSlice(JSRuntime *rt,
          * Do the incremental collection type specified by zeal mode if the
          * collection was triggered by RunDebugGC() and incremental GC has not
          * been cancelled by ResetIncrementalGC.
          */
         zeal = rt->gcZeal();
     }
 #endif
 
-    rt->gcIsIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
-                          budget != SliceBudget::Unlimited;
+    rt->gcIsIncremental = budget != SliceBudget::Unlimited;
 
     if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
         /*
          * Yields between slices occurs at predetermined points in these
          * modes. sliceBudget is not used.
          */
         sliceBudget.reset();
     }
@@ -4180,33 +4221,43 @@ IncrementalCollectSlice(JSRuntime *rt,
          */
         if (budget != SliceBudget::Unlimited && zeal == ZealIncrementalMultipleSlices)
             break;
 
         /* fall through */
       }
 
       case SWEEP: {
-#ifdef DEBUG
-        for (CompartmentsIter c(rt); !c.done(); c.next())
-            JS_ASSERT(!c->needsBarrier());
-#endif
-
         bool finished = SweepPhase(rt, sliceBudget);
         if (!finished)
             break;
+        rt->gcIncrementalState = SWEEP_END;
+
+        if (rt->gcIsIncremental)
+            break;
+      }
+
+      case SWEEP_END:
+        if (rt->atomsCompartment->isGCMarking()) {
+            bool finished = DrainMarkStack(rt, sliceBudget);
+            if (!finished)
+                break;
+
+            SweepAtomsCompartment(rt);
+            if (rt->gcIsIncremental)
+                break;
+        }
 
         EndSweepPhase(rt, gckind, reason);
 
         if (rt->gcSweepOnBackgroundThread)
             rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
 
         rt->gcIncrementalState = NO_INCREMENTAL;
         break;
-      }
 
       default:
         JS_ASSERT(false);
      }
 }
 
 class IncrementalSafety
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -45,16 +45,17 @@ struct SliceBudget;
 
 namespace gc {
 
 enum State {
     NO_INCREMENTAL,
     MARK_ROOTS,
     MARK,
     SWEEP,
+    SWEEP_END,
     INVALID
 };
 
 class ChunkPool {
     Chunk   *emptyChunkListHead;
     size_t  emptyCount;
 
   public: