Bug 1570590 : trace weak references in tracing. r=jonco

Introduces SweepingTracer and TraceWeakEdge to trace weak references in
AtomsTable::sweep and AtomsTable::sweepIncrementally while sweeping.
Also rename those two functions to traceWeak and traceWeakIncrementally.

Differential Revision: https://phabricator.services.mozilla.com/D40939

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Yoshi Cheng-Hao Huang 2019-08-08 12:27:50 +00:00
Родитель 2747e35c51
Коммит a29edd5706
7 изменённых файлов: 98 добавлений и 18 удалений

Просмотреть файл

@ -252,7 +252,8 @@ class JS_PUBLIC_API CallbackTracer : public JSTracer {
GrayBuffering,
VerifyTraceProtoAndIface,
ClearEdges,
UnmarkGray
UnmarkGray,
Sweeping
};
virtual TracerKind getTracerKind() const { return TracerKind::Unspecified; }
#endif

Просмотреть файл

@ -6180,7 +6180,8 @@ void GCRuntime::startSweepingAtomsTable() {
// Create secondary tables to hold new atoms added while we're sweeping the
// main tables incrementally.
if (!atomsTable->startIncrementalSweep()) {
atomsTable->sweepAll(rt);
SweepingTracer trc(rt);
atomsTable->traceWeak(&trc);
return;
}
@ -6201,7 +6202,9 @@ IncrementalProgress GCRuntime::sweepAtomsTable(FreeOp* fop,
return Finished;
}
if (!rt->atomsForSweeping()->sweepIncrementally(maybeAtoms.ref(), budget)) {
SweepingTracer trc(rt);
if (!rt->atomsForSweeping()->traceWeakIncrementally(&trc, maybeAtoms.ref(),
budget)) {
return NotFinished;
}
@ -6367,9 +6370,8 @@ IncrementalProgress GCRuntime::sweepShapeTree(FreeOp* fop,
return NotFinished;
}
if (!SweepArenaList<AccessorShape>(fop,
&al.gcAccessorShapeArenasToUpdate.ref(),
budget)) {
if (!SweepArenaList<AccessorShape>(
fop, &al.gcAccessorShapeArenasToUpdate.ref(), budget)) {
return NotFinished;
}

Просмотреть файл

@ -176,6 +176,33 @@ struct MovingTracer final : public JS::CallbackTracer {
bool updateEdge(T** thingp);
};
struct SweepingTracer final : public JS::CallbackTracer {
explicit SweepingTracer(JSRuntime* rt)
: CallbackTracer(rt, TraceWeakMapKeysValues) {}
bool onObjectEdge(JSObject** objp) override;
bool onShapeEdge(Shape** shapep) override;
bool onStringEdge(JSString** stringp) override;
bool onScriptEdge(JSScript** scriptp) override;
bool onLazyScriptEdge(LazyScript** lazyp) override;
bool onBaseShapeEdge(BaseShape** basep) override;
bool onScopeEdge(Scope** scopep) override;
bool onRegExpSharedEdge(RegExpShared** sharedp) override;
bool onBigIntEdge(BigInt** bip) override;
bool onChild(const JS::GCCellPtr& thing) override {
MOZ_CRASH("unexpected edge.");
return true;
}
#ifdef DEBUG
TracerKind getTracerKind() const override { return TracerKind::Sweeping; }
#endif
private:
template <typename T>
bool sweepEdge(T** thingp);
};
// Structure for counting how many times objects in a particular group have
// been tenured during a minor collection.
struct TenureCount {

Просмотреть файл

@ -246,7 +246,8 @@ void js::CheckTracedThing(JSTracer* trc, T* thing) {
isGcMarkingTracer ||
IsTracerKind(trc, JS::CallbackTracer::TracerKind::GrayBuffering) ||
IsTracerKind(trc, JS::CallbackTracer::TracerKind::UnmarkGray) ||
IsTracerKind(trc, JS::CallbackTracer::TracerKind::ClearEdges));
IsTracerKind(trc, JS::CallbackTracer::TracerKind::ClearEdges) ||
IsTracerKind(trc, JS::CallbackTracer::TracerKind::Sweeping));
if (isGcMarkingTracer) {
GCMarker* gcMarker = GCMarker::fromTracer(trc);
@ -3434,6 +3435,47 @@ bool js::gc::IsAboutToBeFinalizedInternal(T* thingp) {
return dying;
}
template <typename T>
inline bool SweepingTracer::sweepEdge(T** thingp) {
CheckIsMarkedThing(thingp);
T* thing = *thingp;
JSRuntime* rt = thing->runtimeFromAnyThread();
if (ThingIsPermanentAtomOrWellKnownSymbol(thing) && runtime() != rt) {
return true;
}
TenuredCell& tenured = thing->asTenured();
MOZ_ASSERT(tenured.zoneFromAnyThread()->isGCSweeping(),
"Should be called during Sweeping.");
if (!tenured.isMarkedAny()) {
*thingp = nullptr;
return false;
}
return true;
}
bool SweepingTracer::onObjectEdge(JSObject** objp) { return sweepEdge(objp); }
bool SweepingTracer::onShapeEdge(Shape** shapep) { return sweepEdge(shapep); }
bool SweepingTracer::onStringEdge(JSString** stringp) {
return sweepEdge(stringp);
}
bool SweepingTracer::onScriptEdge(JSScript** scriptp) {
return sweepEdge(scriptp);
}
bool SweepingTracer::onLazyScriptEdge(LazyScript** lazyp) {
return sweepEdge(lazyp);
}
bool SweepingTracer::onBaseShapeEdge(BaseShape** basep) {
return sweepEdge(basep);
}
bool SweepingTracer::onScopeEdge(Scope** scopep) { return sweepEdge(scopep); }
bool SweepingTracer::onRegExpSharedEdge(RegExpShared** sharedp) {
return sweepEdge(sharedp);
}
bool SweepingTracer::onBigIntEdge(BigInt** bip) { return sweepEdge(bip); }
namespace js {
namespace gc {

Просмотреть файл

@ -111,8 +111,8 @@ inline void AssertRootMarkingPhase(JSTracer* trc) {}
// Note that weak edges are handled separately. GC things with weak edges must
// not trace those edges during marking tracing (which would keep the referent
// alive) but instead arrange for the edge to be swept by calling
// js::gc::IsAboutToBeFinalized during sweeping. For example, see the treatment
// of the script_ edge in LazyScript::traceChildren and
// js::gc::IsAboutToBeFinalized or TraceWeakEdge during sweeping. For example,
// see the treatment of the script_ edge in LazyScript::traceChildren and
// js::gc::SweepLazyScripts.
//
// GC things that are weakly held in containers can use WeakMap or a container
@ -191,6 +191,14 @@ inline void TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp,
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
}
// Trace through a weak edge. If *thingp is not marked at the end of marking,
// it is replaced by nullptr, and this method will return false to indicate that
// the edge no longer exists.
template <typename T>
inline bool TraceWeakEdge(JSTracer* trc, T* thingp, const char* name) {
return gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
}
// Trace all edges contained in the given array.
template <typename T>

Просмотреть файл

@ -188,12 +188,13 @@ class AtomsTable {
void tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access);
// Sweep all atoms non-incrementally.
void sweepAll(JSRuntime* rt);
void traceWeak(JSTracer* trc);
bool startIncrementalSweep();
// Sweep some atoms incrementally and return whether we finished.
bool sweepIncrementally(SweepIterator& atomsToSweep, SliceBudget& budget);
bool traceWeakIncrementally(JSTracer* trc, SweepIterator& atomsToSweep,
SliceBudget& budget);
#ifdef DEBUG
bool mainThreadHasAllLocks() const { return allPartitionsLocked; }

Просмотреть файл

@ -476,15 +476,15 @@ void js::TraceWellKnownSymbols(JSTracer* trc) {
}
}
void AtomsTable::sweepAll(JSRuntime* rt) {
void AtomsTable::traceWeak(JSTracer* trc) {
JSRuntime* rt = trc->runtime();
for (size_t i = 0; i < PartitionCount; i++) {
AutoLock lock(rt, partitions[i]->lock);
AtomSet& atoms = partitions[i]->atoms;
for (AtomSet::Enum e(atoms); !e.empty(); e.popFront()) {
JSAtom* atom = e.front().asPtrUnbarriered();
MOZ_DIAGNOSTIC_ASSERT(atom);
if (IsAboutToBeFinalizedUnbarriered(&atom)) {
MOZ_ASSERT(!atom->isPinned());
if (!TraceWeakEdge(trc, &atom, "AtomsTable::partitions::atoms")) {
e.removeFront();
} else {
MOZ_ASSERT(atom == e.front().asPtrUnbarriered());
@ -589,8 +589,8 @@ void AtomsTable::mergeAtomsAddedWhileSweeping(Partition& part) {
js_delete(newAtoms);
}
bool AtomsTable::sweepIncrementally(SweepIterator& atomsToSweep,
SliceBudget& budget) {
bool AtomsTable::traceWeakIncrementally(JSTracer* trc, SweepIterator& atomsToSweep,
SliceBudget& budget) {
// Sweep the table incrementally until we run out of work or budget.
while (!atomsToSweep.empty()) {
budget.step();
@ -600,8 +600,7 @@ bool AtomsTable::sweepIncrementally(SweepIterator& atomsToSweep,
JSAtom* atom = atomsToSweep.front();
MOZ_DIAGNOSTIC_ASSERT(atom);
if (IsAboutToBeFinalizedUnbarriered(&atom)) {
MOZ_ASSERT(!atom->isPinned());
if (!TraceWeakEdge(trc, &atom, "maybeAtomsToSweep")) {
atomsToSweep.removeFront();
} else {
MOZ_ASSERT(atom == atomsToSweep.front());