Bug 1064578 - Sweep tables in parallel; r=jonco r=bhackett

--HG--
extra : rebase_source : f4e1d6c1067c2ae5c01f62dee3d4b28471c62e98
This commit is contained in:
Terrence Cole 2014-09-12 17:32:51 -07:00
Родитель 83ec10e366
Коммит bec7c338fd
19 изменённых файлов: 428 добавлений и 89 удалений

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

@ -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.endPhase(phase);
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);

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

@ -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()) {