Bug 820422 - GC: Store buffered gray roots per-compartment r=billm
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 11 Dec 2012 17:03:44 +0000
changeset 115891 37b4c4b9d7d6814e559d7a47d83652e68a5689aa
parent 115890 9ff7b0790b02501f39f968477d6693c54f4569d6
child 115892 34840088cc10a7fb9cd94ea56ff07f449bbf1568
push id24028
push useremorley@mozilla.com
push dateThu, 13 Dec 2012 15:56:02 +0000
treeherderautoland@9db79b97abbb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs820422
milestone20.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 820422 - GC: Store buffered gray roots per-compartment r=billm
js/src/gc/GCInternals.h
js/src/gc/RootMarking.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgc.h
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -11,16 +11,19 @@
 #include "jsapi.h"
 
 namespace js {
 namespace gc {
 
 void
 MarkRuntime(JSTracer *trc, bool useSavedRoots = false);
 
+void
+BufferGrayRoots(GCMarker *gcmarker);
+
 class AutoCopyFreeListToArenas {
     JSRuntime *runtime;
 
   public:
     AutoCopyFreeListToArenas(JSRuntime *rt);
     ~AutoCopyFreeListToArenas();
 };
 
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -786,20 +786,25 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->mark(trc);
 
     /* The embedding can register additional roots here. */
     if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
         (*op)(trc, rt->gcBlackRootsData);
 
-    /* During GC, this buffers up the gray roots and doesn't mark them. */
+    /* During GC, we don't mark gray roots at this stage. */
     if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) {
-        if (IS_GC_MARKING_TRACER(trc)) {
-            GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-            gcmarker->startBufferingGrayRoots();
+        if (!IS_GC_MARKING_TRACER(trc))
             (*op)(trc, rt->gcGrayRootsData);
-            gcmarker->endBufferingGrayRoots();
-        } else {
-            (*op)(trc, rt->gcGrayRootsData);
-        }
     }
 }
+
+void
+js::gc::BufferGrayRoots(GCMarker *gcmarker)
+{
+    JSRuntime *rt = gcmarker->runtime;
+    if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) {
+        gcmarker->startBufferingGrayRoots();
+        (*op)(gcmarker, rt->gcGrayRootsData);
+        gcmarker->endBufferingGrayRoots();
+    }
+}
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -70,16 +70,17 @@ JSCompartment::JSCompartment(JSRuntime *
     lastAnimationTime(0),
     regExps(rt),
     propertyTree(thisForCtor()),
     gcMallocAndFreeBytes(0),
     gcTriggerMallocAndFreeBytes(0),
     gcIncomingGrayPointers(NULL),
     gcLiveArrayBuffers(NULL),
     gcWeakMapList(NULL),
+    gcGrayRoots(),
     gcMallocBytes(0),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     watchpointMap(NULL),
     scriptCountsMap(NULL),
     debugScriptMap(NULL),
     debugScopes(NULL)
 #ifdef JS_ION
     , ionCompartment_(NULL)
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -370,16 +370,19 @@ struct JSCompartment : private JS::shado
     js::RawObject                gcIncomingGrayPointers;
 
     /* Linked list of live array buffers with >1 view. */
     JSObject                     *gcLiveArrayBuffers;
 
     /* Linked list of live weakmaps in this compartment. */
     js::WeakMapBase              *gcWeakMapList;
 
+    /* This compartment's gray roots. */
+    js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> gcGrayRoots;
+
   private:
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs from
      * gcMaxMallocBytes down to zero. This counter should be used only when it's
      * not possible to know the size of a free.
      */
     ptrdiff_t                    gcMallocBytes;
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1553,19 +1553,16 @@ GCMarker::start(JSRuntime *rt)
     InitTracer(this, rt, NULL);
     JS_ASSERT(!started);
     started = true;
     color = BLACK;
 
     JS_ASSERT(!unmarkedArenaStackTop);
     JS_ASSERT(markLaterArenas == 0);
 
-    JS_ASSERT(grayRoots.empty());
-    JS_ASSERT(!grayFailed);
-
     /*
      * The GC is recomputing the liveness of WeakMap entries, so we delay
      * visting entries.
      */
     eagerlyTraceWeakMaps = JS_FALSE;
 }
 
 void
