зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1064578 - Sweep tables in parallel; r=jonco r=bhackett
--HG-- extra : rebase_source : f4e1d6c1067c2ae5c01f62dee3d4b28471c62e98
This commit is contained in:
Родитель
83ec10e366
Коммит
bec7c338fd
|
@ -697,6 +697,12 @@ class GCRuntime
|
|||
int sweepKindIndex;
|
||||
bool abortSweepAfterCurrentGroup;
|
||||
|
||||
/*
|
||||
* Concurrent sweep infrastructure.
|
||||
*/
|
||||
void startTask(GCParallelTask &task, gcstats::Phase phase);
|
||||
void joinTask(GCParallelTask &task, gcstats::Phase phase);
|
||||
|
||||
/*
|
||||
* List head of arenas allocated during the sweep phase.
|
||||
*/
|
||||
|
|
|
@ -471,6 +471,15 @@ IsMarked(T **thingp)
|
|||
template <typename T>
|
||||
static bool
|
||||
IsAboutToBeFinalized(T **thingp)
|
||||
{
|
||||
MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
|
||||
CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
|
||||
return IsAboutToBeFinalizedFromAnyThread(thingp);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool
|
||||
IsAboutToBeFinalizedFromAnyThread(T **thingp)
|
||||
{
|
||||
MOZ_ASSERT(thingp);
|
||||
MOZ_ASSERT(*thingp);
|
||||
|
@ -503,7 +512,7 @@ IsAboutToBeFinalized(T **thingp)
|
|||
}
|
||||
#endif // JSGC_GENERATIONAL
|
||||
|
||||
Zone *zone = thing->asTenured().zone();
|
||||
Zone *zone = thing->asTenured().zoneFromAnyThread();
|
||||
if (zone->isGCSweeping()) {
|
||||
/*
|
||||
* We should return false for things that have been allocated during
|
||||
|
@ -616,6 +625,12 @@ Is##base##AboutToBeFinalized(type **thingp)
|
|||
} \
|
||||
\
|
||||
bool \
|
||||
Is##base##AboutToBeFinalizedFromAnyThread(type **thingp) \
|
||||
{ \
|
||||
return IsAboutToBeFinalizedFromAnyThread<type>(thingp); \
|
||||
} \
|
||||
\
|
||||
bool \
|
||||
Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp) \
|
||||
{ \
|
||||
return IsAboutToBeFinalized<type>(thingp->unsafeGet()); \
|
||||
|
@ -910,6 +925,28 @@ gc::IsValueAboutToBeFinalized(Value *v)
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
gc::IsValueAboutToBeFinalizedFromAnyThread(Value *v)
|
||||
{
|
||||
MOZ_ASSERT(v->isMarkable());
|
||||
bool rv;
|
||||
if (v->isString()) {
|
||||
JSString *str = (JSString *)v->toGCThing();
|
||||
rv = IsAboutToBeFinalizedFromAnyThread<JSString>(&str);
|
||||
v->setString(str);
|
||||
} else if (v->isObject()) {
|
||||
JSObject *obj = (JSObject *)v->toGCThing();
|
||||
rv = IsAboutToBeFinalizedFromAnyThread<JSObject>(&obj);
|
||||
v->setObject(*obj);
|
||||
} else {
|
||||
MOZ_ASSERT(v->isSymbol());
|
||||
JS::Symbol *sym = v->toSymbol();
|
||||
rv = IsAboutToBeFinalizedFromAnyThread<JS::Symbol>(&sym);
|
||||
v->setSymbol(sym);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*** Slot Marking ***/
|
||||
|
||||
bool
|
||||
|
@ -1031,6 +1068,12 @@ gc::IsCellAboutToBeFinalized(Cell **thingp)
|
|||
return IsAboutToBeFinalized<Cell>(thingp);
|
||||
}
|
||||
|
||||
bool
|
||||
gc::IsCellAboutToBeFinalizedFromAnyThread(Cell **thingp)
|
||||
{
|
||||
return IsAboutToBeFinalizedFromAnyThread<Cell>(thingp);
|
||||
}
|
||||
|
||||
/*** Push Mark Stack ***/
|
||||
|
||||
#define JS_COMPARTMENT_ASSERT(rt, thing) \
|
||||
|
|
|
@ -98,6 +98,7 @@ void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *
|
|||
bool Is##base##Marked(type **thingp); \
|
||||
bool Is##base##Marked(BarrieredBase<type*> *thingp); \
|
||||
bool Is##base##AboutToBeFinalized(type **thingp); \
|
||||
bool Is##base##AboutToBeFinalizedFromAnyThread(type **thingp); \
|
||||
bool Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp); \
|
||||
type *Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp); \
|
||||
type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
|
||||
|
@ -219,6 +220,9 @@ IsValueMarked(Value *v);
|
|||
bool
|
||||
IsValueAboutToBeFinalized(Value *v);
|
||||
|
||||
bool
|
||||
IsValueAboutToBeFinalizedFromAnyThread(Value *v);
|
||||
|
||||
/*** Slot Marking ***/
|
||||
|
||||
bool
|
||||
|
@ -327,6 +331,9 @@ IsCellMarked(Cell **thingp);
|
|||
bool
|
||||
IsCellAboutToBeFinalized(Cell **thing);
|
||||
|
||||
bool
|
||||
IsCellAboutToBeFinalizedFromAnyThread(Cell **thing);
|
||||
|
||||
inline bool
|
||||
IsMarked(BarrieredBase<Value> *v)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "prmjtime.h"
|
||||
|
||||
#include "gc/Memory.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/Runtime.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -837,6 +838,16 @@ Statistics::endPhase(Phase phase)
|
|||
phaseStartTimes[phase] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Statistics::endParallelPhase(Phase phase, const GCParallelTask *task)
|
||||
{
|
||||
phaseNestingDepth--;
|
||||
|
||||
slices.back().phaseTimes[phase] += task->duration();
|
||||
phaseTimes[phase] += task->duration();
|
||||
phaseStartTimes[phase] = 0;
|
||||
}
|
||||
|
||||
int64_t
|
||||
Statistics::beginSCC()
|
||||
{
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
struct JSCompartment;
|
||||
|
||||
namespace js {
|
||||
|
||||
class GCParallelTask;
|
||||
|
||||
namespace gcstats {
|
||||
|
||||
enum Phase {
|
||||
|
@ -109,6 +112,7 @@ struct Statistics
|
|||
|
||||
void beginPhase(Phase phase);
|
||||
void endPhase(Phase phase);
|
||||
void endParallelPhase(Phase phase, const GCParallelTask *task);
|
||||
|
||||
void beginSlice(const ZoneGCStats &zoneStats, JS::gcreason::Reason reason);
|
||||
void endSlice();
|
||||
|
@ -234,25 +238,41 @@ struct AutoPhase
|
|||
{
|
||||
AutoPhase(Statistics &stats, Phase phase
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: stats(stats), phase(phase), enabled(true)
|
||||
: stats(stats), task(nullptr), phase(phase), enabled(true)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
stats.beginPhase(phase);
|
||||
}
|
||||
|
||||
AutoPhase(Statistics &stats, bool condition, Phase phase
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: stats(stats), phase(phase), enabled(condition)
|
||||
: stats(stats), task(nullptr), phase(phase), enabled(condition)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (enabled)
|
||||
stats.beginPhase(phase);
|
||||
}
|
||||
~AutoPhase() {
|
||||
|
||||
AutoPhase(Statistics &stats, const GCParallelTask &task, Phase phase
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: stats(stats), task(&task), phase(phase), enabled(true)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (enabled)
|
||||
stats.beginPhase(phase);
|
||||
}
|
||||
|
||||
~AutoPhase() {
|
||||
if (enabled) {
|
||||
if (task)
|
||||
stats.endParallelPhase(phase, task);
|
||||
else
|
||||
stats.endPhase(phase);
|
||||
}
|
||||
}
|
||||
|
||||
Statistics &stats;
|
||||
const GCParallelTask *task;
|
||||
Phase phase;
|
||||
bool enabled;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
|
|
@ -254,7 +254,7 @@ JSRuntime::sweepAtoms()
|
|||
for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
|
||||
AtomStateEntry entry = e.front();
|
||||
JSAtom *atom = entry.asPtr();
|
||||
bool isDying = IsStringAboutToBeFinalized(&atom);
|
||||
bool isDying = IsStringAboutToBeFinalizedFromAnyThread(&atom);
|
||||
|
||||
/* Pinned or interned key cannot be finalized. */
|
||||
MOZ_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
|
||||
|
|
|
@ -101,8 +101,9 @@ JSCompartment::sweepCallsiteClones()
|
|||
if (callsiteClones.initialized()) {
|
||||
for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) {
|
||||
CallsiteCloneKey key = e.front().key();
|
||||
if (IsObjectAboutToBeFinalized(&key.original) || IsScriptAboutToBeFinalized(&key.script) ||
|
||||
IsObjectAboutToBeFinalized(e.front().value().unsafeGet()))
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(&key.original) ||
|
||||
IsScriptAboutToBeFinalizedFromAnyThread(&key.script) ||
|
||||
IsObjectAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()))
|
||||
{
|
||||
e.removeFront();
|
||||
} else if (key != e.front().key()) {
|
||||
|
|
|
@ -535,14 +535,14 @@ JSCompartment::markRoots(JSTracer *trc)
|
|||
* If a compartment is on-stack, we mark its global so that
|
||||
* JSContext::global() remains valid.
|
||||
*/
|
||||
if (enterCompartmentDepth && global_)
|
||||
if (enterCompartmentDepth && global_.unbarrieredGet())
|
||||
MarkObjectRoot(trc, global_.unsafeGet(), "on-stack compartment global");
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepInnerViews()
|
||||
{
|
||||
innerViews.sweep(runtimeFromMainThread());
|
||||
innerViews.sweep(runtimeFromAnyThread());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -555,13 +555,13 @@ JSCompartment::sweepTypeObjectTables()
|
|||
void
|
||||
JSCompartment::sweepSavedStacks()
|
||||
{
|
||||
savedStacks_.sweep(runtimeFromMainThread());
|
||||
savedStacks_.sweep(runtimeFromAnyThread());
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepGlobalObject(FreeOp *fop)
|
||||
{
|
||||
if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) {
|
||||
if (global_.unbarrieredGet() && IsObjectAboutToBeFinalizedFromAnyThread(global_.unsafeGet())) {
|
||||
if (debugMode())
|
||||
Debugger::detachAllDebuggersFromGlobal(fop, global_);
|
||||
global_.set(nullptr);
|
||||
|
@ -571,8 +571,8 @@ JSCompartment::sweepGlobalObject(FreeOp *fop)
|
|||
void
|
||||
JSCompartment::sweepSelfHostingScriptSource()
|
||||
{
|
||||
if (selfHostingScriptSource &&
|
||||
IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet()))
|
||||
if (selfHostingScriptSource.unbarrieredGet() &&
|
||||
IsObjectAboutToBeFinalizedFromAnyThread((JSObject **) selfHostingScriptSource.unsafeGet()))
|
||||
{
|
||||
selfHostingScriptSource.set(nullptr);
|
||||
}
|
||||
|
@ -593,13 +593,13 @@ JSCompartment::sweepRegExps()
|
|||
* code for the lifetime of the JIT script. Thus, we must perform
|
||||
* sweeping after clearing jit code.
|
||||
*/
|
||||
regExps.sweep(runtimeFromMainThread());
|
||||
regExps.sweep(runtimeFromAnyThread());
|
||||
}
|
||||
|
||||
void
|
||||
JSCompartment::sweepDebugScopes()
|
||||
{
|
||||
JSRuntime *rt = runtimeFromMainThread();
|
||||
JSRuntime *rt = runtimeFromAnyThread();
|
||||
if (debugScopes)
|
||||
debugScopes->sweep(rt);
|
||||
}
|
||||
|
@ -619,7 +619,7 @@ JSCompartment::sweepNativeIterators()
|
|||
while (ni != enumerators) {
|
||||
JSObject *iterObj = ni->iterObj();
|
||||
NativeIterator *next = ni->next();
|
||||
if (gc::IsObjectAboutToBeFinalized(&iterObj))
|
||||
if (gc::IsObjectAboutToBeFinalizedFromAnyThread(&iterObj))
|
||||
ni->unlink();
|
||||
ni = next;
|
||||
}
|
||||
|
@ -636,9 +636,9 @@ JSCompartment::sweepCrossCompartmentWrappers()
|
|||
/* Remove dead wrappers from the table. */
|
||||
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
CrossCompartmentKey key = e.front().key();
|
||||
bool keyDying = IsCellAboutToBeFinalized(&key.wrapped);
|
||||
bool valDying = IsValueAboutToBeFinalized(e.front().value().unsafeGet());
|
||||
bool dbgDying = key.debugger && IsObjectAboutToBeFinalized(&key.debugger);
|
||||
bool keyDying = IsCellAboutToBeFinalizedFromAnyThread(&key.wrapped);
|
||||
bool valDying = IsValueAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet());
|
||||
bool dbgDying = key.debugger && IsObjectAboutToBeFinalizedFromAnyThread(&key.debugger);
|
||||
if (keyDying || valDying || dbgDying) {
|
||||
MOZ_ASSERT(key.kind != CrossCompartmentKey::StringWrapper);
|
||||
e.removeFront();
|
||||
|
|
|
@ -254,6 +254,7 @@ struct JSCompartment
|
|||
js::types::TypeObjectWithNewScriptSet newTypeObjects;
|
||||
js::types::TypeObjectWithNewScriptSet lazyTypeObjects;
|
||||
void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table);
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
void checkTypeObjectTablesAfterMovingGC();
|
||||
void checkTypeObjectTableAfterMovingGC(js::types::TypeObjectWithNewScriptSet &table);
|
||||
|
|
179
js/src/jsgc.cpp
179
js/src/jsgc.cpp
|
@ -3255,7 +3255,9 @@ GCHelperState::waitForBackgroundThread()
|
|||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
|
||||
#ifdef DEBUG
|
||||
rt->gc.lockOwner = nullptr;
|
||||
#endif
|
||||
PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
|
||||
#ifdef DEBUG
|
||||
rt->gc.lockOwner = PR_GetCurrentThread();
|
||||
|
@ -4610,6 +4612,97 @@ GCRuntime::endMarkingZoneGroup()
|
|||
marker.setMarkColorBlack();
|
||||
}
|
||||
|
||||
#define MAKE_GC_PARALLEL_TASK(name) \
|
||||
class name : public GCParallelTask {\
|
||||
JSRuntime *runtime;\
|
||||
virtual void run() MOZ_OVERRIDE;\
|
||||
public:\
|
||||
name (JSRuntime *rt) : runtime(rt) {}\
|
||||
}
|
||||
MAKE_GC_PARALLEL_TASK(SweepAtomsTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepInnerViewsTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepCCWrappersTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepBaseShapesTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepInitialShapesTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepTypeObjectsTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepRegExpsTask);
|
||||
MAKE_GC_PARALLEL_TASK(SweepMiscTask);
|
||||
|
||||
/* virtual */ void
|
||||
SweepAtomsTask::run()
|
||||
{
|
||||
runtime->sweepAtoms();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepInnerViewsTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepInnerViews();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepCCWrappersTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepCrossCompartmentWrappers();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepBaseShapesTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepBaseShapeTable();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepInitialShapesTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepInitialShapeTable();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepTypeObjectsTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepTypeObjectTables();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepRegExpsTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
|
||||
c->sweepRegExps();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
SweepMiscTask::run()
|
||||
{
|
||||
for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) {
|
||||
c->sweepCallsiteClones();
|
||||
c->sweepSavedStacks();
|
||||
c->sweepSelfHostingScriptSource();
|
||||
c->sweepNativeIterators();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::startTask(GCParallelTask &task, gcstats::Phase phase)
|
||||
{
|
||||
if (!task.startWithLockHeld()) {
|
||||
gcstats::AutoPhase ap(stats, phase);
|
||||
task.runFromMainThread(rt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::joinTask(GCParallelTask &task, gcstats::Phase phase)
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, task, phase);
|
||||
task.joinWithLockHeld();
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::beginSweepingZoneGroup()
|
||||
{
|
||||
|
@ -4639,6 +4732,14 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
validateIncrementalMarking();
|
||||
|
||||
FreeOp fop(rt);
|
||||
SweepAtomsTask sweepAtomsTask(rt);
|
||||
SweepInnerViewsTask sweepInnerViewsTask(rt);
|
||||
SweepCCWrappersTask sweepCCWrappersTask(rt);
|
||||
SweepBaseShapesTask sweepBaseShapesTask(rt);
|
||||
SweepInitialShapesTask sweepInitialShapesTask(rt);
|
||||
SweepTypeObjectsTask sweepTypeObjectsTask(rt);
|
||||
SweepRegExpsTask sweepRegExpsTask(rt);
|
||||
SweepMiscTask sweepMiscTask(rt);
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
|
||||
|
@ -4647,8 +4748,8 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
}
|
||||
|
||||
if (sweepingAtoms) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS);
|
||||
rt->sweepAtoms();
|
||||
AutoLockHelperThreadState helperLock;
|
||||
startTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -4656,58 +4757,26 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
gcstats::AutoSCC scc(stats, zoneGroupIndex);
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apiv(stats, gcstats::PHASE_SWEEP_INNER_VIEWS);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepInnerViews();
|
||||
}
|
||||
AutoLockHelperThreadState helperLock;
|
||||
startTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
|
||||
startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
|
||||
startTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
|
||||
startTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
|
||||
startTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
|
||||
startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
|
||||
startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
|
||||
}
|
||||
|
||||
// The remainder of the of the tasks run in parallel on the main
|
||||
// thread until we join, below.
|
||||
{
|
||||
gcstats::AutoPhase apccw(stats, gcstats::PHASE_SWEEP_CC_WRAPPER);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepCrossCompartmentWrappers();
|
||||
}
|
||||
}
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_MISC);
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apbs(stats, gcstats::PHASE_SWEEP_BASE_SHAPE);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepBaseShapeTable();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apis(stats, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepInitialShapeTable();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apto(stats, gcstats::PHASE_SWEEP_TYPE_OBJECT);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepTypeObjectTables();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apre(stats, gcstats::PHASE_SWEEP_REGEXP);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepRegExps();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase apmisc(stats, gcstats::PHASE_SWEEP_MISC);
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
c->sweepCallsiteClones();
|
||||
c->sweepSavedStacks();
|
||||
c->sweepGlobalObject(&fop);
|
||||
c->sweepSelfHostingScriptSource();
|
||||
c->sweepDebugScopes();
|
||||
c->sweepJitCompartment(&fop);
|
||||
c->sweepWeakMaps();
|
||||
c->sweepNativeIterators();
|
||||
}
|
||||
|
||||
// Bug 1071218: the following two methods have not yet been
|
||||
|
@ -4747,6 +4816,26 @@ GCRuntime::beginSweepingZoneGroup()
|
|||
rt->symbolRegistry().sweep();
|
||||
}
|
||||
|
||||
// Rejoin our off-main-thread tasks.
|
||||
if (sweepingAtoms) {
|
||||
AutoLockHelperThreadState helperLock;
|
||||
joinTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoSCC scc(stats, zoneGroupIndex);
|
||||
|
||||
AutoLockHelperThreadState helperLock;
|
||||
joinTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
|
||||
joinTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
|
||||
joinTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
|
||||
joinTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
|
||||
joinTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
|
||||
joinTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
|
||||
joinTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue all GC things in all zones for sweeping, either in the
|
||||
* foreground or on the background thread.
|
||||
|
|
|
@ -1070,6 +1070,47 @@ class GCHelperState
|
|||
}
|
||||
};
|
||||
|
||||
// A generic task used to dispatch work to the helper thread system.
|
||||
// Users should derive from GCParallelTask add what data they need and
|
||||
// override |run|.
|
||||
class GCParallelTask
|
||||
{
|
||||
// The state of the parallel computation.
|
||||
enum TaskState {
|
||||
NotStarted,
|
||||
Dispatched,
|
||||
Finished,
|
||||
} state;
|
||||
|
||||
// Amount of time this task took to execute.
|
||||
uint64_t duration_;
|
||||
|
||||
protected:
|
||||
virtual void run() = 0;
|
||||
|
||||
public:
|
||||
GCParallelTask() : state(NotStarted), duration_(0) {}
|
||||
|
||||
int64_t duration() const { return duration_; }
|
||||
|
||||
// The simple interface to a parallel task works exactly like pthreads.
|
||||
bool start();
|
||||
void join();
|
||||
|
||||
// If multiple tasks are to be started or joined at once, it is more
|
||||
// efficient to take the helper thread lock once and use these methods.
|
||||
bool startWithLockHeld();
|
||||
void joinWithLockHeld();
|
||||
|
||||
// Instead of dispatching to a helper, run the task on the main thread.
|
||||
void runFromMainThread(JSRuntime *rt);
|
||||
|
||||
// This should be friended to HelperThread, but cannot be because it
|
||||
// would introduce several circular dependencies.
|
||||
public:
|
||||
void runFromHelperThread();
|
||||
};
|
||||
|
||||
struct GCChunkHasher {
|
||||
typedef gc::Chunk *Lookup;
|
||||
|
||||
|
|
|
@ -4784,17 +4784,17 @@ TypeCompartment::sweep(FreeOp *fop)
|
|||
void
|
||||
JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
|
||||
{
|
||||
MOZ_ASSERT(zone()->isCollecting());
|
||||
MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting());
|
||||
if (table.initialized()) {
|
||||
for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
|
||||
TypeObjectWithNewScriptEntry entry = e.front();
|
||||
if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()) ||
|
||||
(entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)))
|
||||
if (IsTypeObjectAboutToBeFinalizedFromAnyThread(entry.object.unsafeGet()) ||
|
||||
(entry.newFunction && IsObjectAboutToBeFinalizedFromAnyThread(&entry.newFunction)))
|
||||
{
|
||||
e.removeFront();
|
||||
} else {
|
||||
/* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */
|
||||
MOZ_ASSERT(entry.object == e.front().object);
|
||||
MOZ_ASSERT(entry.object.unbarrieredGet() == e.front().object.unbarrieredGet());
|
||||
MOZ_ASSERT(entry.newFunction == e.front().newFunction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -888,12 +888,12 @@ InnerViewTable::removeViews(ArrayBufferObject *obj)
|
|||
bool
|
||||
InnerViewTable::sweepEntry(JSObject **pkey, ViewVector &views)
|
||||
{
|
||||
if (IsObjectAboutToBeFinalized(pkey))
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(pkey))
|
||||
return true;
|
||||
|
||||
MOZ_ASSERT(!views.empty());
|
||||
for (size_t i = 0; i < views.length(); i++) {
|
||||
if (IsObjectAboutToBeFinalized(&views[i])) {
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(&views[i])) {
|
||||
views[i--] = views.back();
|
||||
views.popBack();
|
||||
}
|
||||
|
|
|
@ -714,6 +714,101 @@ GlobalHelperThreadState::canStartGCHelperTask()
|
|||
return !gcHelperWorklist().empty();
|
||||
}
|
||||
|
||||
bool
|
||||
GlobalHelperThreadState::canStartGCParallelTask()
|
||||
{
|
||||
return !gcParallelWorklist().empty();
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::startWithLockHeld()
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().isLocked());
|
||||
|
||||
// Tasks cannot be started twice.
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
|
||||
// If we do the shutdown GC before running anything, we may never
|
||||
// have initialized the helper threads. Just use the serial path
|
||||
// since we cannot safely intialize them at this point.
|
||||
if (!HelperThreadState().threads)
|
||||
return false;
|
||||
|
||||
if (!HelperThreadState().gcParallelWorklist().append(this))
|
||||
return false;
|
||||
state = Dispatched;
|
||||
|
||||
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GCParallelTask::start()
|
||||
{
|
||||
AutoLockHelperThreadState helperLock;
|
||||
return startWithLockHeld();
|
||||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::joinWithLockHeld()
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().isLocked());
|
||||
|
||||
if (state == NotStarted)
|
||||
return;
|
||||
|
||||
while (state != Finished)
|
||||
HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
|
||||
state = NotStarted;
|
||||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::join()
|
||||
{
|
||||
AutoLockHelperThreadState helperLock;
|
||||
joinWithLockHeld();
|
||||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::runFromMainThread(JSRuntime *rt)
|
||||
{
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
|
||||
uint64_t timeStart = PRMJ_Now();
|
||||
run();
|
||||
duration_ = PRMJ_Now() - timeStart;
|
||||
}
|
||||
|
||||
void
|
||||
js::GCParallelTask::runFromHelperThread()
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().isLocked());
|
||||
|
||||
{
|
||||
AutoUnlockHelperThreadState parallelSection;
|
||||
uint64_t timeStart = PRMJ_Now();
|
||||
run();
|
||||
duration_ = PRMJ_Now() - timeStart;
|
||||
}
|
||||
|
||||
state = Finished;
|
||||
HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
void
|
||||
HelperThread::handleGCParallelWorkload()
|
||||
{
|
||||
MOZ_ASSERT(HelperThreadState().isLocked());
|
||||
MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
|
||||
MOZ_ASSERT(idle());
|
||||
|
||||
MOZ_ASSERT(!gcParallelTask);
|
||||
gcParallelTask = HelperThreadState().gcParallelWorklist().popCopy();
|
||||
gcParallelTask->runFromHelperThread();
|
||||
gcParallelTask = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
LeaveParseTaskZone(JSRuntime *rt, ParseTask *task)
|
||||
{
|
||||
|
@ -1237,7 +1332,8 @@ HelperThread::threadLoop()
|
|||
(ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority()) ||
|
||||
HelperThreadState().canStartParseTask() ||
|
||||
HelperThreadState().canStartCompressionTask() ||
|
||||
HelperThreadState().canStartGCHelperTask())
|
||||
HelperThreadState().canStartGCHelperTask() ||
|
||||
HelperThreadState().canStartGCParallelTask())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -1255,6 +1351,8 @@ HelperThread::threadLoop()
|
|||
handleCompressionWorkload();
|
||||
else if (HelperThreadState().canStartGCHelperTask())
|
||||
handleGCHelperWorkload();
|
||||
else if (HelperThreadState().canStartGCParallelTask())
|
||||
handleGCParallelWorkload();
|
||||
else
|
||||
MOZ_CRASH("No task to perform");
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ class GlobalHelperThreadState
|
|||
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
|
||||
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
|
||||
typedef Vector<GCHelperState *, 0, SystemAllocPolicy> GCHelperStateVector;
|
||||
typedef Vector<GCParallelTask *, 0, SystemAllocPolicy> GCParallelTaskVector;
|
||||
typedef mozilla::LinkedList<jit::IonBuilder> IonBuilderList;
|
||||
|
||||
// List of available threads, or null if the thread state has not been initialized.
|
||||
|
@ -87,6 +88,9 @@ class GlobalHelperThreadState
|
|||
// Runtimes which have sweeping / allocating work to do.
|
||||
GCHelperStateVector gcHelperWorklist_;
|
||||
|
||||
// GC tasks needing to be done in parallel.
|
||||
GCParallelTaskVector gcParallelWorklist_;
|
||||
|
||||
public:
|
||||
size_t maxIonCompilationThreads() const {
|
||||
return 1;
|
||||
|
@ -178,11 +182,17 @@ class GlobalHelperThreadState
|
|||
return gcHelperWorklist_;
|
||||
}
|
||||
|
||||
GCParallelTaskVector &gcParallelWorklist() {
|
||||
MOZ_ASSERT(isLocked());
|
||||
return gcParallelWorklist_;
|
||||
}
|
||||
|
||||
bool canStartAsmJSCompile();
|
||||
bool canStartIonCompile();
|
||||
bool canStartParseTask();
|
||||
bool canStartCompressionTask();
|
||||
bool canStartGCHelperTask();
|
||||
bool canStartGCParallelTask();
|
||||
|
||||
// Unlike the methods above, the value returned by this method can change
|
||||
// over time, even if the helper thread state lock is held throughout.
|
||||
|
@ -299,8 +309,16 @@ struct HelperThread
|
|||
/* Any GC state for background sweeping or allocating being performed. */
|
||||
GCHelperState *gcHelperState;
|
||||
|
||||
/* State required to perform a GC parallel task. */
|
||||
GCParallelTask *gcParallelTask;
|
||||
|
||||
bool idle() const {
|
||||
return !ionBuilder && !asmData && !parseTask && !compressionTask && !gcHelperState;
|
||||
return !ionBuilder &&
|
||||
!asmData &&
|
||||
!parseTask &&
|
||||
!compressionTask &&
|
||||
!gcHelperState &&
|
||||
!gcParallelTask;
|
||||
}
|
||||
|
||||
void destroy();
|
||||
|
@ -310,6 +328,7 @@ struct HelperThread
|
|||
void handleParseWorkload();
|
||||
void handleCompressionWorkload();
|
||||
void handleGCHelperWorkload();
|
||||
void handleGCParallelWorkload();
|
||||
|
||||
static void ThreadMain(void *arg);
|
||||
void threadLoop();
|
||||
|
|
|
@ -771,11 +771,12 @@ RegExpCompartment::sweep(JSRuntime *rt)
|
|||
// Because of this we only treat the marked_ bit as a hint, and destroy
|
||||
// the RegExpShared if it was accidentally marked earlier but wasn't
|
||||
// marked by the current trace.
|
||||
bool keep = shared->marked() && !IsStringAboutToBeFinalized(shared->source.unsafeGet());
|
||||
bool keep = shared->marked() &&
|
||||
!IsStringAboutToBeFinalizedFromAnyThread(shared->source.unsafeGet());
|
||||
for (size_t i = 0; i < ArrayLength(shared->compilationArray); i++) {
|
||||
RegExpShared::RegExpCompilation &compilation = shared->compilationArray[i];
|
||||
if (compilation.jitCode &&
|
||||
IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet()))
|
||||
IsJitCodeAboutToBeFinalizedFromAnyThread(compilation.jitCode.unsafeGet()))
|
||||
{
|
||||
keep = false;
|
||||
}
|
||||
|
@ -789,7 +790,7 @@ RegExpCompartment::sweep(JSRuntime *rt)
|
|||
}
|
||||
|
||||
if (matchResultTemplateObject_ &&
|
||||
IsObjectAboutToBeFinalized(matchResultTemplateObject_.unsafeGet()))
|
||||
IsObjectAboutToBeFinalizedFromAnyThread(matchResultTemplateObject_.unsafeGet()))
|
||||
{
|
||||
matchResultTemplateObject_.set(nullptr);
|
||||
}
|
||||
|
|
|
@ -431,10 +431,10 @@ SavedStacks::sweep(JSRuntime *rt)
|
|||
{
|
||||
if (frames.initialized()) {
|
||||
for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) {
|
||||
JSObject *obj = static_cast<JSObject *>(e.front());
|
||||
JSObject *obj = e.front().unbarrieredGet();
|
||||
JSObject *temp = obj;
|
||||
|
||||
if (IsObjectAboutToBeFinalized(&obj)) {
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(&obj)) {
|
||||
e.removeFront();
|
||||
} else {
|
||||
SavedFrame *frame = &obj->as<SavedFrame>();
|
||||
|
@ -459,7 +459,9 @@ SavedStacks::sweep(JSRuntime *rt)
|
|||
|
||||
sweepPCLocationMap();
|
||||
|
||||
if (savedFrameProto && IsObjectAboutToBeFinalized(savedFrameProto.unsafeGet())) {
|
||||
if (savedFrameProto.unbarrieredGet() &&
|
||||
IsObjectAboutToBeFinalizedFromAnyThread(savedFrameProto.unsafeGet()))
|
||||
{
|
||||
savedFrameProto.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -650,7 +652,7 @@ SavedStacks::sweepPCLocationMap()
|
|||
for (PCLocationMap::Enum e(pcLocationMap); !e.empty(); e.popFront()) {
|
||||
PCKey key = e.front().key();
|
||||
JSScript *script = key.script.get();
|
||||
if (IsScriptAboutToBeFinalized(&script)) {
|
||||
if (IsScriptAboutToBeFinalizedFromAnyThread(&script)) {
|
||||
e.removeFront();
|
||||
} else if (script != key.script.get()) {
|
||||
key.script = script;
|
||||
|
|
|
@ -1251,9 +1251,9 @@ void
|
|||
ScopeIterVal::sweep()
|
||||
{
|
||||
/* We need to update possibly moved pointers on sweep. */
|
||||
MOZ_ALWAYS_FALSE(IsObjectAboutToBeFinalized(cur_.unsafeGet()));
|
||||
MOZ_ALWAYS_FALSE(IsObjectAboutToBeFinalizedFromAnyThread(cur_.unsafeGet()));
|
||||
if (staticScope_)
|
||||
MOZ_ALWAYS_FALSE(IsObjectAboutToBeFinalized(staticScope_.unsafeGet()));
|
||||
MOZ_ALWAYS_FALSE(IsObjectAboutToBeFinalizedFromAnyThread(staticScope_.unsafeGet()));
|
||||
}
|
||||
|
||||
// Live ScopeIter values may be added to DebugScopes::liveScopes, as
|
||||
|
@ -1952,7 +1952,7 @@ DebugScopes::sweep(JSRuntime *rt)
|
|||
*/
|
||||
for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
|
||||
DebugScopeObject **debugScope = e.front().value().unsafeGet();
|
||||
if (IsObjectAboutToBeFinalized(debugScope)) {
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(debugScope)) {
|
||||
/*
|
||||
* Note that onPopCall and onPopBlock rely on missingScopes to find
|
||||
* scope objects that we synthesized for the debugger's sake, and
|
||||
|
@ -1997,7 +1997,7 @@ DebugScopes::sweep(JSRuntime *rt)
|
|||
* Scopes can be finalized when a debugger-synthesized ScopeObject is
|
||||
* no longer reachable via its DebugScopeObject.
|
||||
*/
|
||||
if (IsObjectAboutToBeFinalized(&scope))
|
||||
if (IsObjectAboutToBeFinalizedFromAnyThread(&scope))
|
||||
e.removeFront();
|
||||
else if (scope != e.front().key())
|
||||
e.rekeyFront(scope);
|
||||
|
|
|
@ -1530,9 +1530,9 @@ JSCompartment::sweepBaseShapeTable()
|
|||
if (baseShapes.initialized()) {
|
||||
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
|
||||
UnownedBaseShape *base = e.front().unbarrieredGet();
|
||||
if (IsBaseShapeAboutToBeFinalized(&base)) {
|
||||
if (IsBaseShapeAboutToBeFinalizedFromAnyThread(&base)) {
|
||||
e.removeFront();
|
||||
} else if (base != e.front()) {
|
||||
} else if (base != e.front().unbarrieredGet()) {
|
||||
StackBaseShape sbase(base);
|
||||
ReadBarriered<UnownedBaseShape *> b(base);
|
||||
e.rekeyFront(&sbase, b);
|
||||
|
@ -1826,14 +1826,14 @@ JSCompartment::sweepInitialShapeTable()
|
|||
const InitialShapeEntry &entry = e.front();
|
||||
Shape *shape = entry.shape.unbarrieredGet();
|
||||
JSObject *proto = entry.proto.raw();
|
||||
if (IsShapeAboutToBeFinalized(&shape) ||
|
||||
(entry.proto.isObject() && IsObjectAboutToBeFinalized(&proto)))
|
||||
if (IsShapeAboutToBeFinalizedFromAnyThread(&shape) ||
|
||||
(entry.proto.isObject() && IsObjectAboutToBeFinalizedFromAnyThread(&proto)))
|
||||
{
|
||||
e.removeFront();
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
DebugOnly<JSObject *> parent = shape->getObjectParent();
|
||||
MOZ_ASSERT(!parent || !IsObjectAboutToBeFinalized(&parent));
|
||||
MOZ_ASSERT(!parent || !IsObjectAboutToBeFinalizedFromAnyThread(&parent));
|
||||
MOZ_ASSERT(parent == shape->getObjectParent());
|
||||
#endif
|
||||
if (shape != entry.shape.unbarrieredGet() || proto != entry.proto.raw()) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче