diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 4e56a2d1cbac..fa422ff87088 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1459,7 +1459,7 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason, if (sCCLockedOut && aIncremental == IncrementalGC) { // We're in the middle of incremental GC. Do another slice. JS::PrepareForIncrementalGC(sRuntime); - JS::IncrementalGCSlice(sRuntime, aReason, aSliceMillis); + JS::IncrementalGC(sRuntime, aReason, aSliceMillis); return; } @@ -1472,10 +1472,11 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason, if (aIncremental == IncrementalGC) { MOZ_ASSERT(aShrinking == NonShrinkingGC); - JS::StartIncrementalGC(sRuntime, GC_NORMAL, aReason, aSliceMillis); + JS::IncrementalGC(sRuntime, aReason, aSliceMillis); + } else if (aShrinking == ShrinkingGC) { + JS::ShrinkingGC(sRuntime, aReason); } else { - JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL; - JS::GCForReason(sRuntime, gckind, aReason); + JS::GCForReason(sRuntime, aReason); } } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index c4bdd9ffa433..fefc1b959e88 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -6026,14 +6026,14 @@ WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, JS::PrepareForFullGC(rt); if (aShrinking) { - JS::GCForReason(rt, GC_SHRINK, JS::gcreason::DOM_WORKER); + JS::ShrinkingGC(rt, JS::gcreason::DOM_WORKER); if (!aCollectChildren) { LOG(("Worker %p collected idle garbage\n", this)); } } else { - JS::GCForReason(rt, GC_NORMAL, JS::gcreason::DOM_WORKER); + JS::GCForReason(rt, JS::gcreason::DOM_WORKER); LOG(("Worker %p collected garbage\n", this)); } } diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index ad6c70147fc0..67843d40fe3a 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -31,17 +31,6 @@ typedef enum JSGCMode { JSGC_MODE_INCREMENTAL = 2 } JSGCMode; -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL = 0, - - /* Minimize GC triggers and release empty GC chunks right away. */ - GC_SHRINK = 1 -} JSGCInvocationKind; - namespace JS { #define GCREASONS(D) \ @@ -60,7 +49,6 @@ namespace JS { D(EVICT_NURSERY) \ D(FULL_STORE_BUFFER) \ D(SHARED_MEMORY_LIMIT) \ - D(INCREMENTAL_ALLOC_TRIGGER) \ \ /* These are reserved for future use. */ \ D(RESERVED0) \ @@ -81,6 +69,7 @@ namespace JS { D(RESERVED15) \ D(RESERVED16) \ D(RESERVED17) \ + D(RESERVED18) \ \ /* Reasons from Firefox */ \ D(DOM_WINDOW_UTILS) \ @@ -141,13 +130,13 @@ enum Reason { /* * Schedule the given zone to be collected as part of the next GC. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) PrepareZoneForGC(Zone *zone); /* * Schedule all zones to be collected in the next GC. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) PrepareForFullGC(JSRuntime *rt); /* @@ -155,21 +144,21 @@ PrepareForFullGC(JSRuntime *rt); * previous incremental slice must be selected in subsequent slices as well. * This function selects those slices automatically. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) PrepareForIncrementalGC(JSRuntime *rt); /* * Returns true if any zone in the system has been scheduled for GC with one of * the functions above or by the JS engine. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsGCScheduled(JSRuntime *rt); /* * Undoes the effect of the Prepare methods above. The given zone will not be * collected in the next GC. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) SkipZoneForGC(Zone *zone); /* @@ -179,16 +168,20 @@ SkipZoneForGC(Zone *zone); */ /* - * Performs a non-incremental collection of all selected zones. - * - * If the gckind argument is GC_NORMAL, then some objects that are unreachable - * from the program may still be alive afterwards because of internal - * references; if GC_SHRINK is passed then caches and other temporary references - * to objects will be cleared and all unreferenced objects will be removed from - * the system. + * Performs a non-incremental collection of all selected zones. Some objects + * that are unreachable from the program may still be alive afterwards because + * of internal references. */ -extern JS_PUBLIC_API(void) -GCForReason(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason); +extern JS_FRIEND_API(void) +GCForReason(JSRuntime *rt, gcreason::Reason reason); + +/* + * Perform a non-incremental collection after clearing caches and other + * temporary references to objects. This will remove all unreferenced objects + * in the system. + */ +extern JS_FRIEND_API(void) +ShrinkingGC(JSRuntime *rt, gcreason::Reason reason); /* * Incremental GC: @@ -214,28 +207,16 @@ GCForReason(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason); */ /* - * Begin an incremental collection and perform one slice worth of work. When - * this function returns, the collection may not be complete. - * IncrementalGCSlice() must be called repeatedly until - * !IsIncrementalGCInProgress(rt). + * Begin an incremental collection and perform one slice worth of work or + * perform a slice of an ongoing incremental collection. When this function + * returns, the collection is not complete. This function must be called + * repeatedly until !IsIncrementalGCInProgress(rt). * * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or * shorter than the requested interval. */ -extern JS_PUBLIC_API(void) -StartIncrementalGC(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason, - int64_t millis = 0); - -/* - * Perform a slice of an ongoing incremental collection. When this function - * returns, the collection may not be complete. It must be called repeatedly - * until !IsIncrementalGCInProgress(rt). - * - * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or - * shorter than the requested interval. - */ -extern JS_PUBLIC_API(void) -IncrementalGCSlice(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); +extern JS_FRIEND_API(void) +IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); /* * If IsIncrementalGCInProgress(rt), this call finishes the ongoing collection @@ -243,7 +224,7 @@ IncrementalGCSlice(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); * this is equivalent to GCForReason. When this function returns, * IsIncrementalGCInProgress(rt) will always be false. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason); enum GCProgress { @@ -263,7 +244,7 @@ enum GCProgress { GC_CYCLE_END }; -struct JS_PUBLIC_API(GCDescription) { +struct JS_FRIEND_API(GCDescription) { bool isCompartment_; explicit GCDescription(bool isCompartment) @@ -281,7 +262,7 @@ typedef void * callback may be used for GC notifications as well as to perform additional * marking. */ -extern JS_PUBLIC_API(GCSliceCallback) +extern JS_FRIEND_API(GCSliceCallback) SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); /* @@ -290,7 +271,7 @@ SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); * There is not currently a way to re-enable incremental GC once it has been * disabled on the runtime. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) DisableIncrementalGC(JSRuntime *rt); /* @@ -301,7 +282,7 @@ DisableIncrementalGC(JSRuntime *rt); * GCDescription returned by GCSliceCallback may help narrow down the cause if * collections are not happening incrementally when expected. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsIncrementalGCEnabled(JSRuntime *rt); /* @@ -310,20 +291,20 @@ IsIncrementalGCEnabled(JSRuntime *rt); * pointer callbacks. There is not currently a way to re-enable compacting GC * once it has been disabled on the runtime. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) DisableCompactingGC(JSRuntime *rt); /* * Returns true if compacting GC is enabled. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsCompactingGCEnabled(JSRuntime *rt); /* * Returns true while an incremental GC is ongoing, both when actively * collecting and between slices. */ -extern JS_PUBLIC_API(bool) +JS_FRIEND_API(bool) IsIncrementalGCInProgress(JSRuntime *rt); /* @@ -331,29 +312,29 @@ IsIncrementalGCInProgress(JSRuntime *rt); * This is generally only true when running mutator code in-between GC slices. * At other times, the barrier may be elided for performance. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSRuntime *rt); -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSContext *cx); /* * Notify the GC that a reference to a GC thing is about to be overwritten. * These methods must be called if IsIncrementalBarrierNeeded. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) IncrementalReferenceBarrier(GCCellPtr thing); -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) IncrementalValueBarrier(const Value &v); -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) IncrementalObjectBarrier(JSObject *obj); /* * Returns true if the most recent GC ran incrementally. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) WasIncrementalGC(JSRuntime *rt); /* @@ -365,7 +346,7 @@ WasIncrementalGC(JSRuntime *rt); */ /* Ensure that generational GC is disabled within some scope. */ -class JS_PUBLIC_API(AutoDisableGenerationalGC) +class JS_FRIEND_API(AutoDisableGenerationalGC) { js::gc::GCRuntime *gc; #ifdef JS_GC_ZEAL @@ -381,7 +362,7 @@ class JS_PUBLIC_API(AutoDisableGenerationalGC) * Returns true if generational allocation and collection is currently enabled * on the given runtime. */ -extern JS_PUBLIC_API(bool) +extern JS_FRIEND_API(bool) IsGenerationalGCEnabled(JSRuntime *rt); /* @@ -389,7 +370,7 @@ IsGenerationalGCEnabled(JSRuntime *rt); * of GCs that have been run, but is guaranteed to be monotonically increasing * with GC activity. */ -extern JS_PUBLIC_API(size_t) +extern JS_FRIEND_API(size_t) GetGCNumber(); /* @@ -397,7 +378,7 @@ GetGCNumber(); * back to the system incase it is needed soon afterwards. This call forces the * GC to return this memory immediately. */ -extern JS_PUBLIC_API(void) +extern JS_FRIEND_API(void) ShrinkGCBuffers(JSRuntime *rt); /* diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 8718f4c8fe42..faf5484aa35f 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -238,8 +238,10 @@ GC(JSContext *cx, unsigned argc, jsval *vp) else JS::PrepareForFullGC(cx->runtime()); - JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL; - JS::GCForReason(cx->runtime(), gckind, JS::gcreason::API); + if (shrinking) + JS::ShrinkingGC(cx->runtime(), JS::gcreason::API); + else + JS::GCForReason(cx->runtime(), JS::gcreason::API); char buf[256] = { '\0' }; #ifndef JS_MORE_DETERMINISTIC diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index 022fcdeb8a84..6a1a040740a1 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -160,7 +160,7 @@ function loadCallgraph(file) suppressedFunctions[name] = true; } - for (var gcName of [ 'void js::gc::GCRuntime::collect(uint8, js::SliceBudget, uint32)', + for (var gcName of [ 'void js::gc::GCRuntime::collect(uint8, js::SliceBudget*, uint32, uint32)', 'void js::gc::GCRuntime::minorGC(uint32)', 'void js::gc::GCRuntime::minorGC(uint32)' ]) { diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 197fbc9a3294..0b9001c6122d 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -326,9 +326,8 @@ class GCRuntime } bool gcIfNeeded(JSContext *cx = nullptr); void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason); - void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0); - void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0); - void finishGC(JS::gcreason::Reason reason); + void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0); + void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason); void gcDebugSlice(SliceBudget &budget); void runDebugGC(); @@ -568,9 +567,10 @@ class GCRuntime void startBackgroundAllocTaskIfIdle(); void requestMajorGC(JS::gcreason::Reason reason); - SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis); - void collect(bool incremental, SliceBudget budget, JS::gcreason::Reason reason); - bool gcCycle(bool incremental, SliceBudget &budget, JS::gcreason::Reason reason); + void collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind, + JS::gcreason::Reason reason); + bool gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind, + JS::gcreason::Reason reason); gcstats::ZoneGCStats scanZonesBeforeGC(); void budgetIncrementalGC(SliceBudget &budget); void resetIncrementalGC(const char *reason); diff --git a/js/src/jsapi-tests/testGCFinalizeCallback.cpp b/js/src/jsapi-tests/testGCFinalizeCallback.cpp index b7c681b9aabb..e526ce1c1173 100644 --- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp +++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp @@ -24,7 +24,7 @@ BEGIN_TEST(testGCFinalizeCallback) /* Full GC, incremental. */ FinalizeCalls = 0; JS::PrepareForFullGC(rt); - JS::StartIncrementalGC(rt, GC_NORMAL, JS::gcreason::API, 1000000); + JS::IncrementalGC(rt, JS::gcreason::API, 1000000); CHECK(!rt->gc.isIncrementalGCInProgress()); CHECK(rt->gc.isFullGc()); CHECK(checkMultipleGroups()); @@ -41,7 +41,7 @@ BEGIN_TEST(testGCFinalizeCallback) /* Compartment GC, non-incremental, single compartment. */ FinalizeCalls = 0; JS::PrepareZoneForGC(global1->zone()); - JS::GCForReason(rt, GC_NORMAL, JS::gcreason::API); + JS::GCForReason(rt, JS::gcreason::API); CHECK(!rt->gc.isFullGc()); CHECK(checkSingleGroup()); CHECK(checkFinalizeStatus()); @@ -52,7 +52,7 @@ BEGIN_TEST(testGCFinalizeCallback) JS::PrepareZoneForGC(global1->zone()); JS::PrepareZoneForGC(global2->zone()); JS::PrepareZoneForGC(global3->zone()); - JS::GCForReason(rt, GC_NORMAL, JS::gcreason::API); + JS::GCForReason(rt, JS::gcreason::API); CHECK(!rt->gc.isFullGc()); CHECK(checkSingleGroup()); CHECK(checkFinalizeStatus()); @@ -61,7 +61,7 @@ BEGIN_TEST(testGCFinalizeCallback) /* Compartment GC, incremental, single compartment. */ FinalizeCalls = 0; JS::PrepareZoneForGC(global1->zone()); - JS::StartIncrementalGC(rt, GC_NORMAL, JS::gcreason::API, 1000000); + JS::IncrementalGC(rt, JS::gcreason::API, 1000000); CHECK(!rt->gc.isIncrementalGCInProgress()); CHECK(!rt->gc.isFullGc()); CHECK(checkSingleGroup()); @@ -73,7 +73,7 @@ BEGIN_TEST(testGCFinalizeCallback) JS::PrepareZoneForGC(global1->zone()); JS::PrepareZoneForGC(global2->zone()); JS::PrepareZoneForGC(global3->zone()); - JS::StartIncrementalGC(rt, GC_NORMAL, JS::gcreason::API, 1000000); + JS::IncrementalGC(rt, JS::gcreason::API, 1000000); CHECK(!rt->gc.isIncrementalGCInProgress()); CHECK(!rt->gc.isFullGc()); CHECK(checkMultipleGroups()); diff --git a/js/src/jsapi-tests/testPreserveJitCode.cpp b/js/src/jsapi-tests/testPreserveJitCode.cpp index 40ee89a8d030..fd7d328a3e0c 100644 --- a/js/src/jsapi-tests/testPreserveJitCode.cpp +++ b/js/src/jsapi-tests/testPreserveJitCode.cpp @@ -65,10 +65,10 @@ testPreserveJitCode(bool preserveJitCode, unsigned remainingIonScripts) CHECK_EQUAL(value.toInt32(), 45); CHECK_EQUAL(countIonScripts(global), 1u); - GCForReason(rt, GC_NORMAL, gcreason::API); + GCForReason(rt, gcreason::API); CHECK_EQUAL(countIonScripts(global), remainingIonScripts); - GCForReason(rt, GC_SHRINK, gcreason::API); + ShrinkingGC(rt, gcreason::API); CHECK_EQUAL(countIonScripts(global), 0u); return true; diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 20bd5ea2f798..d01113d93802 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -428,7 +428,7 @@ class AutoLeaveZeal JS_GetGCZeal(cx_, &zeal_, &frequency_); JS_SetGCZeal(cx_, 0, 0); JS::PrepareForFullGC(JS_GetRuntime(cx_)); - JS::GCForReason(JS_GetRuntime(cx_), GC_SHRINK, JS::gcreason::DEBUG_GC); + JS::ShrinkingGC(JS_GetRuntime(cx_), JS::gcreason::DEBUG_GC); } ~AutoLeaveZeal() { JS_SetGCZeal(cx_, zeal_, frequency_); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 7169c4527edf..c9645f7853a6 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -153,6 +153,72 @@ JS_NewObjectWithUniqueType(JSContext *cx, const JSClass *clasp, HandleObject pro return obj; } +JS_FRIEND_API(void) +JS::PrepareZoneForGC(Zone *zone) +{ + zone->scheduleGC(); +} + +JS_FRIEND_API(void) +JS::PrepareForFullGC(JSRuntime *rt) +{ + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) + zone->scheduleGC(); +} + +JS_FRIEND_API(void) +JS::PrepareForIncrementalGC(JSRuntime *rt) +{ + if (!JS::IsIncrementalGCInProgress(rt)) + return; + + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { + if (zone->wasGCStarted()) + PrepareZoneForGC(zone); + } +} + +JS_FRIEND_API(bool) +JS::IsGCScheduled(JSRuntime *rt) +{ + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { + if (zone->isGCScheduled()) + return true; + } + + return false; +} + +JS_FRIEND_API(void) +JS::SkipZoneForGC(Zone *zone) +{ + zone->unscheduleGC(); +} + +JS_FRIEND_API(void) +JS::GCForReason(JSRuntime *rt, gcreason::Reason reason) +{ + rt->gc.gc(GC_NORMAL, reason); +} + +JS_FRIEND_API(void) +JS::ShrinkingGC(JSRuntime *rt, gcreason::Reason reason) +{ + rt->gc.gc(GC_SHRINK, reason); +} + +JS_FRIEND_API(void) +JS::IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis) +{ + rt->gc.gcSlice(GC_NORMAL, reason, millis); +} + +JS_FRIEND_API(void) +JS::FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason) +{ + rt->gc.gcFinalSlice(GC_NORMAL, reason); +} + JS_FRIEND_API(JSPrincipals *) JS_GetCompartmentPrincipals(JSCompartment *compartment) { @@ -997,6 +1063,12 @@ js::IsContextRunningJS(JSContext *cx) return cx->currentlyRunning(); } +JS_FRIEND_API(JS::GCSliceCallback) +JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) +{ + return rt->gc.setSliceCallback(callback); +} + JS_FRIEND_API(int64_t) GetMaxGCPauseSinceClear(JSRuntime *rt) { @@ -1009,12 +1081,160 @@ ClearMaxGCPauseAccumulator(JSRuntime *rt) return rt->gc.stats.clearMaxGCPauseAccumulator(); } +JS_FRIEND_API(bool) +JS::WasIncrementalGC(JSRuntime *rt) +{ + return rt->gc.isIncrementalGc(); +} + +char16_t * +JS::GCDescription::formatMessage(JSRuntime *rt) const +{ + return rt->gc.stats.formatMessage(); +} + +char16_t * +JS::GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const +{ + return rt->gc.stats.formatJSON(timestamp); +} + JS_FRIEND_API(void) JS::NotifyDidPaint(JSRuntime *rt) { rt->gc.notifyDidPaint(); } +JS_FRIEND_API(bool) +JS::IsIncrementalGCEnabled(JSRuntime *rt) +{ + return rt->gc.isIncrementalGCEnabled(); +} + +JS_FRIEND_API(bool) +JS::IsIncrementalGCInProgress(JSRuntime *rt) +{ + return rt->gc.isIncrementalGCInProgress() && !rt->gc.isVerifyPreBarriersEnabled(); +} + +JS_FRIEND_API(void) +JS::DisableIncrementalGC(JSRuntime *rt) +{ + rt->gc.disallowIncrementalGC(); +} + +JS_FRIEND_API(void) +JS::DisableCompactingGC(JSRuntime *rt) +{ +#ifdef JSGC_COMPACTING + rt->gc.disableCompactingGC(); +#endif +} + +JS_FRIEND_API(bool) +JS::IsCompactingGCEnabled(JSRuntime *rt) +{ +#ifdef JSGC_COMPACTING + return rt->gc.isCompactingGCEnabled(); +#else + return false; +#endif +} + +JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSRuntime *rt) + : gc(&rt->gc) +#ifdef JS_GC_ZEAL + , restartVerifier(false) +#endif +{ +#ifdef JS_GC_ZEAL + restartVerifier = gc->endVerifyPostBarriers(); +#endif + gc->disableGenerationalGC(); +} + +JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() +{ + gc->enableGenerationalGC(); +#ifdef JS_GC_ZEAL + if (restartVerifier) { + MOZ_ASSERT(gc->isGenerationalGCEnabled()); + gc->startVerifyPostBarriers(); + } +#endif +} + +extern JS_FRIEND_API(bool) +JS::IsGenerationalGCEnabled(JSRuntime *rt) +{ + return rt->gc.isGenerationalGCEnabled(); +} + +JS_FRIEND_API(bool) +JS::IsIncrementalBarrierNeeded(JSRuntime *rt) +{ + return rt->gc.state() == gc::MARK && !rt->isHeapBusy(); +} + +JS_FRIEND_API(bool) +JS::IsIncrementalBarrierNeeded(JSContext *cx) +{ + return IsIncrementalBarrierNeeded(cx->runtime()); +} + +JS_FRIEND_API(void) +JS::IncrementalObjectBarrier(JSObject *obj) +{ + if (!obj) + return; + + MOZ_ASSERT(!obj->zone()->runtimeFromMainThread()->isHeapMajorCollecting()); + + JSObject::writeBarrierPre(obj); +} + +JS_FRIEND_API(void) +JS::IncrementalReferenceBarrier(GCCellPtr thing) +{ + if (!thing) + return; + + if (thing.isString() && StringIsPermanentAtom(thing.toString())) + return; + +#ifdef DEBUG + Zone *zone = thing.isObject() + ? thing.toObject()->zone() + : thing.asCell()->asTenured().zone(); + MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting()); +#endif + + switch(thing.kind()) { + case JSTRACE_OBJECT: return JSObject::writeBarrierPre(thing.toObject()); + case JSTRACE_STRING: return JSString::writeBarrierPre(thing.toString()); + case JSTRACE_SCRIPT: return JSScript::writeBarrierPre(thing.toScript()); + case JSTRACE_SYMBOL: return JS::Symbol::writeBarrierPre(thing.toSymbol()); + case JSTRACE_LAZY_SCRIPT: + return LazyScript::writeBarrierPre(static_cast(thing.asCell())); + case JSTRACE_JITCODE: + return jit::JitCode::writeBarrierPre(static_cast(thing.asCell())); + case JSTRACE_SHAPE: + return Shape::writeBarrierPre(static_cast(thing.asCell())); + case JSTRACE_BASE_SHAPE: + return BaseShape::writeBarrierPre(static_cast(thing.asCell())); + case JSTRACE_TYPE_OBJECT: + return types::TypeObject::writeBarrierPre(static_cast(thing.asCell())); + default: + MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); + } +} + +JS_FRIEND_API(void) +JS::IncrementalValueBarrier(const Value &v) +{ + js::HeapValue::writeBarrierPre(v); +} + JS_FRIEND_API(void) JS::PokeGC(JSRuntime *rt) { diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 60597e3bb93c..beec59c69fb7 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3263,7 +3263,7 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone *zone, const AutoLockGC &lock) // to try to avoid performing non-incremental GCs on zones // which allocate a lot of data, even when incremental slices // can't be triggered via scheduling in the event loop. - triggerZoneGC(zone, JS::gcreason::INCREMENTAL_ALLOC_TRIGGER); + triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER); // Delay the next slice until a certain amount of allocation // has been performed. @@ -3332,7 +3332,7 @@ GCRuntime::maybeGC(Zone *zone) !isBackgroundSweeping()) { PrepareZoneForGC(zone); - gc(GC_NORMAL, JS::gcreason::MAYBEGC); + gcSlice(GC_NORMAL, JS::gcreason::MAYBEGC); return true; } @@ -3358,7 +3358,7 @@ GCRuntime::maybePeriodicFullGC() numArenasFreeCommitted > decommitThreshold) { JS::PrepareForFullGC(rt); - gc(GC_SHRINK, JS::gcreason::MAYBEGC); + gcSlice(GC_SHRINK, JS::gcreason::MAYBEGC); } else { nextFullGCTime = now + GC_IDLE_FULL_SPAN; } @@ -5785,16 +5785,9 @@ GCRuntime::resetIncrementalGC(const char *reason) /* Finish sweeping the current zone group, then abort. */ abortSweepAfterCurrentGroup = true; - - /* Don't perform any compaction after sweeping. */ - JSGCInvocationKind oldInvocationKind = invocationKind; - invocationKind = GC_NORMAL; - SliceBudget budget; incrementalCollectSlice(budget, JS::gcreason::RESET); - invocationKind = oldInvocationKind; - { gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD); rt->gc.waitBackgroundSweepOrAllocEnd(); @@ -6117,7 +6110,8 @@ class AutoDisableStoreBuffer * to run another cycle. */ MOZ_NEVER_INLINE bool -GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JS::gcreason::Reason reason) +GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind, + JS::gcreason::Reason reason) { evictNursery(reason); @@ -6180,6 +6174,10 @@ GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JS::gcreason::Reason r TraceMajorGCStart(); + /* Set the invocation kind in the first slice. */ + if (!isIncrementalGCInProgress()) + invocationKind = gckind; + incrementalCollectSlice(budget, reason); #ifndef JS_MORE_DETERMINISTIC @@ -6264,7 +6262,8 @@ GCRuntime::scanZonesBeforeGC() } void -GCRuntime::collect(bool incremental, SliceBudget budget, JS::gcreason::Reason reason) +GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind, + JS::gcreason::Reason reason) { /* GC shouldn't be running in parallel execution mode */ MOZ_ALWAYS_TRUE(!InParallelSection()); @@ -6291,9 +6290,9 @@ GCRuntime::collect(bool incremental, SliceBudget budget, JS::gcreason::Reason re AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC || reason == JS::gcreason::DESTROY_RUNTIME); - gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, reason); + gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), gckind, reason); - cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind); + cleanUpEverything = ShouldCleanUpEverything(reason, gckind); bool repeat = false; do { @@ -6308,7 +6307,7 @@ GCRuntime::collect(bool incremental, SliceBudget budget, JS::gcreason::Reason re } poked = false; - bool wasReset = gcCycle(incremental, budget, reason); + bool wasReset = gcCycle(incremental, budget, gckind, reason); if (!isIncrementalGCInProgress()) { gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END); @@ -6350,48 +6349,34 @@ GCRuntime::collect(bool incremental, SliceBudget budget, JS::gcreason::Reason re EnqueuePendingParseTasksAfterGC(rt); } -SliceBudget -GCRuntime::defaultBudget(JS::gcreason::Reason reason, int64_t millis) -{ - if (millis == 0) { - if (reason == JS::gcreason::ALLOC_TRIGGER) - millis = sliceBudget; - else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled()) - millis = sliceBudget * IGC_MARK_SLICE_MULTIPLIER; - else - millis = sliceBudget; - } - - return SliceBudget(TimeBudget(millis)); -} - void GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason) { - invocationKind = gckind; - collect(false, SliceBudget(), reason); + SliceBudget budget; + collect(false, budget, gckind, reason); } void -GCRuntime::startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis) +GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis) { - MOZ_ASSERT(!isIncrementalGCInProgress()); - invocationKind = gckind; - collect(true, defaultBudget(reason, millis), reason); + SliceBudget budget; + if (millis) + budget = SliceBudget(TimeBudget(millis)); + else if (reason == JS::gcreason::ALLOC_TRIGGER) + budget = SliceBudget(TimeBudget(sliceBudget)); + else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled()) + budget = SliceBudget(TimeBudget(sliceBudget * IGC_MARK_SLICE_MULTIPLIER)); + else + budget = SliceBudget(TimeBudget(sliceBudget)); + + collect(true, budget, gckind, reason); } void -GCRuntime::gcSlice(JS::gcreason::Reason reason, int64_t millis) +GCRuntime::gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason) { - MOZ_ASSERT(isIncrementalGCInProgress()); - collect(true, defaultBudget(reason, millis), reason); -} - -void -GCRuntime::finishGC(JS::gcreason::Reason reason) -{ - MOZ_ASSERT(isIncrementalGCInProgress()); - collect(true, SliceBudget(), reason); + SliceBudget budget; + collect(true, budget, gckind, reason); } void @@ -6410,14 +6395,14 @@ GCRuntime::notifyDidPaint() if (zealMode == ZealFrameGCValue) { JS::PrepareForFullGC(rt); - gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME); + gcSlice(GC_NORMAL, JS::gcreason::REFRESH_FRAME); return; } #endif - if (isIncrementalGCInProgress() && !interFrameGC) { + if (JS::IsIncrementalGCInProgress(rt) && !interFrameGC) { JS::PrepareForIncrementalGC(rt); - gcSlice(JS::gcreason::REFRESH_FRAME); + gcSlice(GC_NORMAL, JS::gcreason::REFRESH_FRAME); } interFrameGC = false; @@ -6437,14 +6422,12 @@ void GCRuntime::gcDebugSlice(SliceBudget &budget) { if (!ZonesSelected(rt)) { - if (isIncrementalGCInProgress()) + if (JS::IsIncrementalGCInProgress(rt)) JS::PrepareForIncrementalGC(rt); else JS::PrepareForFullGC(rt); } - if (!isIncrementalGCInProgress()) - invocationKind = GC_NORMAL; - collect(true, budget, JS::gcreason::DEBUG_GC); + collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC); } /* Schedule a full GC unless a zone will already be collected. */ @@ -6455,7 +6438,7 @@ js::PrepareForDebugGC(JSRuntime *rt) JS::PrepareForFullGC(rt); } -JS_PUBLIC_API(void) +JS_FRIEND_API(void) JS::ShrinkGCBuffers(JSRuntime *rt) { rt->gc.shrinkBuffers(); @@ -6563,14 +6546,7 @@ GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */) } if (majorGCRequested) { - if (majorGCTriggerReason == JS::gcreason::INCREMENTAL_ALLOC_TRIGGER) { - if (!isIncrementalGCInProgress()) - startGC(GC_NORMAL, majorGCTriggerReason); - else - gcSlice(majorGCTriggerReason); - } else { - gc(GC_NORMAL, majorGCTriggerReason); - } + gcSlice(GC_NORMAL, rt->gc.majorGCTriggerReason); return true; } @@ -6735,9 +6711,7 @@ GCRuntime::runDebugGC() budget = SliceBudget(WorkBudget(1)); } - if (!isIncrementalGCInProgress()) - invocationKind = GC_NORMAL; - collect(true, budget, JS::gcreason::DEBUG_GC); + collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC); /* * For multi-slice zeal, reset the slice size when we get to the sweep @@ -6749,9 +6723,9 @@ GCRuntime::runDebugGC() incrementalLimit = zealFrequency / 2; } } else if (type == ZealCompactValue) { - gc(GC_SHRINK, JS::gcreason::DEBUG_GC); + collect(false, budget, GC_SHRINK, JS::gcreason::DEBUG_GC); } else { - gc(GC_NORMAL, JS::gcreason::DEBUG_GC); + collect(false, budget, GC_NORMAL, JS::gcreason::DEBUG_GC); } #endif @@ -6978,7 +6952,7 @@ js::gc::AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind) MOZ_ASSERT(MapAllocToTraceKind(cell->asTenured().getAllocKind()) == kind); } -JS_PUBLIC_API(size_t) +JS_FRIEND_API(size_t) JS::GetGCNumber() { JSRuntime *rt = js::TlsPerThreadData.get()->runtimeFromMainThread(); @@ -7116,225 +7090,3 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt) } } #endif - -JS_PUBLIC_API(void) -JS::PrepareZoneForGC(Zone *zone) -{ - zone->scheduleGC(); -} - -JS_PUBLIC_API(void) -JS::PrepareForFullGC(JSRuntime *rt) -{ - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - zone->scheduleGC(); -} - -JS_PUBLIC_API(void) -JS::PrepareForIncrementalGC(JSRuntime *rt) -{ - if (!JS::IsIncrementalGCInProgress(rt)) - return; - - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - if (zone->wasGCStarted()) - PrepareZoneForGC(zone); - } -} - -JS_PUBLIC_API(bool) -JS::IsGCScheduled(JSRuntime *rt) -{ - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - if (zone->isGCScheduled()) - return true; - } - - return false; -} - -JS_PUBLIC_API(void) -JS::SkipZoneForGC(Zone *zone) -{ - zone->unscheduleGC(); -} - -JS_PUBLIC_API(void) -JS::GCForReason(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason) -{ - MOZ_ASSERT(gckind == GC_NORMAL || gckind == GC_SHRINK); - rt->gc.gc(gckind, reason); -} - -JS_PUBLIC_API(void) -JS::StartIncrementalGC(JSRuntime *rt, JSGCInvocationKind gckind, gcreason::Reason reason, int64_t millis) -{ - MOZ_ASSERT(gckind == GC_NORMAL || gckind == GC_SHRINK); - rt->gc.startGC(gckind, reason, millis); -} - -JS_PUBLIC_API(void) -JS::IncrementalGCSlice(JSRuntime *rt, gcreason::Reason reason, int64_t millis) -{ - rt->gc.gcSlice(reason, millis); -} - -JS_PUBLIC_API(void) -JS::FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason) -{ - rt->gc.finishGC(reason); -} - -char16_t * -JS::GCDescription::formatMessage(JSRuntime *rt) const -{ - return rt->gc.stats.formatMessage(); -} - -char16_t * -JS::GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const -{ - return rt->gc.stats.formatJSON(timestamp); -} - -JS_PUBLIC_API(JS::GCSliceCallback) -JS::SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback) -{ - return rt->gc.setSliceCallback(callback); -} - -JS_PUBLIC_API(void) -JS::DisableIncrementalGC(JSRuntime *rt) -{ - rt->gc.disallowIncrementalGC(); -} - -JS_PUBLIC_API(bool) -JS::IsIncrementalGCEnabled(JSRuntime *rt) -{ - return rt->gc.isIncrementalGCEnabled(); -} - -JS_PUBLIC_API(void) -JS::DisableCompactingGC(JSRuntime *rt) -{ -#ifdef JSGC_COMPACTING - rt->gc.disableCompactingGC(); -#endif -} - -JS_PUBLIC_API(bool) -JS::IsCompactingGCEnabled(JSRuntime *rt) -{ -#ifdef JSGC_COMPACTING - return rt->gc.isCompactingGCEnabled(); -#else - return false; -#endif -} - -JS_PUBLIC_API(bool) -JS::IsIncrementalGCInProgress(JSRuntime *rt) -{ - return rt->gc.isIncrementalGCInProgress() && !rt->gc.isVerifyPreBarriersEnabled(); -} - -JS_PUBLIC_API(bool) -JS::IsIncrementalBarrierNeeded(JSRuntime *rt) -{ - return rt->gc.state() == gc::MARK && !rt->isHeapBusy(); -} - -JS_PUBLIC_API(bool) -JS::IsIncrementalBarrierNeeded(JSContext *cx) -{ - return IsIncrementalBarrierNeeded(cx->runtime()); -} - -JS_PUBLIC_API(void) -JS::IncrementalReferenceBarrier(GCCellPtr thing) -{ - if (!thing) - return; - - if (thing.isString() && StringIsPermanentAtom(thing.toString())) - return; - -#ifdef DEBUG - Zone *zone = thing.isObject() - ? thing.toObject()->zone() - : thing.asCell()->asTenured().zone(); - MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting()); -#endif - - switch(thing.kind()) { - case JSTRACE_OBJECT: return JSObject::writeBarrierPre(thing.toObject()); - case JSTRACE_STRING: return JSString::writeBarrierPre(thing.toString()); - case JSTRACE_SCRIPT: return JSScript::writeBarrierPre(thing.toScript()); - case JSTRACE_SYMBOL: return JS::Symbol::writeBarrierPre(thing.toSymbol()); - case JSTRACE_LAZY_SCRIPT: - return LazyScript::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_JITCODE: - return jit::JitCode::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_SHAPE: - return Shape::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_BASE_SHAPE: - return BaseShape::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_TYPE_OBJECT: - return types::TypeObject::writeBarrierPre(static_cast(thing.asCell())); - default: - MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); - } -} - -JS_PUBLIC_API(void) -JS::IncrementalValueBarrier(const Value &v) -{ - js::HeapValue::writeBarrierPre(v); -} - -JS_PUBLIC_API(void) -JS::IncrementalObjectBarrier(JSObject *obj) -{ - if (!obj) - return; - - MOZ_ASSERT(!obj->zone()->runtimeFromMainThread()->isHeapMajorCollecting()); - - JSObject::writeBarrierPre(obj); -} - -JS_PUBLIC_API(bool) -JS::WasIncrementalGC(JSRuntime *rt) -{ - return rt->gc.isIncrementalGc(); -} - -JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSRuntime *rt) - : gc(&rt->gc) -#ifdef JS_GC_ZEAL - , restartVerifier(false) -#endif -{ -#ifdef JS_GC_ZEAL - restartVerifier = gc->endVerifyPostBarriers(); -#endif - gc->disableGenerationalGC(); -} - -JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() -{ - gc->enableGenerationalGC(); -#ifdef JS_GC_ZEAL - if (restartVerifier) { - MOZ_ASSERT(gc->isGenerationalGCEnabled()); - gc->startVerifyPostBarriers(); - } -#endif -} - -JS_PUBLIC_API(bool) -JS::IsGenerationalGCEnabled(JSRuntime *rt) -{ - return rt->gc.isGenerationalGCEnabled(); -} diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d9940e4febc5..4ca9ab92b3b2 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -945,6 +945,17 @@ TraceRuntime(JSTracer *trc); extern void ReleaseAllJITCode(FreeOp *op); +/* + * Kinds of js_GC invocation. + */ +typedef enum JSGCInvocationKind { + /* Normal invocation. */ + GC_NORMAL = 0, + + /* Minimize GC triggers and release empty GC chunks right away. */ + GC_SHRINK = 1 +} JSGCInvocationKind; + extern void PrepareForDebugGC(JSRuntime *rt); diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 6af61cbdd8ed..e6aa4593281e 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2750,7 +2750,7 @@ nsXPCComponents_Utils::ForceGC() { JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->Runtime(); PrepareForFullGC(rt); - GCForReason(rt, GC_NORMAL, gcreason::COMPONENT_UTILS); + GCForReason(rt, gcreason::COMPONENT_UTILS); return NS_OK; } @@ -2800,7 +2800,7 @@ nsXPCComponents_Utils::ForceShrinkingGC() { JSRuntime* rt = nsXPConnect::GetRuntimeInstance()->Runtime(); PrepareForFullGC(rt); - GCForReason(rt, GC_SHRINK, gcreason::COMPONENT_UTILS); + ShrinkingGC(rt, gcreason::COMPONENT_UTILS); return NS_OK; } @@ -2823,8 +2823,10 @@ class PreciseGCRunnable : public nsRunnable } PrepareForFullGC(rt); - JSGCInvocationKind gckind = mShrinking ? GC_SHRINK : GC_NORMAL; - GCForReason(rt, gckind, gcreason::COMPONENT_UTILS); + if (mShrinking) + ShrinkingGC(rt, gcreason::COMPONENT_UTILS); + else + GCForReason(rt, gcreason::COMPONENT_UTILS); mCallback->Callback(); return NS_OK; diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 0e10a427c394..e4e6b5be3d4e 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1025,7 +1025,7 @@ CycleCollectedJSRuntime::GarbageCollect(uint32_t aReason) const JS::gcreason::Reason gcreason = static_cast(aReason); JS::PrepareForFullGC(mJSRuntime); - JS::GCForReason(mJSRuntime, GC_NORMAL, gcreason); + JS::GCForReason(mJSRuntime, gcreason); } void