diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp index b5e3565dba..4a6b8e21ef 100755 --- a/dom/console/Console.cpp +++ b/dom/console/Console.cpp @@ -176,8 +176,6 @@ public: void Trace(const TraceCallbacks& aCallbacks, void* aClosure) { - AssertIsOnOwningThread(); - ConsoleCallData* tmp = this; for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) { NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i]) diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 177b09f2e3..d9c900df8f 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3805,7 +3805,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END template NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent, DOMEventTargetHelper) - tmp->AssertIsOnParentThread(); NS_IMPL_CYCLE_COLLECTION_TRACE_END #ifdef DEBUG diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 329851a669..7a8ea549bc 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -124,15 +124,18 @@ struct DagChildEdge { * empty. Timings for these phases are thus exclusive of any other phase. */ -static const PhaseInfo phases[] = { - { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT }, - { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT }, - { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT }, - { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT }, - { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT }, - { PHASE_PURGE, "Purge", PHASE_NO_PARENT }, - { PHASE_MARK, "Mark", PHASE_NO_PARENT }, - { PHASE_UNMARK, "Unmark", PHASE_MARK }, + static const PhaseInfo phases[] = { + { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT}, + { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT}, + { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT}, + { PHASE_PREPARE, "Prepare For Collection", PHASE_NO_PARENT}, + { PHASE_UNMARK, "Unmark", PHASE_PREPARE}, + { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_PREPARE}, + { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_PREPARE}, + { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_PREPARE}, + { PHASE_PURGE, "Purge", PHASE_PREPARE}, + { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_PREPARE}, + { PHASE_MARK, "Mark", PHASE_NO_PARENT}, /* PHASE_MARK_ROOTS */ { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK }, { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT }, @@ -182,18 +185,15 @@ static const PhaseInfo phases[] = { /* PHASE_MARK_ROOTS */ { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT }, /* PHASE_MARK_ROOTS */ - { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT }, - { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER }, - { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS }, - { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS }, - { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS }, - { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS }, - { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS }, - { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS }, - { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS }, - { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT }, - - { PHASE_LIMIT, nullptr, PHASE_NO_PARENT } + { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT}, + { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER}, + { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS}, + { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS}, + { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS}, + { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS}, + { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS}, + { PHASE_MARK_COMPARTMENTS, "Mark compartments", PHASE_MARK_ROOTS,}, + { PHASE_LIMIT, nullptr, PHASE_NO_PARENT} }; static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } }; diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 9e28be9f97..6cfe4d776e 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -26,11 +26,14 @@ enum Phase : uint8_t { PHASE_MUTATOR, PHASE_GC_BEGIN, PHASE_WAIT_BACKGROUND_THREAD, + PHASE_PREPARE, + PHASE_UNMARK, + PHASE_BUFFER_GRAY_ROOTS, PHASE_MARK_DISCARD_CODE, PHASE_RELAZIFY_FUNCTIONS, PHASE_PURGE, + PHASE_PURGE_SHAPE_TABLES, PHASE_MARK, - PHASE_UNMARK, PHASE_MARK_DELAYED, PHASE_SWEEP, PHASE_SWEEP_MARK, @@ -78,13 +81,11 @@ enum Phase : uint8_t { PHASE_BARRIER, PHASE_UNMARK_GRAY, PHASE_MARK_ROOTS, - PHASE_BUFFER_GRAY_ROOTS, PHASE_MARK_CCWS, PHASE_MARK_STACK, PHASE_MARK_RUNTIME_DATA, PHASE_MARK_EMBEDDING, PHASE_MARK_COMPARTMENTS, - PHASE_PURGE_SHAPE_TABLES, PHASE_LIMIT, PHASE_NONE = PHASE_LIMIT, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 41b0fce10d..cbce3584fd 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -866,7 +866,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : majorGCNumber(0), jitReleaseNumber(0), number(0), - startNumber(0), isFull(false), #ifdef DEBUG disableStrictProxyCheckingCount(0), @@ -3418,10 +3417,38 @@ ArenaLists::checkEmptyArenaList(AllocKind kind) return num_live == 0; } +class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask +{ + using Func = void (*)(JSRuntime*); + + Func func_; + gcstats::Phase phase_; + AutoLockHelperThreadState& lock_; + + public: + AutoRunParallelTask(JSRuntime* rt, Func func, gcstats::Phase phase, + AutoLockHelperThreadState& lock) + : GCParallelTask(rt), + func_(func), + phase_(phase), + lock_(lock) + { + runtime()->gc.startTask(*this, phase_, lock_); + } + + ~AutoRunParallelTask() { + runtime()->gc.joinTask(*this, phase_, lock_); + } + + void run() override { + func_(runtime()); + } +}; + void GCRuntime::purgeRuntime(AutoLockForExclusiveAccess& lock) { - gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE); + gcstats::AutoPhase ap(stats(), gcstats::PHASE_PURGE); for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) comp->purge(); @@ -3676,7 +3703,7 @@ DiscardJITCodeForIncrementalGC(JSRuntime* rt) { js::CancelOffThreadIonCompile(rt, JS::Zone::Mark); for (GCZonesIter zone(rt); !zone.done(); zone.next()) { - gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::MARK_DISCARD_CODE); + gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_MARK_DISCARD_CODE); zone->discardJitCode(rt->defaultFreeOp()); } } @@ -3684,7 +3711,7 @@ DiscardJITCodeForIncrementalGC(JSRuntime* rt) static void RelazifyFunctionsForShrinkingGC(JSRuntime* rt) { - gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::RELAZIFY_FUNCTIONS); + gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_RELAZIFY_FUNCTIONS); for (GCZonesIter zone(rt); !zone.done(); zone.next()) { if (zone->isSelfHostingZone()) continue; @@ -3696,7 +3723,7 @@ RelazifyFunctionsForShrinkingGC(JSRuntime* rt) static void PurgeShapeTablesForShrinkingGC(JSRuntime* rt) { - gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::PURGE_SHAPE_TABLES); + gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_PURGE_SHAPE_TABLES); for (GCZonesIter zone(rt); !zone.done(); zone.next()) { if (zone->keepShapeTables() || zone->isSelfHostingZone()) continue; @@ -3708,8 +3735,6 @@ PurgeShapeTablesForShrinkingGC(JSRuntime* rt) static void UnmarkCollectedZones(JSRuntime* rt) { - gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::UNMARK); - for (GCZonesIter zone(rt); !zone.done(); zone.next()) { /* Unmark everything in the zones being collected. */ zone->arenas.unmarkAll(); @@ -3721,6 +3746,12 @@ UnmarkCollectedZones(JSRuntime* rt) } } +static void +BufferGrayRoots(JSRuntime* rt) +{ + rt->gc.bufferGrayRoots(); +} + bool GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock) { @@ -3745,55 +3776,78 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces marker.start(); GCMarker* gcmarker = ▮ - /* For non-incremental GC the following sweep discards the jit code. */ - if (isIncremental) - DiscardJITCodeForIncrementalGC(rt); - - /* - * Relazify functions after discarding JIT code (we can't relazify functions - * with JIT code) and before the actual mark phase, so that the current GC - * can collect the JSScripts we're unlinking here. We do this only when - * we're performing a shrinking GC, as too much relazification can cause - * performance issues when we have to reparse the same functions over and - * over. - */ - if (invocationKind == GC_SHRINK) { - RelazifyFunctionsForShrinkingGC(rt); - PurgeShapeTablesForShrinkingGC(rt); - } - - updateMallocCountersOnGCStart(); - - /* Process any queued source compressions during the start of a major GC. */ { + gcstats::AutoPhase ap1(stats(), gcstats::PHASE_PREPARE); AutoLockHelperThreadState helperLock; - HelperThreadState().startHandlingCompressionTasks(helperLock); - } - /* - * We must purge the runtime at the beginning of an incremental GC. The - * danger if we purge later is that the snapshot invariant of incremental GC - * will be broken, as follows. If some object is reachable only through some - * cache (say the dtoaCache) then it will not be part of the snapshot. If - * we purge after root marking, then the mutator could obtain a pointer to - * the object and start using it. This object might never be marked, so a GC - * hazard would exist. - */ - purgeRuntime(lock); + /* + * Clear all mark state for the zones we are collecting. This is linear + * in the size of the heap we are collecting and so can be slow. Do this + * in parallel with the rest of this block. + */ + AutoRunParallelTask + unmarkCollectedZones(rt, UnmarkCollectedZones, gcstats::PHASE_UNMARK, helperLock); + + /* + * Buffer gray roots for incremental collections. This is linear in the + * number of roots which can be in the tens of thousands. Do this in + * parallel with the rest of this block. + */ + Maybe bufferGrayRoots; + if (isIncremental) + bufferGrayRoots.emplace(rt, BufferGrayRoots, gcstats::PHASE_BUFFER_GRAY_ROOTS, helperLock); + AutoUnlockHelperThreadState unlock(helperLock); + + /* + * Discard JIT code for incremental collections (for non-incremental + * collections the following sweep discards the jit code). + */ + if (isIncremental) + DiscardJITCodeForIncrementalGC(rt); + + /* + * Relazify functions after discarding JIT code (we can't relazify + * functions with JIT code) and before the actual mark phase, so that + * the current GC can collect the JSScripts we're unlinking here. We do + * this only when we're performing a shrinking GC, as too much + * relazification can cause performance issues when we have to reparse + * the same functions over and over. + */ + if (invocationKind == GC_SHRINK) { + RelazifyFunctionsForShrinkingGC(rt); + PurgeShapeTablesForShrinkingGC(rt); + } + + /* + * We must purge the runtime at the beginning of an incremental GC. The + * danger if we purge later is that the snapshot invariant of + * incremental GC will be broken, as follows. If some object is + * reachable only through some cache (say the dtoaCache) then it will + * not be part of the snapshot. If we purge after root marking, then + * the mutator could obtain a pointer to the object and start using + * it. This object might never be marked, so a GC hazard would exist. + */ + purgeRuntime(lock); + } /* * Mark phase. */ - gcstats::AutoPhase ap1(stats, gcstats::PHASE_MARK); - - UnmarkCollectedZones(rt); + gcstats::AutoPhase ap(stats(), gcstats::PHASE_MARK); traceRuntimeForMajorGC(gcmarker, lock); - gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS); - - if (isIncremental) { - bufferGrayRoots(); + if (isIncremental) markCompartments(); + + updateMallocCountersOnGCStart(); + + /* + * Process any queued source compressions during the start of a major + * GC. + */ + { + AutoLockHelperThreadState helperLock; + HelperThreadState().startHandlingCompressionTasks(helperLock); } return true; @@ -3802,7 +3856,8 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces void GCRuntime::markCompartments() { - gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_COMPARTMENTS); + gcstats::AutoPhase ap1(stats(), gcstats::PHASE_MARK_ROOTS); + gcstats::AutoPhase ap2(stats(), gcstats::PHASE_MARK_COMPARTMENTS); /* * This code ensures that if a compartment is "dead", then it will be @@ -4068,7 +4123,7 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock) gc->incrementalState = State::MarkRoots; { - gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_MARK); + gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_PREPARE); { gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_UNMARK); @@ -4082,6 +4137,10 @@ js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock) for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done(); chunk.next()) chunk->bitmap.clear(); } + } + + { + gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_MARK); gc->traceRuntimeForMajorGC(gcmarker, lock); @@ -5002,30 +5061,6 @@ SweepWeakCachesFromMainThread(JSRuntime* rt) } } -static WeakCacheTaskVector -PrepareWeakCacheTasks(JSRuntime* rt) -{ - WeakCacheTaskVector out; - for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - for (JS::WeakCache* cache : zone->weakCaches_) { - if (!out.append(SweepWeakCacheTask(rt, *cache))) { - SweepWeakCachesFromMainThread(rt); - return WeakCacheTaskVector(); - } - } - } - return out; -} - - ~AutoRunParallelTask() { - runtime()->gc.joinTask(*this, phase_, lock_); - } - - void run() override { - func_(runtime()); - } -}; - IncrementalProgress GCRuntime::beginSweepingSweepGroup(FreeOp* fop, SliceBudget& budget) {