285 lines
9.3 KiB
Diff
285 lines
9.3 KiB
Diff
From 272c1ba11fac7a9ceede2f4f737bb27b4bbcad71 Mon Sep 17 00:00:00 2001
|
|
From: Steve Fink <sfink@mozilla.com>
|
|
Date: Thu, 19 Mar 2015 20:50:57 -0700
|
|
Subject: [PATCH] Bug 1120655 - Suppress zone/compartment collection while
|
|
iterating. r=terrence, a=bkerensa
|
|
|
|
---
|
|
js/src/gc/Zone.h | 9 ++++----
|
|
js/src/jsgc.cpp | 57 +++++++++++++++++++++++++++++++++++----------------
|
|
js/src/jsgc.h | 11 +++++++++-
|
|
js/src/vm/Runtime.cpp | 1 +
|
|
js/src/vm/Runtime.h | 3 +++
|
|
5 files changed, 58 insertions(+), 23 deletions(-)
|
|
|
|
diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
|
|
index e7f687a..dd058f0 100644
|
|
--- a/js/src/gc/Zone.h
|
|
+++ b/js/src/gc/Zone.h
|
|
@@ -353,10 +353,11 @@ enum ZoneSelector {
|
|
|
|
class ZonesIter {
|
|
private:
|
|
+ gc::AutoEnterIteration iterMarker;
|
|
JS::Zone **it, **end;
|
|
|
|
public:
|
|
- ZonesIter(JSRuntime *rt, ZoneSelector selector) {
|
|
+ ZonesIter(JSRuntime *rt, ZoneSelector selector) : iterMarker(rt) {
|
|
it = rt->zones.begin();
|
|
end = rt->zones.end();
|
|
|
|
@@ -427,13 +428,13 @@ struct CompartmentsInZoneIter
|
|
template<class ZonesIterT>
|
|
class CompartmentsIterT
|
|
{
|
|
- private:
|
|
+ gc::AutoEnterIteration iterMarker;
|
|
ZonesIterT zone;
|
|
mozilla::Maybe<CompartmentsInZoneIter> comp;
|
|
|
|
public:
|
|
explicit CompartmentsIterT(JSRuntime *rt)
|
|
- : zone(rt)
|
|
+ : iterMarker(rt), zone(rt)
|
|
{
|
|
if (zone.done())
|
|
comp.construct();
|
|
@@ -442,7 +443,7 @@ class CompartmentsIterT
|
|
}
|
|
|
|
CompartmentsIterT(JSRuntime *rt, ZoneSelector selector)
|
|
- : zone(rt, selector)
|
|
+ : iterMarker(rt), zone(rt, selector)
|
|
{
|
|
if (zone.done())
|
|
comp.construct();
|
|
diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
|
|
index 15c86c8..1dfe0ab 100644
|
|
--- a/js/src/jsgc.cpp
|
|
+++ b/js/src/jsgc.cpp
|
|
@@ -2525,7 +2525,7 @@ ReleaseObservedTypes(JSRuntime* rt)
|
|
* arbitrary compartment in the zone.
|
|
*/
|
|
static void
|
|
-SweepCompartments(FreeOp *fop, Zone *zone, bool keepAtleastOne, bool lastGC)
|
|
+SweepCompartments(FreeOp *fop, Zone *zone, bool keepAtleastOne, bool destroyingRuntime)
|
|
{
|
|
JSRuntime *rt = zone->runtimeFromMainThread();
|
|
JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
|
|
@@ -2543,7 +2543,7 @@ SweepCompartments(FreeOp *fop, Zone *zone, bool keepAtleastOne, bool lastGC)
|
|
* deleted and keepAtleastOne is true.
|
|
*/
|
|
bool dontDelete = read == end && !foundOne && keepAtleastOne;
|
|
- if ((!comp->marked && !dontDelete) || lastGC) {
|
|
+ if ((!comp->marked && !dontDelete) || destroyingRuntime) {
|
|
if (callback)
|
|
callback(fop, comp);
|
|
if (comp->principals)
|
|
@@ -2559,9 +2559,13 @@ SweepCompartments(FreeOp *fop, Zone *zone, bool keepAtleastOne, bool lastGC)
|
|
}
|
|
|
|
static void
|
|
-SweepZones(FreeOp *fop, bool lastGC)
|
|
+SweepZones(FreeOp *fop, bool destroyingRuntime)
|
|
{
|
|
JSRuntime *rt = fop->runtime();
|
|
+ MOZ_ASSERT_IF(destroyingRuntime, rt->numActiveZoneIters == 0);
|
|
+ if (rt->numActiveZoneIters)
|
|
+ return;
|
|
+
|
|
JSZoneCallback callback = rt->destroyZoneCallback;
|
|
|
|
/* Skip the atomsCompartment zone. */
|
|
@@ -2576,17 +2580,17 @@ SweepZones(FreeOp* fop, bool lastGC)
|
|
|
|
if (zone->wasGCStarted()) {
|
|
if ((zone->allocator.arenas.arenaListsAreEmpty() && !zone->hasMarkedCompartments()) ||
|
|
- lastGC)
|
|
+ destroyingRuntime)
|
|
{
|
|
zone->allocator.arenas.checkEmptyFreeLists();
|
|
if (callback)
|
|
callback(zone);
|
|
- SweepCompartments(fop, zone, false, lastGC);
|
|
+ SweepCompartments(fop, zone, false, destroyingRuntime);
|
|
JS_ASSERT(zone->compartments.empty());
|
|
fop->delete_(zone);
|
|
continue;
|
|
}
|
|
- SweepCompartments(fop, zone, true, lastGC);
|
|
+ SweepCompartments(fop, zone, true, destroyingRuntime);
|
|
}
|
|
*write++ = zone;
|
|
}
|
|
@@ -3787,7 +3791,7 @@ EndSweepingZoneGroup(JSRuntime *rt)
|
|
}
|
|
|
|
static void
|
|
-BeginSweepPhase(JSRuntime *rt, bool lastGC)
|
|
+BeginSweepPhase(JSRuntime *rt, bool destroyingRuntime)
|
|
{
|
|
/*
|
|
* Sweep phase.
|
|
@@ -3804,7 +3808,7 @@ BeginSweepPhase(JSRuntime *rt, bool lastGC)
|
|
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP);
|
|
|
|
#ifdef JS_THREADSAFE
|
|
- rt->gcSweepOnBackgroundThread = !lastGC && rt->useHelperThreads();
|
|
+ rt->gcSweepOnBackgroundThread = !destroyingRuntime && rt->useHelperThreads();
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
@@ -3903,12 +3907,12 @@ SweepPhase(JSRuntime *rt, SliceBudget &sliceBudget)
|
|
}
|
|
|
|
static void
|
|
-EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
|
+EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool destroyingRuntime)
|
|
{
|
|
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP);
|
|
FreeOp fop(rt, rt->gcSweepOnBackgroundThread);
|
|
|
|
- JS_ASSERT_IF(lastGC, !rt->gcSweepOnBackgroundThread);
|
|
+ JS_ASSERT_IF(destroyingRuntime, !rt->gcSweepOnBackgroundThread);
|
|
|
|
JS_ASSERT(rt->gcMarker.isDrained());
|
|
rt->gcMarker.stop();
|
|
@@ -3959,8 +3963,8 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
|
* This removes compartments from rt->compartment, so we do it last to make
|
|
* sure we don't miss sweeping any compartments.
|
|
*/
|
|
- if (!lastGC)
|
|
- SweepZones(&fop, lastGC);
|
|
+ if (!destroyingRuntime)
|
|
+ SweepZones(&fop, destroyingRuntime);
|
|
|
|
if (!rt->gcSweepOnBackgroundThread) {
|
|
/*
|
|
@@ -4001,8 +4005,8 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
|
rt->freeLifoAlloc.freeAll();
|
|
|
|
/* Ensure the compartments get swept if it's the last GC. */
|
|
- if (lastGC)
|
|
- SweepZones(&fop, lastGC);
|
|
+ if (destroyingRuntime)
|
|
+ SweepZones(&fop, destroyingRuntime);
|
|
}
|
|
|
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
|
@@ -4339,7 +4343,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|
AutoCopyFreeListToArenasForGC copy(rt);
|
|
AutoGCSlice slice(rt);
|
|
|
|
- bool lastGC = (reason == JS::gcreason::DESTROY_RUNTIME);
|
|
+ bool destroyingRuntime = (reason == JS::gcreason::DESTROY_RUNTIME);
|
|
|
|
gc::State initialState = rt->gcIncrementalState;
|
|
|
|
@@ -4384,7 +4388,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|
return;
|
|
}
|
|
|
|
- if (!lastGC)
|
|
+ if (!destroyingRuntime)
|
|
PushZealSelectedObjects(rt);
|
|
|
|
rt->gcIncrementalState = MARK;
|
|
@@ -4426,7 +4430,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|
* This runs to completion, but we don't continue if the budget is
|
|
* now exhasted.
|
|
*/
|
|
- BeginSweepPhase(rt, lastGC);
|
|
+ BeginSweepPhase(rt, destroyingRuntime);
|
|
if (sliceBudget.isOverBudget())
|
|
break;
|
|
|
|
@@ -4445,7 +4449,7 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|
if (!finished)
|
|
break;
|
|
|
|
- EndSweepPhase(rt, gckind, lastGC);
|
|
+ EndSweepPhase(rt, gckind, destroyingRuntime);
|
|
|
|
if (rt->gcSweepOnBackgroundThread)
|
|
rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
|
|
@@ -5386,3 +5390,20 @@ JS::AutoAssertNoGC::~AutoAssertNoGC()
|
|
MOZ_ASSERT(gcNumber == runtime->gcNumber, "GC ran inside an AutoAssertNoGC scope.");
|
|
}
|
|
#endif
|
|
+
|
|
+namespace js {
|
|
+namespace gc {
|
|
+
|
|
+AutoEnterIteration::AutoEnterIteration(JSRuntime *rt_) : rt(rt_)
|
|
+{
|
|
+ ++rt->numActiveZoneIters;
|
|
+}
|
|
+
|
|
+AutoEnterIteration::~AutoEnterIteration()
|
|
+{
|
|
+ MOZ_ASSERT(rt->numActiveZoneIters);
|
|
+ --rt->numActiveZoneIters;
|
|
+}
|
|
+
|
|
+} /* namespace gc */
|
|
+} /* namespace js */
|
|
diff --git a/js/src/jsgc.h b/js/src/jsgc.h
|
|
index 825cff5..ca331c0 100644
|
|
--- a/js/src/jsgc.h
|
|
+++ b/js/src/jsgc.h
|
|
@@ -1077,7 +1077,7 @@ MaybeVerifyBarriers(JSContext* cx, bool always = false)
|
|
/*
|
|
* Instances of this class set the |JSRuntime::suppressGC| flag for the duration
|
|
* that they are live. Use of this class is highly discouraged. Please carefully
|
|
- * read the comment in jscntxt.h above |suppressGC| and take all appropriate
|
|
+ * read the comment in vm/Runtime.h above |suppressGC| and take all appropriate
|
|
* precautions before instantiating this class.
|
|
*/
|
|
class AutoSuppressGC
|
|
@@ -1113,6 +1113,15 @@ class AutoEnterOOMUnsafeRegion
|
|
class AutoEnterOOMUnsafeRegion {};
|
|
#endif /* DEBUG */
|
|
|
|
+/* Prevent compartments and zones from being collected during iteration. */
|
|
+class AutoEnterIteration {
|
|
+ JSRuntime *rt;
|
|
+
|
|
+ public:
|
|
+ AutoEnterIteration(JSRuntime *rt_);
|
|
+ ~AutoEnterIteration();
|
|
+};
|
|
+
|
|
} /* namespace gc */
|
|
|
|
#ifdef DEBUG
|
|
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
|
|
index bb5c8680..0d8c6cd 100644
|
|
--- a/js/src/vm/Runtime.cpp
|
|
+++ b/js/src/vm/Runtime.cpp
|
|
@@ -195,6 +195,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThrea
|
|
gcShouldCleanUpEverything(false),
|
|
gcGrayBitsValid(false),
|
|
gcIsNeeded(0),
|
|
+ numActiveZoneIters(0),
|
|
gcStats(thisFromCtor()),
|
|
gcNumber(0),
|
|
gcStartNumber(0),
|
|
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
|
|
index 5aeb924..ba4180e 100644
|
|
--- a/js/src/vm/Runtime.h
|
|
+++ b/js/src/vm/Runtime.h
|
|
@@ -1061,6 +1061,9 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|
*/
|
|
volatile uintptr_t gcIsNeeded;
|
|
|
|
+ mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
|
|
+ friend class js::gc::AutoEnterIteration;
|
|
+
|
|
js::gcstats::Statistics gcStats;
|
|
|
|
/* Incremented on every GC slice. */
|
|
--
|
|
2.2.1
|
|
|