@@ -1574,21 +1571,20 @@ GCMarker::stop()
     JS_ASSERT(isDrained());
 
     JS_ASSERT(started);
     started = false;
 
     JS_ASSERT(!unmarkedArenaStackTop);
     JS_ASSERT(markLaterArenas == 0);
 
-    grayRoots.clearAndFree();
-    grayFailed = false;
-
     /* Free non-ballast stack memory. */
     stack.reset();
+
+    resetBufferedGrayRoots();
 }
 
 void
 GCMarker::reset()
 {
     color = BLACK;
 
     stack.reset();
@@ -1601,19 +1597,16 @@ GCMarker::reset()
         unmarkedArenaStackTop = aheader->getNextDelayedMarking();
         aheader->unsetDelayedMarking();
         aheader->markOverflow = 0;
         aheader->allocatedDuringIncremental = 0;
         markLaterArenas--;
     }
     JS_ASSERT(isDrained());
     JS_ASSERT(!markLaterArenas);
-
-    grayRoots.clearAndFree();
-    grayFailed = false;
 }
 
 /*
  * When the native stack is low, the GC does not call JS_TraceChildren to mark
  * the reachable "children" of the thing. Rather the thing is put aside and
  * JS_TraceChildren is called later with more space on the C stack.
  *
  * To implement such delayed marking of the children with minimal overhead for
@@ -1716,67 +1709,60 @@ bool
 GCMarker::hasBufferedGrayRoots() const
 {
     return !grayFailed;
 }
 
 void
 GCMarker::startBufferingGrayRoots()
 {
+    JS_ASSERT(!grayFailed);
+    for (GCCompartmentsIter c(runtime); !c.done(); c.next())
+        JS_ASSERT(c->gcGrayRoots.empty());
+
     JS_ASSERT(!callback);
     callback = GrayCallback;
     JS_ASSERT(IS_GC_MARKING_TRACER(this));
 }
 
 void
 GCMarker::endBufferingGrayRoots()
 {
     JS_ASSERT(callback == GrayCallback);
     callback = NULL;
     JS_ASSERT(IS_GC_MARKING_TRACER(this));
 }
 
 void
+GCMarker::resetBufferedGrayRoots()
+{
+    for (GCCompartmentsIter c(runtime); !c.done(); c.next())
+        c->gcGrayRoots.clearAndFree();
+    grayFailed = false;
+}
+
+void
 GCMarker::markBufferedGrayRoots()
 {
     JS_ASSERT(!grayFailed);
 
-    unsigned markCount = 0;
-
-    GrayRoot *elem = grayRoots.begin();
-    GrayRoot *write = elem;
-    for (; elem != grayRoots.end(); elem++) {
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) {
+        JS_ASSERT(c->isGCMarkingGray());
+        for (GrayRoot *elem = c->gcGrayRoots.begin(); elem != c->gcGrayRoots.end(); elem++) {
 #ifdef DEBUG
-        debugPrinter = elem->debugPrinter;
-        debugPrintArg = elem->debugPrintArg;
-        debugPrintIndex = elem->debugPrintIndex;
+            debugPrinter = elem->debugPrinter;
+            debugPrintArg = elem->debugPrintArg;
+            debugPrintIndex = elem->debugPrintIndex;
 #endif
-        void *tmp = elem->thing;
-        if (static_cast<Cell *>(tmp)->compartment()->isGCMarkingGray()) {
+            void *tmp = elem->thing;
             JS_SET_TRACING_LOCATION(this, (void *)&elem->thing);
             MarkKind(this, &tmp, elem->kind);
             JS_ASSERT(tmp == elem->thing);
-            ++markCount;
-        } else {
-            if (write != elem)
-                *write = *elem;
-            ++write;
         }
     }
-    JS_ASSERT(markCount == elem - write);
-    grayRoots.shrinkBy(elem - write);
-}
-
-void
-GCMarker::markBufferedGrayRootCompartmentsAlive()
-{
-    for (GrayRoot *elem = grayRoots.begin(); elem != grayRoots.end(); elem++) {
-        Cell *thing = static_cast<Cell *>(elem->thing);
-        thing->compartment()->maybeAlive = true;
-    }
 }
 
 void
 GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(started);
 
     if (grayFailed)
@@ -1784,34 +1770,40 @@ GCMarker::appendGrayRoot(void *thing, JS
 
     GrayRoot root(thing, kind);
 #ifdef DEBUG
     root.debugPrinter = debugPrinter;
     root.debugPrintArg = debugPrintArg;
     root.debugPrintIndex = debugPrintIndex;
 #endif
 
-    if (!grayRoots.append(root)) {
-        grayRoots.clearAndFree();
-        grayFailed = true;
+    JSCompartment *comp = static_cast<Cell *>(thing)->compartment();
+    if (comp->isCollecting()) {
+        comp->maybeAlive = true;
+        if (!comp->gcGrayRoots.append(root)) {
+            grayFailed = true;
+            resetBufferedGrayRoots();
+        }
     }
 }
 
 void
 GCMarker::GrayCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     GCMarker *gcmarker = static_cast<GCMarker *>(trc);
     gcmarker->appendGrayRoot(*thingp, kind);
 }
 
 size_t
 GCMarker::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const
 {
-    return stack.sizeOfExcludingThis(mallocSizeOf) +
-           grayRoots.sizeOfExcludingThis(mallocSizeOf);
+    size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
+    for (CompartmentsIter c(runtime); !c.done(); c.next())
+        size += c->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
+    return size;
 }
 
 void
 js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
 {
     JS_ASSERT(!rt->isHeapBusy());
     rt->gcMarker.setSizeLimit(limit);
 }
@@ -2636,16 +2628,17 @@ BeginMarkPhase(JSRuntime *rt)
         /* Unmark everything in the compartments being collected. */
         c->arenas.unmarkAll();
 
         /* Reset weak map list for the compartments being collected. */
         WeakMapBase::resetCompartmentWeakMapList(c);
     }
 
     MarkRuntime(gcmarker);
