Bug 1259850 - Make ZoneCellIter variants to communicate nogc to the hazard analysis, r=jonco

Also create accessors on gc::Zone for a nicer API to ZoneCellIters.

MozReview-Commit-ID: AxRTTUfWrND

--HG--
extra : rebase_source : cf32560db5a123bdc64178a7567ed36e1ecb5b2b
This commit is contained in:
Steve Fink 2016-05-27 22:00:10 -07:00
parent 69331f349a
commit 96d0fa6dcf
16 changed files with 341 additions and 140 deletions

View File

@ -95,21 +95,19 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment,
void* data, IterateScriptCallback scriptCallback)
{
MOZ_ASSERT(!rt->mainThread.suppressGC);
rt->gc.evictNursery();
MOZ_ASSERT(rt->gc.nursery.isEmpty());
AutoEmptyNursery empty(rt);
AutoPrepareForTracing prep(rt, SkipAtoms);
if (compartment) {
for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
Zone* zone = compartment->zone();
for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next()) {
if (script->compartment() == compartment)
scriptCallback(rt, data, script);
}
} else {
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next())
scriptCallback(rt, data, i.get<JSScript>());
for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next())
scriptCallback(rt, data, script);
}
}
}
@ -117,14 +115,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment,
void
js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
{
zone->runtimeFromMainThread()->gc.evictNursery();
AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms);
JSRuntime* rt = zone->runtimeFromMainThread();
AutoEmptyNursery empty(rt);
AutoPrepareForTracing prep(rt, SkipAtoms);
for (auto thingKind : ObjectAllocKinds()) {
for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
JSObject* obj = i.get<JSObject>();
for (auto obj = zone->cellIter<JSObject>(thingKind, empty); !obj.done(); obj.next()) {
if (obj->asTenured().isMarked(GRAY))
cellCallback(data, JS::GCCellPtr(obj));
cellCallback(data, JS::GCCellPtr(obj.get()));
}
}
}

View File

@ -761,7 +761,7 @@ Statistics::Statistics(JSRuntime* rt)
maxPauseInInterval(0),
phaseNestingDepth(0),
activeDagSlot(PHASE_DAG_NONE),
suspendedPhaseNestingDepth(0),
suspended(0),
sliceCallback(nullptr),
nurseryCollectionCallback(nullptr),
aborted(false)
@ -1069,7 +1069,7 @@ Statistics::startTimingMutator()
return false;
}
MOZ_ASSERT(suspendedPhaseNestingDepth == 0);
MOZ_ASSERT(suspended == 0);
timedGCTime = 0;
phaseStartTimes[PHASE_MUTATOR] = 0;
@ -1094,6 +1094,35 @@ Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms)
return true;
}
void
Statistics::suspendPhases(Phase suspension)
{
MOZ_ASSERT(suspension == PHASE_EXPLICIT_SUSPENSION || suspension == PHASE_IMPLICIT_SUSPENSION);
while (phaseNestingDepth) {
MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases));
Phase parent = phaseNesting[phaseNestingDepth - 1];
suspendedPhases[suspended++] = parent;
recordPhaseEnd(parent);
}
suspendedPhases[suspended++] = suspension;
}
void
Statistics::resumePhases()
{
Phase popped = suspendedPhases[--suspended];
MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION);
while (suspended &&
suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION &&
suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION)
{
Phase resumePhase = suspendedPhases[--suspended];
if (resumePhase == PHASE_MUTATOR)
timedGCTime += PRMJ_Now() - timedGCStart;
beginPhase(resumePhase);
}
}
void
Statistics::beginPhase(Phase phase)
{
@ -1105,9 +1134,7 @@ Statistics::beginPhase(Phase phase)
//
// Reuse this mechanism for managing PHASE_MUTATOR.
if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) {
MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases));
suspendedPhases[suspendedPhaseNestingDepth++] = parent;
recordPhaseEnd(parent);
suspendPhases(PHASE_IMPLICIT_SUSPENSION);
parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
}
@ -1154,12 +1181,8 @@ Statistics::endPhase(Phase phase)
// When emptying the stack, we may need to resume a callback phase
// (PHASE_GC_BEGIN/END) or return to timing the mutator (PHASE_MUTATOR).
if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0) {
Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth];
if (resumePhase == PHASE_MUTATOR)
timedGCTime += PRMJ_Now() - timedGCStart;
beginPhase(resumePhase);
}
if (phaseNestingDepth == 0 && suspended > 0 && suspendedPhases[suspended - 1] == PHASE_IMPLICIT_SUSPENSION)
resumePhases();
}
void

View File

@ -86,6 +86,8 @@ enum Phase : uint8_t {
PHASE_LIMIT,
PHASE_NONE = PHASE_LIMIT,
PHASE_EXPLICIT_SUSPENSION = PHASE_LIMIT,
PHASE_IMPLICIT_SUSPENSION,
PHASE_MULTI_PARENTS
};
@ -169,6 +171,22 @@ struct Statistics
void endPhase(Phase phase);
void endParallelPhase(Phase phase, const GCParallelTask* task);
// Occasionally, we may be in the middle of something that is tracked by
// this class, and we need to do something unusual (eg evict the nursery)
// that doesn't normally nest within the current phase. Suspend the
// currently tracked phase stack, at which time the caller is free to do
// other tracked operations.
//
// This also happens internally with PHASE_GC_BEGIN and other "non-GC"
// phases. While in these phases, any beginPhase will automatically suspend
// the non-GC phase, until that inner stack is complete, at which time it
// will automatically resume the non-GC phase. Explicit suspensions do not
// get auto-resumed.
void suspendPhases(Phase suspension = PHASE_EXPLICIT_SUSPENSION);
// Resume a suspended stack of phases.
void resumePhases();
void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
SliceBudget budget, JS::gcreason::Reason reason);
void endSlice();
@ -318,13 +336,14 @@ struct Statistics
size_t activeDagSlot;
/*
* To avoid recursive nesting, we discontinue a callback phase when any
* other phases are started. Remember what phase to resume when the inner
* phases are complete. (And because GCs can nest within the callbacks any
* number of times, we need a whole stack of of phases to resume.)
* Certain phases can interrupt the phase stack, eg callback phases. When
* this happens, we move the suspended phases over to a sepearate list,
* terminated by a dummy PHASE_SUSPENSION phase (so that we can nest
* suspensions by suspending multiple stacks with a PHASE_SUSPENSION in
* between).
*/
Phase suspendedPhases[MAX_NESTING];
size_t suspendedPhaseNestingDepth;
Phase suspendedPhases[MAX_NESTING * 3];
size_t suspended;
/* Sweep times for SCCs of compartments. */
Vector<int64_t, 0, SystemAllocPolicy> sccTimes;

View File

@ -153,13 +153,13 @@ Zone::sweepBreakpoints(FreeOp* fop)
*/
MOZ_ASSERT(isGCSweepingOrCompacting());
for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto iter = cellIter<JSScript>(); !iter.done(); iter.next()) {
JSScript* script = iter;
if (!script->hasAnyBreakpointsOrStepMode())
continue;
bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
MOZ_ASSERT(script == i.get<JSScript>());
MOZ_ASSERT(script == iter);
for (unsigned i = 0; i < script->length(); i++) {
BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
if (!site)
@ -207,10 +207,8 @@ Zone::discardJitCode(FreeOp* fop)
#ifdef DEBUG
/* Assert no baseline scripts are marked as active. */
for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = cellIter<JSScript>(); !script.done(); script.next())
MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
}
#endif
/* Mark baseline scripts on the stack as active. */
@ -219,8 +217,7 @@ Zone::discardJitCode(FreeOp* fop)
/* Only mark OSI points if code is being discarded. */
jit::InvalidateAll(fop, this);
for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
jit::FinishInvalidation(fop, script);
/*

View File

@ -84,6 +84,9 @@ using UniqueIdMap = GCHashMap<Cell*,
extern uint64_t NextCellUniqueId(JSRuntime* rt);
template <typename T>
class ZoneCellIter;
} // namespace gc
} // namespace js
@ -157,6 +160,13 @@ struct Zone : public JS::shadow::Zone,
onTooMuchMalloc();
}
// Iterate over all cells in the zone. See the definition of ZoneCellIter
// in jsgcinlines.h for the possible arguments and documentation.
template <typename T, typename... Args>
js::gc::ZoneCellIter<T> cellIter(Args... args) {
return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), mozilla::Forward<Args>(args)...);
}
bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
void onTooMuchMalloc();

View File

@ -488,6 +488,7 @@ BaselineScript::Trace(JSTracer* trc, BaselineScript* script)
void
BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
{
MOZ_ASSERT(!script->hasPendingIonBuilder());
script->unlinkDependentWasmModules(fop);
@ -1171,8 +1172,7 @@ jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable)
return;
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (!script->hasBaselineScript())
continue;
AutoWritableJitCode awjc(script->baselineScript()->method());
@ -1186,8 +1186,7 @@ void
jit::ToggleBaselineTraceLoggerScripts(JSRuntime* runtime, bool enable)
{
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (!script->hasBaselineScript())
continue;
script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable);
@ -1199,8 +1198,7 @@ void
jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable)
{
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (!script->hasBaselineScript())
continue;
script->baselineScript()->toggleTraceLoggerEngine(enable);

View File

@ -11,6 +11,7 @@
#include "mozilla/ThreadLocal.h"
#include "jscompartment.h"
#include "jsgc.h"
#include "jsprf.h"
#include "gc/Marking.h"
@ -589,8 +590,8 @@ JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock)
{
MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) {
JitCode* code = i.get<JitCode>();
for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
JitCode* code = i;
TraceRoot(trc, &code, "wrapper");
}
}
@ -1351,8 +1352,7 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs)
if (!rt->hasJitRuntime())
return;
for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (script->hasIonScript())
script->ionScript()->toggleBarriers(needs);
if (script->hasBaselineScript())

View File

@ -984,8 +984,8 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A
// want to delazify in that case: this pointer is weak so the JSScript
// could be destroyed at the next GC.
for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) {
JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
JSFunction* fun = &i->as<JSFunction>();
// Sweeping is incremental; take care to not delazify functions that
// are about to be finalized. GC things referenced by objects that are
@ -1157,8 +1157,7 @@ JSCompartment::clearScriptCounts()
void
JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
{
for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
script->clearBreakpointsIn(fop, dbg, handler);
}

View File

@ -2381,15 +2381,10 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone)
AutoClearTypeInferenceStateOnOOM oom(zone);
for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
script->maybeSweepTypes(&oom);
}
for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
ObjectGroup* group = i.get<ObjectGroup>();
for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next())
group->maybeSweep(&oom);
}
zone->types.endSweep(rt);
}
@ -3981,10 +3976,11 @@ GCRuntime::checkForCompartmentMismatches()
return;
CompartmentCheckTracer trc(rt);
AutoAssertEmptyNursery empty(rt);
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
trc.zone = zone;
for (auto thingKind : AllAllocKinds()) {
for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
for (auto i = zone->cellIter<TenuredCell>(thingKind, empty); !i.done(); i.next()) {
trc.src = i.getCell();
trc.srcKind = MapAllocToTraceKind(thingKind);
trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
@ -4003,9 +3999,10 @@ RelazifyFunctions(Zone* zone, AllocKind kind)
kind == AllocKind::FUNCTION_EXTENDED);
JSRuntime* rt = zone->runtimeFromMainThread();
AutoAssertEmptyNursery empty(rt);
for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) {
JSFunction* fun = &i.get<JSObject>()->as<JSFunction>();
for (auto i = zone->cellIter<JSObject>(kind, empty); !i.done(); i.next()) {
JSFunction* fun = &i->as<JSFunction>();
if (fun->hasScript())
fun->maybeRelazify(rt);
}
@ -6919,8 +6916,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
RootedObject targetStaticGlobalLexicalScope(rt);
targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock();
for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) {
JSScript* script = iter.get<JSScript>();
for (auto script = source->zone()->cellIter<JSScript>(); !script.done(); script.next()) {
MOZ_ASSERT(script->compartment() == source);
script->compartment_ = target;
script->setTypesGeneration(target->zone()->types.generation);
@ -6952,14 +6948,12 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
}
}
for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) {
BaseShape* base = iter.get<BaseShape>();
for (auto base = source->zone()->cellIter<BaseShape>(); !base.done(); base.next()) {
MOZ_ASSERT(base->compartment() == source);
base->compartment_ = target;
}
for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) {
ObjectGroup* group = iter.get<ObjectGroup>();
for (auto group = source->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
group->setGeneration(target->zone()->types.generation);
group->compartment_ = target;
@ -6981,8 +6975,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
// After fixing JSFunctions' compartments, we can fix LazyScripts'
// enclosing scopes.
for (ZoneCellIter iter(source->zone(), AllocKind::LAZY_SCRIPT); !iter.done(); iter.next()) {
LazyScript* lazy = iter.get<LazyScript>();
for (auto lazy = source->zone()->cellIter<LazyScript>(); !lazy.done(); lazy.next()) {
MOZ_ASSERT(lazy->functionNonDelazifying()->compartment() == target);
// See warning in handleParseWorkload. If we start optimizing global
@ -7116,12 +7109,9 @@ js::ReleaseAllJITCode(FreeOp* fop)
void
js::PurgeJITCaches(Zone* zone)
{
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
/* Discard Ion caches. */
/* Discard Ion caches. */
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
jit::PurgeCaches(script);
}
}
void
@ -7391,8 +7381,7 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
zone->checkUniqueIdTableAfterMovingGC();
for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) {
BaseShape* baseShape = i.get<BaseShape>();
for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next()) {
if (baseShape->hasTable())
baseShape->table().checkAfterMovingGC();
}
@ -7870,5 +7859,28 @@ StateName(State state)
return names[state];
}
void
AutoAssertHeapBusy::checkCondition(JSRuntime *rt)
{
this->rt = rt;
MOZ_ASSERT(rt->isHeapBusy());
}
void
AutoAssertEmptyNursery::checkCondition(JSRuntime *rt) {
this->rt = rt;
MOZ_ASSERT(rt->gc.nursery.isEmpty());
}
AutoEmptyNursery::AutoEmptyNursery(JSRuntime *rt)
: AutoAssertEmptyNursery()
{
MOZ_ASSERT(!rt->mainThread.suppressGC);
rt->gc.stats.suspendPhases();
rt->gc.evictNursery();
rt->gc.stats.resumePhases();
checkCondition(rt);
}
} /* namespace gc */
} /* namespace js */

View File

@ -1328,6 +1328,95 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc
#endif
};
/*
* There are a couple of classes here that serve mostly as "tokens" indicating
* that a condition holds. Some functions force the caller to possess such a
* token because they would misbehave if the condition were false, and it is
* far more clear to make the condition visible at the point where it can be
* affected rather than just crashing in an assertion down in the place where
* it is relied upon.
*/
/*
* Token meaning that the heap is busy and no allocations will be made.
*
* This class may be instantiated directly if it is known that the condition is
* already true, or it can be used as a base class for another RAII class that
* causes the condition to become true. Such base classes will use the no-arg
* constructor, establish the condition, then call checkCondition() to assert
* it and possibly record data needed to re-check the condition during
* destruction.
*
* Ordinarily, you would do something like this with a Maybe<> member that is
* emplaced during the constructor, but token-requiring functions want to
* require a reference to a base class instance. That said, you can always pass
* in the Maybe<> field as the token.
*/
class MOZ_RAII AutoAssertHeapBusy {
protected:
JSRuntime* rt;
// Check that the heap really is busy, and record the rt for the check in
// the destructor.
void checkCondition(JSRuntime *rt);
AutoAssertHeapBusy() : rt(nullptr) {
}
public:
explicit AutoAssertHeapBusy(JSRuntime* rt) {
checkCondition(rt);
}
~AutoAssertHeapBusy() {
MOZ_ASSERT(rt); // checkCondition must always be called.
checkCondition(rt);
}
};
/*
* A class that serves as a token that the nursery is empty. It descends from
* AutoAssertHeapBusy, which means that it additionally requires the heap to be
* busy (which is not necessarily linked, but turns out to be true in practice
* for all users and simplifies the usage of these classes.)
*/
class MOZ_RAII AutoAssertEmptyNursery
{
protected:
JSRuntime* rt;
// Check that the nursery is empty.
void checkCondition(JSRuntime *rt);
// For subclasses that need to empty the nursery in their constructors.
AutoAssertEmptyNursery() : rt(nullptr) {
}
public:
explicit AutoAssertEmptyNursery(JSRuntime* rt) {
checkCondition(rt);
}
~AutoAssertEmptyNursery() {
checkCondition(rt);
}
};
/*
* Evict the nursery upon construction. Serves as a token indicating that the
* nursery is empty. (See AutoAssertEmptyNursery, above.)
*
* Note that this is very improper subclass of AutoAssertHeapBusy, in that the
* heap is *not* busy within the scope of an AutoEmptyNursery. I will most
* likely fix this by removing AutoAssertHeapBusy, but that is currently
* waiting on jonco's review.
*/
class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery
{
public:
explicit AutoEmptyNursery(JSRuntime *rt);
};
const char*
StateName(State state);

View File

@ -195,39 +195,71 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {}
};
class ZoneCellIterImpl
{
template <typename T>
class ZoneCellIter;
template <>
class ZoneCellIter<TenuredCell> {
ArenaIter arenaIter;
ArenaCellIterImpl cellIter;
JS::AutoAssertNoAlloc noAlloc;
public:
ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) {
protected:
// For use when a subclass wants to insert some setup before init().
ZoneCellIter() {}
void init(JS::Zone* zone, AllocKind kind) {
JSRuntime* rt = zone->runtimeFromAnyThread();
MOZ_ASSERT(zone);
MOZ_ASSERT_IF(IsNurseryAllocable(kind), rt->gc.nursery.isEmpty());
// If called from outside a GC, ensure that the heap is in a state
// that allows us to iterate.
if (!rt->isHeapBusy()) {
// Assert that no GCs can occur while a ZoneCellIter is live.
noAlloc.disallowAlloc(rt);
}
// We have a single-threaded runtime, so there's no need to protect
// against other threads iterating or allocating. However, we do have
// background finalization; we may have to wait for this to finish if
// it's currently active.
if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind))
rt->gc.waitBackgroundSweepEnd();
arenaIter.init(zone, kind);
if (!arenaIter.done())
cellIter.init(arenaIter.get());
}
public:
ZoneCellIter(JS::Zone* zone, AllocKind kind) {
// If we are iterating a nursery-allocated kind then we need to
// evict first so that we can see all things.
if (IsNurseryAllocable(kind)) {
JSRuntime* rt = zone->runtimeFromMainThread();
rt->gc.evictNursery();
}
init(zone, kind);
}
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) {
// No need to evict the nursery. (This constructor is known statically
// to not GC.)
init(zone, kind);
}
bool done() const {
return arenaIter.done();
}
template<typename T> T* get() const {
template<typename T>
T* get() const {
MOZ_ASSERT(!done());
return cellIter.get<T>();
}
Cell* getCell() const {
TenuredCell* getCell() const {
MOZ_ASSERT(!done());
return cellIter.getCell();
}
@ -244,44 +276,74 @@ class ZoneCellIterImpl
}
};
class ZoneCellIterUnderGC : public ZoneCellIterImpl
{
// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is
// known, for a single AllocKind. Example usages:
//
// for (auto obj = zone->cellIter<JSObject>(AllocKind::OBJECT0); !obj.done(); obj.next())
// ...
//
// for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
// f(script->code());
//
// As this code demonstrates, you can use 'script' as if it were a JSScript*.
// Its actual type is ZoneCellIter<JSScript>, but for most purposes it will
// autoconvert to JSScript*.
//
// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind
// from the type 'JSScript', whereas in the JSObject case, the kind must be
// given (because there are multiple AllocKinds for objects).
//
// Also, the static rooting hazard analysis knows that the JSScript case will
// not GC during construction. The JSObject case needs to GC, or more precisely
// to empty the nursery and clear out the store buffer, so that it can see all
// objects to iterate over (the nursery is not iterable) and remove the
// possibility of having pointers from the store buffer to data hanging off
// stuff we're iterating over that we are going to delete. (The latter should
// not be a problem, since such instances should be using RelocatablePtr do
// remove themselves from the store buffer on deletion, but currently for
// subtle reasons that isn't good enough.)
//
// If the iterator is used within a GC, then there is no need to evict the
// nursery (again). You may select a variant that will skip the eviction either
// by specializing on a GCType that is never allocated in the nursery, or
// explicitly by passing in a trailing AutoAssertEmptyNursery argument.
//
template <typename GCType>
class ZoneCellIter : public ZoneCellIter<TenuredCell> {
public:
ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind)
: ZoneCellIterImpl(zone, kind)
// Non-nursery allocated (equivalent to having an entry in
// MapTypeToFinalizeKind). The template declaration here is to discard this
// constructor overload if MapTypeToFinalizeKind<GCType>::kind does not
// exist. Note that there will be no remaining overloads that will work,
// which makes sense given that you haven't specified which of the
// AllocKinds to use for GCType.
//
// If we later add a nursery allocable GCType with a single AllocKind, we
// will want to add an overload of this constructor that does the right
// thing (ie, it empties the nursery before iterating.)
explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter<TenuredCell>() {
init(zone, MapTypeToFinalizeKind<GCType>::kind);
}
// Non-nursery allocated, nursery is known to be empty: same behavior as above.
ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) {
}
// Arbitrary kind, which will be assumed to be nursery allocable (and
// therefore the nursery will be emptied before iterating.)
ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<TenuredCell>(zone, kind) {
}
// Arbitrary kind, which will be assumed to be nursery allocable, but the
// nursery is known to be empty already: same behavior as non-nursery types.
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty)
: ZoneCellIter<TenuredCell>(zone, kind, empty)
{
MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
}
};
class ZoneCellIter
{
mozilla::Maybe<ZoneCellIterImpl> impl;
JS::AutoAssertNoAlloc noAlloc;
public:
ZoneCellIter(JS::Zone* zone, AllocKind kind) {
// If called from outside a GC, ensure that the heap is in a state
// that allows us to iterate.
JSRuntime* rt = zone->runtimeFromMainThread();
if (!rt->isHeapBusy()) {
// If we are iterating a nursery-allocated kind then we need to
// evict first so that we can see all things.
if (IsNurseryAllocable(kind))
rt->gc.evictNursery();
// Assert that no GCs can occur while a ZoneCellIter is live.
noAlloc.disallowAlloc(rt);
}
impl.emplace(zone, kind);
}
bool done() const { return impl->done(); }
template<typename T>
T* get() const { return impl->get<T>(); }
Cell* getCell() const { return impl->getCell(); }
void next() { impl->next(); }
GCType* get() const { return ZoneCellIter<TenuredCell>::get<GCType>(); }
operator GCType*() const { return get(); }
GCType* operator ->() const { return get(); }
};
class GCZonesIter

