author | Jon Coppeard <jcoppeard@mozilla.com> |
Tue, 11 Dec 2012 17:03:44 +0000 | |
changeset 115891 | 37b4c4b9d7d6814e559d7a47d83652e68a5689aa |
parent 115890 | 9ff7b0790b02501f39f968477d6693c54f4569d6 |
child 115892 | 34840088cc10a7fb9cd94ea56ff07f449bbf1568 |
push id | 24028 |
push user | emorley@mozilla.com |
push date | Thu, 13 Dec 2012 15:56:02 +0000 |
treeherder | autoland@9db79b97abbb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | billm |
bugs | 820422 |
milestone | 20.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
|
js/src/gc/GCInternals.h | file | annotate | diff | comparison | revisions | |
js/src/gc/RootMarking.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscompartment.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscompartment.h | file | annotate | diff | comparison | revisions | |
js/src/jsgc.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsgc.h | file | annotate | diff | comparison | revisions |
--- 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);