+    BufferGrayRoots(gcmarker);
 
     /*
      * This code ensures that if a compartment is "dead", then it will be
      * collected in this GC. A compartment is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
      *   (1) the compartment has incoming cross-compartment edges, or
      *   (2) an object in the compartment was marked during root marking, either
      *       as a black root or a gray root.
@@ -2676,46 +2669,43 @@ BeginMarkPhase(JSRuntime *rt)
             Cell *dst = e.front().key.wrapped;
             dst->compartment()->maybeAlive = true;
         }
 
         if (c->hold)
             c->maybeAlive = true;
     }
 
-    /* Set the maybeAlive flag based on gray roots. */
-    rt->gcMarker.markBufferedGrayRootCompartmentsAlive();
-
     /*
      * For black roots, code in gc/Marking.cpp will already have set maybeAlive
      * during MarkRuntime.
      */
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         if (!c->maybeAlive)
             c->scheduledForDestruction = true;
     }
     rt->gcFoundBlackGrayEdges = false;
 
     return true;
 }
 
-template <class CompartmentIter>
+template <class CompartmentIterT>
 static void
 MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
 {
     GCMarker *gcmarker = &rt->gcMarker;
     JS_ASSERT(gcmarker->isDrained());
 
     gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_MARK);
     gcstats::AutoPhase ap1(rt->gcStats, phase);
 
     for (;;) {
         bool markedAny = false;
-        for (CompartmentIter c(rt); !c.done(); c.next()) {
+        for (CompartmentIterT c(rt); !c.done(); c.next()) {
             markedAny |= WatchpointMap::markCompartmentIteratively(c, gcmarker);
             markedAny |= WeakMapBase::markCompartmentIteratively(c, gcmarker);
         }
         markedAny |= Debugger::markAllIteratively(gcmarker);
 
         if (!markedAny)
             break;
 
@@ -3771,27 +3761,28 @@ ResetIncrementalGC(JSRuntime *rt, const 
 {
     switch (rt->gcIncrementalState) {
       case NO_INCREMENTAL:
         return;
 
       case MARK: {
         /* Cancel any ongoing marking. */
         AutoCopyFreeListToArenas copy(rt);
+
+        rt->gcMarker.reset();
+        rt->gcMarker.stop();
+
         for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
             if (c->isGCMarking()) {
                 c->setNeedsBarrier(false, JSCompartment::UpdateIon);
                 c->setGCState(JSCompartment::NoGC);
                 ArrayBufferObject::resetArrayBufferList(c);
             }
         }
 
-        rt->gcMarker.reset();
-        rt->gcMarker.stop();
-
         rt->gcIncrementalState = NO_INCREMENTAL;
 
         JS_ASSERT(!rt->gcStrictCompartmentChecking);
 
         break;
       }
 
       case SWEEP:
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -897,16 +897,29 @@ struct SliceBudget {
         if (counter >= 0)
             return false;
         return checkOverBudget();
     }
 };
 
 static const size_t MARK_STACK_LENGTH = 32768;
 
+struct GrayRoot {
+    void *thing;
+    JSGCTraceKind kind;
+#ifdef DEBUG
+    JSTraceNamePrinter debugPrinter;
+    const void *debugPrintArg;
+    size_t debugPrintIndex;
+#endif
+
+    GrayRoot(void *thing, JSGCTraceKind kind)
+        : thing(thing), kind(kind) {}
+};
+
 struct GCMarker : public JSTracer {
   private:
     /*
      * We use a common mark stack to mark GC things of different types and use
      * the explicit tags to distinguish them when it cannot be deduced from
      * the context of push or pop operation.
      */
     enum StackTag {
@@ -997,25 +1010,26 @@ struct GCMarker : public JSTracer {
     }
 
     bool drainMarkStack(SliceBudget &budget);
 
     /*
      * Gray marking must be done after all black marking is complete. However,
      * we do not have write barriers on XPConnect roots. Therefore, XPConnect
      * roots must be accumulated in the first slice of incremental GC. We
-     * accumulate these roots in the GrayRootMarker and then mark them later,
-     * after black marking is complete. This accumulation can fail, but in that
-     * case we switch to non-incremental GC.
+     * accumulate these roots in the each compartment's gcGrayRoots vector and
+     * then mark them later, after black marking is complete for each
+     * compartment. This accumulation can fail, but in that case we switch to
+     * non-incremental GC.
      */
     bool hasBufferedGrayRoots() const;
     void startBufferingGrayRoots();
     void endBufferingGrayRoots();
+    void resetBufferedGrayRoots();
     void markBufferedGrayRoots();
-    void markBufferedGrayRootCompartmentsAlive();
 
     static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
 
     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) const;
 
     MarkStack<uintptr_t> stack;
 
   private:
@@ -1065,31 +1079,17 @@ struct GCMarker : public JSTracer {
 
     mozilla::DebugOnly<bool> started;
 
     /* Pointer to the top of the stack of arenas we are delaying marking on. */
     js::gc::ArenaHeader *unmarkedArenaStackTop;
     /* Count of arenas that are currently in the stack. */
     mozilla::DebugOnly<size_t> markLaterArenas;
 
-    struct GrayRoot {
-        void *thing;
-        JSGCTraceKind kind;
-#ifdef DEBUG
-        JSTraceNamePrinter debugPrinter;
-        const void *debugPrintArg;
-        size_t debugPrintIndex;
-#endif
-
-        GrayRoot(void *thing, JSGCTraceKind kind)
-          : thing(thing), kind(kind) {}
-    };
-
     bool grayFailed;
-    Vector<GrayRoot, 0, SystemAllocPolicy> grayRoots;
 };
 
 void
 SetMarkStackLimit(JSRuntime *rt, size_t limit);
 
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);