View File

@ -201,8 +201,9 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
void
js::DumpCompartmentPCCounts(JSContext* cx)
{
for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) {
RootedScript script(cx, i.get<JSScript>());
RootedScript script(cx);
for (auto iter = cx->zone()->cellIter<JSScript>(); !iter.done(); iter.next()) {
script = iter;
if (script->compartment() != cx->compartment())
continue;
@ -1657,8 +1658,7 @@ js::StopPCCountProfiling(JSContext* cx)
return;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (script->hasScriptCounts() && script->types()) {
if (!vec->append(script))
return;
@ -2025,8 +2025,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
}
Rooted<ScriptVector> topScripts(cx, ScriptVector(cx));
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get<JSScript>();
for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
if (script->compartment() != comp ||
!script->isTopLevel() ||
!script->filename())

View File

@ -2352,8 +2352,8 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
return false;
}
} else {
for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) {
JSScript* script = iter.get<JSScript>();
for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
JSScript* script = iter;
if (obs.shouldRecompileOrInvalidate(script) &&
!gc::IsAboutToBeFinalizedUnbarriered(&script))
{

View File

@ -1271,8 +1271,6 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
LeaveParseTaskZone(rt, parseTask);
{
gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);
// Generator functions don't have Function.prototype as prototype but a
// different function object, so the IdentifyStandardPrototype trick
// below won't work. Just special-case it.
@ -1288,8 +1286,7 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
// to the corresponding prototype in the new compartment. This will briefly
// create cross compartment pointers, which will be fixed by the
// MergeCompartments call below.
for (; !iter.done(); iter.next()) {
ObjectGroup* group = iter.get<ObjectGroup>();
for (auto group = parseTask->cx->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
TaggedProto proto(group->proto());
if (!proto.isObject())
continue;

View File

@ -1049,7 +1049,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t
}
static bool
UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, const Value& value)
UpdateShapeTypeAndValue(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value)
{
jsid id = shape->propid();
if (shape->hasSlot()) {

View File

@ -2545,16 +2545,15 @@ js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force)
if (!force && !InferSpewActive(ISpewResult))
return;
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
RootedScript script(cx, i.get<JSScript>());
RootedScript script(cx);
for (auto iter = zone->cellIter<JSScript>(); !iter.done(); iter.next()) {
script = iter;
if (script->types())
script->types()->printTypes(cx, script);
}
for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) {
ObjectGroup* group = i.get<ObjectGroup>();
for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next())
group->print();
}
#endif
}
@ -4425,10 +4424,8 @@ TypeZone::endSweep(JSRuntime* rt)
void
TypeZone::clearAllNewScriptsOnOOM()
{
for (gc::ZoneCellIter iter(zone(), gc::AllocKind::OBJECT_GROUP);
!iter.done(); iter.next())
{
ObjectGroup* group = iter.get<ObjectGroup>();
for (auto iter = zone()->cellIter<ObjectGroup>(); !iter.done(); iter.next()) {
ObjectGroup* group = iter;
if (!IsAboutToBeFinalizedUnbarriered(&group))
group->maybeClearNewScriptOnOOM();
}
@ -4437,8 +4434,9 @@ TypeZone::clearAllNewScriptsOnOOM()
AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()
{
if (oom) {
JSRuntime* rt = zone->runtimeFromMainThread();
zone->setPreservingCode(false);
zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp());
zone->discardJitCode(rt->defaultFreeOp());
zone->types.clearAllNewScriptsOnOOM();
}
}