mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 941805 - Make the pool of JS workers be per process rather than per runtime, r=billm.
This commit is contained in:
parent
0da87bcc7d
commit
5e0f361bec
@ -1405,7 +1405,7 @@ static bool
|
|||||||
WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
|
WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
args.rval().setNumber(static_cast<double>(cx->runtime()->workerThreadCount()));
|
args.rval().setInt32(cx->runtime()->useHelperThreads() ? WorkerThreadState().threadCount : 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5364,20 +5364,20 @@ CheckFunctionsSequential(ModuleCompiler &m)
|
|||||||
// on rt->workerThreadState->asmJSCompilationInProgress.
|
// on rt->workerThreadState->asmJSCompilationInProgress.
|
||||||
class ParallelCompilationGuard
|
class ParallelCompilationGuard
|
||||||
{
|
{
|
||||||
WorkerThreadState *parallelState_;
|
bool parallelState_;
|
||||||
public:
|
public:
|
||||||
ParallelCompilationGuard() : parallelState_(nullptr) {}
|
ParallelCompilationGuard() : parallelState_(false) {}
|
||||||
~ParallelCompilationGuard() {
|
~ParallelCompilationGuard() {
|
||||||
if (parallelState_) {
|
if (parallelState_) {
|
||||||
JS_ASSERT(parallelState_->asmJSCompilationInProgress == true);
|
JS_ASSERT(WorkerThreadState().asmJSCompilationInProgress == true);
|
||||||
parallelState_->asmJSCompilationInProgress = false;
|
WorkerThreadState().asmJSCompilationInProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool claim(WorkerThreadState *state) {
|
bool claim() {
|
||||||
JS_ASSERT(!parallelState_);
|
JS_ASSERT(!parallelState_);
|
||||||
if (!state->asmJSCompilationInProgress.compareExchange(false, true))
|
if (!WorkerThreadState().asmJSCompilationInProgress.compareExchange(false, true))
|
||||||
return false;
|
return false;
|
||||||
parallelState_ = state;
|
parallelState_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5391,22 +5391,23 @@ ParallelCompilationEnabled(ExclusiveContext *cx)
|
|||||||
// parsing task, ensure that there another free thread to avoid deadlock.
|
// parsing task, ensure that there another free thread to avoid deadlock.
|
||||||
// (Note: there is at most one thread used for parsing so we don't have to
|
// (Note: there is at most one thread used for parsing so we don't have to
|
||||||
// worry about general dining philosophers.)
|
// worry about general dining philosophers.)
|
||||||
if (!cx->isJSContext())
|
if (WorkerThreadState().threadCount <= 1)
|
||||||
return cx->workerThreadState()->numThreads > 1;
|
return false;
|
||||||
|
|
||||||
|
if (!cx->isJSContext())
|
||||||
|
return true;
|
||||||
return cx->asJSContext()->runtime()->canUseParallelIonCompilation();
|
return cx->asJSContext()->runtime()->canUseParallelIonCompilation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// State of compilation as tracked and updated by the main thread.
|
// State of compilation as tracked and updated by the main thread.
|
||||||
struct ParallelGroupState
|
struct ParallelGroupState
|
||||||
{
|
{
|
||||||
WorkerThreadState &state;
|
|
||||||
js::Vector<AsmJSParallelTask> &tasks;
|
js::Vector<AsmJSParallelTask> &tasks;
|
||||||
int32_t outstandingJobs; // Good work, jobs!
|
int32_t outstandingJobs; // Good work, jobs!
|
||||||
uint32_t compiledJobs;
|
uint32_t compiledJobs;
|
||||||
|
|
||||||
ParallelGroupState(WorkerThreadState &state, js::Vector<AsmJSParallelTask> &tasks)
|
ParallelGroupState(js::Vector<AsmJSParallelTask> &tasks)
|
||||||
: state(state), tasks(tasks), outstandingJobs(0), compiledJobs(0)
|
: tasks(tasks), outstandingJobs(0), compiledJobs(0)
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5414,14 +5415,14 @@ struct ParallelGroupState
|
|||||||
static AsmJSParallelTask *
|
static AsmJSParallelTask *
|
||||||
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
||||||
{
|
{
|
||||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
while (!group.state.asmJSWorkerFailed()) {
|
while (!WorkerThreadState().asmJSWorkerFailed()) {
|
||||||
if (!group.state.asmJSFinishedList.empty()) {
|
if (!WorkerThreadState().asmJSFinishedList().empty()) {
|
||||||
group.outstandingJobs--;
|
group.outstandingJobs--;
|
||||||
return group.state.asmJSFinishedList.popCopy();
|
return WorkerThreadState().asmJSFinishedList().popCopy();
|
||||||
}
|
}
|
||||||
group.state.wait(WorkerThreadState::CONSUMER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -5471,9 +5472,14 @@ GetUnusedTask(ParallelGroupState &group, uint32_t i, AsmJSParallelTask **outTask
|
|||||||
static bool
|
static bool
|
||||||
CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
||||||
{
|
{
|
||||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
#ifdef DEBUG
|
||||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
{
|
||||||
group.state.resetAsmJSFailureState();
|
AutoLockWorkerThreadState lock;
|
||||||
|
JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
|
||||||
|
JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
WorkerThreadState().resetAsmJSFailureState();
|
||||||
|
|
||||||
for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
|
for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
|
||||||
// Get exclusive access to an empty LifoAlloc from the thread group's pool.
|
// Get exclusive access to an empty LifoAlloc from the thread group's pool.
|
||||||
@ -5488,7 +5494,7 @@ CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Perform optimizations and LIR generation on a worker thread.
|
// Perform optimizations and LIR generation on a worker thread.
|
||||||
task->init(func, mir);
|
task->init(m.cx()->compartment()->runtimeFromAnyThread(), func, mir);
|
||||||
if (!StartOffThreadAsmJSCompile(m.cx(), task))
|
if (!StartOffThreadAsmJSCompile(m.cx(), task))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -5507,9 +5513,14 @@ CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
|||||||
|
|
||||||
JS_ASSERT(group.outstandingJobs == 0);
|
JS_ASSERT(group.outstandingJobs == 0);
|
||||||
JS_ASSERT(group.compiledJobs == m.numFunctions());
|
JS_ASSERT(group.compiledJobs == m.numFunctions());
|
||||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
#ifdef DEBUG
|
||||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
{
|
||||||
JS_ASSERT(!group.state.asmJSWorkerFailed());
|
AutoLockWorkerThreadState lock;
|
||||||
|
JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
|
||||||
|
JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
JS_ASSERT(!WorkerThreadState().asmJSWorkerFailed());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5526,32 +5537,32 @@ CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
|
|||||||
if (!group.outstandingJobs)
|
if (!group.outstandingJobs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
// From the compiling tasks, eliminate those waiting for worker assignation.
|
// From the compiling tasks, eliminate those waiting for worker assignation.
|
||||||
group.outstandingJobs -= group.state.asmJSWorklist.length();
|
group.outstandingJobs -= WorkerThreadState().asmJSWorklist().length();
|
||||||
group.state.asmJSWorklist.clear();
|
WorkerThreadState().asmJSWorklist().clear();
|
||||||
|
|
||||||
// From the compiling tasks, eliminate those waiting for codegen.
|
// From the compiling tasks, eliminate those waiting for codegen.
|
||||||
group.outstandingJobs -= group.state.asmJSFinishedList.length();
|
group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
|
||||||
group.state.asmJSFinishedList.clear();
|
WorkerThreadState().asmJSFinishedList().clear();
|
||||||
|
|
||||||
// Eliminate tasks that failed without adding to the finished list.
|
// Eliminate tasks that failed without adding to the finished list.
|
||||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
|
||||||
|
|
||||||
// Any remaining tasks are therefore undergoing active compilation.
|
// Any remaining tasks are therefore undergoing active compilation.
|
||||||
JS_ASSERT(group.outstandingJobs >= 0);
|
JS_ASSERT(group.outstandingJobs >= 0);
|
||||||
while (group.outstandingJobs > 0) {
|
while (group.outstandingJobs > 0) {
|
||||||
group.state.wait(WorkerThreadState::CONSUMER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||||
|
|
||||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
|
||||||
group.outstandingJobs -= group.state.asmJSFinishedList.length();
|
group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
|
||||||
group.state.asmJSFinishedList.clear();
|
WorkerThreadState().asmJSFinishedList().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(group.outstandingJobs == 0);
|
JS_ASSERT(group.outstandingJobs == 0);
|
||||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
|
||||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
|
static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
|
||||||
@ -5565,12 +5576,11 @@ CheckFunctionsParallel(ModuleCompiler &m)
|
|||||||
// constraint by hoisting asmJS* state out of WorkerThreadState so multiple
|
// constraint by hoisting asmJS* state out of WorkerThreadState so multiple
|
||||||
// concurrent asm.js parallel compilations don't race.)
|
// concurrent asm.js parallel compilations don't race.)
|
||||||
ParallelCompilationGuard g;
|
ParallelCompilationGuard g;
|
||||||
if (!ParallelCompilationEnabled(m.cx()) || !g.claim(m.cx()->workerThreadState()))
|
if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
|
||||||
return CheckFunctionsSequential(m);
|
return CheckFunctionsSequential(m);
|
||||||
|
|
||||||
// Saturate all worker threads plus the main thread.
|
// Saturate all worker threads plus the main thread.
|
||||||
WorkerThreadState &state = *m.cx()->workerThreadState();
|
size_t numParallelJobs = WorkerThreadState().threadCount + 1;
|
||||||
size_t numParallelJobs = state.numThreads + 1;
|
|
||||||
|
|
||||||
// Allocate scoped AsmJSParallelTask objects. Each contains a unique
|
// Allocate scoped AsmJSParallelTask objects. Each contains a unique
|
||||||
// LifoAlloc that provides all necessary memory for compilation.
|
// LifoAlloc that provides all necessary memory for compilation.
|
||||||
@ -5582,12 +5592,12 @@ CheckFunctionsParallel(ModuleCompiler &m)
|
|||||||
tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
|
tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
|
||||||
|
|
||||||
// With compilation memory in-scope, dispatch worker threads.
|
// With compilation memory in-scope, dispatch worker threads.
|
||||||
ParallelGroupState group(state, tasks);
|
ParallelGroupState group(tasks);
|
||||||
if (!CheckFunctionsParallelImpl(m, group)) {
|
if (!CheckFunctionsParallelImpl(m, group)) {
|
||||||
CancelOutstandingJobs(m, group);
|
CancelOutstandingJobs(m, group);
|
||||||
|
|
||||||
// If failure was triggered by a worker thread, report error.
|
// If failure was triggered by a worker thread, report error.
|
||||||
if (void *maybeFunc = state.maybeAsmJSFailedFunction()) {
|
if (void *maybeFunc = WorkerThreadState().maybeAsmJSFailedFunction()) {
|
||||||
ModuleCompiler::Func *func = reinterpret_cast<ModuleCompiler::Func *>(maybeFunc);
|
ModuleCompiler::Func *func = reinterpret_cast<ModuleCompiler::Func *>(maybeFunc);
|
||||||
return m.failOffset(func->srcOffset(), "allocation failure during compilation");
|
return m.failOffset(func->srcOffset(), "allocation failure during compilation");
|
||||||
}
|
}
|
||||||
|
@ -513,15 +513,18 @@ jit::FinishOffThreadBuilder(IonBuilder *builder)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
FinishAllOffThreadCompilations(JitCompartment *ion)
|
FinishAllOffThreadCompilations(JSCompartment *comp)
|
||||||
{
|
{
|
||||||
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
|
AutoLockWorkerThreadState lock;
|
||||||
|
GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
|
||||||
|
|
||||||
for (size_t i = 0; i < compilations.length(); i++) {
|
for (size_t i = 0; i < finished.length(); i++) {
|
||||||
IonBuilder *builder = compilations[i];
|
IonBuilder *builder = finished[i];
|
||||||
FinishOffThreadBuilder(builder);
|
if (builder->compartment == CompileCompartment::get(comp)) {
|
||||||
|
FinishOffThreadBuilder(builder);
|
||||||
|
WorkerThreadState().remove(finished, &i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
compilations.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
@ -543,7 +546,7 @@ JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
|
|||||||
// do this for minor GCs.
|
// do this for minor GCs.
|
||||||
JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
|
JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
|
||||||
CancelOffThreadIonCompile(compartment, nullptr);
|
CancelOffThreadIonCompile(compartment, nullptr);
|
||||||
FinishAllOffThreadCompilations(this);
|
FinishAllOffThreadCompilations(compartment);
|
||||||
|
|
||||||
// Free temporary OSR buffer.
|
// Free temporary OSR buffer.
|
||||||
rt->freeOsrTempData();
|
rt->freeOsrTempData();
|
||||||
@ -1512,18 +1515,30 @@ AttachFinishedCompilations(JSContext *cx)
|
|||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JitCompartment *ion = cx->compartment()->jitCompartment();
|
JitCompartment *ion = cx->compartment()->jitCompartment();
|
||||||
if (!ion || !cx->runtime()->workerThreadState)
|
if (!ion)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
types::AutoEnterAnalysis enterTypes(cx);
|
types::AutoEnterAnalysis enterTypes(cx);
|
||||||
AutoLockWorkerThreadState lock(*cx->runtime()->workerThreadState);
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
|
GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
|
||||||
|
|
||||||
// Incorporate any off thread compilations which have finished, failed or
|
// Incorporate any off thread compilations for the compartment which have
|
||||||
// have been cancelled.
|
// finished, failed or have been cancelled.
|
||||||
while (!compilations.empty()) {
|
while (true) {
|
||||||
IonBuilder *builder = compilations.popCopy();
|
IonBuilder *builder = nullptr;
|
||||||
|
|
||||||
|
// Find a finished builder for the compartment.
|
||||||
|
for (size_t i = 0; i < finished.length(); i++) {
|
||||||
|
IonBuilder *testBuilder = finished[i];
|
||||||
|
if (testBuilder->compartment == CompileCompartment::get(cx->compartment())) {
|
||||||
|
builder = testBuilder;
|
||||||
|
WorkerThreadState().remove(finished, &i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!builder)
|
||||||
|
break;
|
||||||
|
|
||||||
if (CodeGenerator *codegen = builder->backgroundCodegen()) {
|
if (CodeGenerator *codegen = builder->backgroundCodegen()) {
|
||||||
RootedScript script(cx, builder->script());
|
RootedScript script(cx, builder->script());
|
||||||
@ -1538,7 +1553,7 @@ AttachFinishedCompilations(JSContext *cx)
|
|||||||
{
|
{
|
||||||
// Release the worker thread lock and root the compiler for GC.
|
// Release the worker thread lock and root the compiler for GC.
|
||||||
AutoTempAllocatorRooter root(cx, &builder->alloc());
|
AutoTempAllocatorRooter root(cx, &builder->alloc());
|
||||||
AutoUnlockWorkerThreadState unlock(cx->runtime());
|
AutoUnlockWorkerThreadState unlock;
|
||||||
AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->jitRuntime());
|
AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->jitRuntime());
|
||||||
success = codegen->link(cx, builder->constraints());
|
success = codegen->link(cx, builder->constraints());
|
||||||
}
|
}
|
||||||
@ -1555,8 +1570,6 @@ AttachFinishedCompilations(JSContext *cx)
|
|||||||
|
|
||||||
FinishOffThreadBuilder(builder);
|
FinishOffThreadBuilder(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
compilations.clear();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1569,10 +1582,14 @@ OffThreadCompilationAvailable(JSContext *cx)
|
|||||||
// on the main thread in some cases. Do not compile off thread during an
|
// on the main thread in some cases. Do not compile off thread during an
|
||||||
// incremental GC, as this may trip incremental read barriers.
|
// incremental GC, as this may trip incremental read barriers.
|
||||||
//
|
//
|
||||||
|
// Require cpuCount > 1 so that Ion compilation jobs and main-thread
|
||||||
|
// execution are not competing for the same resources.
|
||||||
|
//
|
||||||
// Skip off thread compilation if PC count profiling is enabled, as
|
// Skip off thread compilation if PC count profiling is enabled, as
|
||||||
// CodeGenerator::maybeCreateScriptCounts will not attach script profiles
|
// CodeGenerator::maybeCreateScriptCounts will not attach script profiles
|
||||||
// when running off thread.
|
// when running off thread.
|
||||||
return cx->runtime()->canUseParallelIonCompilation()
|
return cx->runtime()->canUseParallelIonCompilation()
|
||||||
|
&& WorkerThreadState().cpuCount > 1
|
||||||
&& cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
|
&& cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
|
||||||
&& !cx->runtime()->profilingScripts;
|
&& !cx->runtime()->profilingScripts;
|
||||||
}
|
}
|
||||||
@ -1832,7 +1849,9 @@ CheckScriptSize(JSContext *cx, JSScript* script)
|
|||||||
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
|
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
|
||||||
numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
|
numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
|
||||||
{
|
{
|
||||||
if (cx->runtime()->canUseParallelIonCompilation()) {
|
if (cx->runtime()->canUseParallelIonCompilation() &&
|
||||||
|
WorkerThreadState().cpuCount > 1)
|
||||||
|
{
|
||||||
// Even if off thread compilation is enabled, there are cases where
|
// Even if off thread compilation is enabled, there are cases where
|
||||||
// compilation must still occur on the main thread. Don't compile
|
// compilation must still occur on the main thread. Don't compile
|
||||||
// in these cases (except when profiling scripts, as compilations
|
// in these cases (except when profiling scripts, as compilations
|
||||||
@ -2489,7 +2508,7 @@ jit::StopAllOffThreadCompilations(JSCompartment *comp)
|
|||||||
if (!comp->jitCompartment())
|
if (!comp->jitCompartment())
|
||||||
return;
|
return;
|
||||||
CancelOffThreadIonCompile(comp, nullptr);
|
CancelOffThreadIonCompile(comp, nullptr);
|
||||||
FinishAllOffThreadCompilations(comp->jitCompartment());
|
FinishAllOffThreadCompilations(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -59,8 +59,6 @@ typedef void (*EnterJitCode)(void *code, unsigned argc, Value *argv, StackFrame
|
|||||||
|
|
||||||
class IonBuilder;
|
class IonBuilder;
|
||||||
|
|
||||||
typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
|
|
||||||
|
|
||||||
// ICStubSpace is an abstraction for allocation policy and storage for stub data.
|
// ICStubSpace is an abstraction for allocation policy and storage for stub data.
|
||||||
// There are two kinds of stubs: optimized stubs and fallback stubs (the latter
|
// There are two kinds of stubs: optimized stubs and fallback stubs (the latter
|
||||||
// also includes stubs that can make non-tail calls that can GC).
|
// also includes stubs that can make non-tail calls that can GC).
|
||||||
@ -332,12 +330,6 @@ class JitCompartment
|
|||||||
// Ion state for the compartment's runtime.
|
// Ion state for the compartment's runtime.
|
||||||
JitRuntime *rt;
|
JitRuntime *rt;
|
||||||
|
|
||||||
// Any scripts for which off thread compilation has successfully finished,
|
|
||||||
// failed, or been cancelled. All off thread compilations which are started
|
|
||||||
// will eventually appear in this list asynchronously. Protected by the
|
|
||||||
// runtime's analysis lock.
|
|
||||||
OffThreadCompilationVector finishedOffThreadCompilations_;
|
|
||||||
|
|
||||||
// Map ICStub keys to ICStub shared code objects.
|
// Map ICStub keys to ICStub shared code objects.
|
||||||
typedef WeakValueCache<uint32_t, ReadBarriered<JitCode> > ICStubCodeMap;
|
typedef WeakValueCache<uint32_t, ReadBarriered<JitCode> > ICStubCodeMap;
|
||||||
ICStubCodeMap *stubCodes_;
|
ICStubCodeMap *stubCodes_;
|
||||||
@ -361,10 +353,6 @@ class JitCompartment
|
|||||||
JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
|
JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OffThreadCompilationVector &finishedOffThreadCompilations() {
|
|
||||||
return finishedOffThreadCompilations_;
|
|
||||||
}
|
|
||||||
|
|
||||||
JitCode *getStubCode(uint32_t key) {
|
JitCode *getStubCode(uint32_t key) {
|
||||||
ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
|
ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
|
||||||
if (p)
|
if (p)
|
||||||
|
@ -4529,7 +4529,7 @@ JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token)
|
|||||||
if (maybecx)
|
if (maybecx)
|
||||||
lfc.construct(maybecx);
|
lfc.construct(maybecx);
|
||||||
|
|
||||||
return rt->workerThreadState->finishParseTask(maybecx, rt, token);
|
return WorkerThreadState().finishParseTask(maybecx, rt, token);
|
||||||
#else
|
#else
|
||||||
MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
|
MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
|
||||||
#endif
|
#endif
|
||||||
|
@ -1052,9 +1052,6 @@ js::ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, Conte
|
|||||||
perThreadData(pt),
|
perThreadData(pt),
|
||||||
allocator_(nullptr)
|
allocator_(nullptr)
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
JS_ASSERT_IF(kind == Context_Exclusive, rt->workerThreadState != nullptr);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -282,8 +282,6 @@ struct ThreadSafeContext : ContextFriendFields,
|
|||||||
PropertyName *emptyString() { return runtime_->emptyString; }
|
PropertyName *emptyString() { return runtime_->emptyString; }
|
||||||
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
||||||
bool useHelperThreads() { return runtime_->useHelperThreads(); }
|
bool useHelperThreads() { return runtime_->useHelperThreads(); }
|
||||||
unsigned cpuCount() { return runtime_->cpuCount(); }
|
|
||||||
size_t workerThreadCount() { return runtime_->workerThreadCount(); }
|
|
||||||
void *runtimeAddressForJit() { return runtime_; }
|
void *runtimeAddressForJit() { return runtime_; }
|
||||||
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
||||||
void *stackLimitAddressForJitCode(StackKind kind);
|
void *stackLimitAddressForJitCode(StackKind kind);
|
||||||
@ -390,15 +388,6 @@ class ExclusiveContext : public ThreadSafeContext
|
|||||||
return runtime_->scriptDataTable();
|
return runtime_->scriptDataTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
// Since JSRuntime::workerThreadState is necessarily initialized from the
|
|
||||||
// main thread before the first worker thread can access it, there is no
|
|
||||||
// possibility for a race read/writing it.
|
|
||||||
WorkerThreadState *workerThreadState() {
|
|
||||||
return runtime_->workerThreadState;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Methods specific to any WorkerThread for the context.
|
// Methods specific to any WorkerThread for the context.
|
||||||
frontend::CompileError &addPendingCompileError();
|
frontend::CompileError &addPendingCompileError();
|
||||||
void addPendingOverRecursed();
|
void addPendingOverRecursed();
|
||||||
@ -1040,7 +1029,7 @@ class AutoLockForExclusiveAccess
|
|||||||
void init(JSRuntime *rt) {
|
void init(JSRuntime *rt) {
|
||||||
runtime = rt;
|
runtime = rt;
|
||||||
if (runtime->numExclusiveThreads) {
|
if (runtime->numExclusiveThreads) {
|
||||||
runtime->assertCanLock(JSRuntime::ExclusiveAccessLock);
|
runtime->assertCanLock(ExclusiveAccessLock);
|
||||||
PR_Lock(runtime->exclusiveAccessLock);
|
PR_Lock(runtime->exclusiveAccessLock);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
||||||
@ -1095,7 +1084,7 @@ class AutoLockForCompilation
|
|||||||
void init(JSRuntime *rt) {
|
void init(JSRuntime *rt) {
|
||||||
runtime = rt;
|
runtime = rt;
|
||||||
if (runtime->numCompilationThreads) {
|
if (runtime->numCompilationThreads) {
|
||||||
runtime->assertCanLock(JSRuntime::CompilationLock);
|
runtime->assertCanLock(CompilationLock);
|
||||||
PR_Lock(runtime->compilationLock);
|
PR_Lock(runtime->compilationLock);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
runtime->compilationLockOwner = PR_GetCurrentThread();
|
runtime->compilationLockOwner = PR_GetCurrentThread();
|
||||||
|
@ -1713,9 +1713,9 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
|||||||
mozilla::Maybe<AutoLockWorkerThreadState> lock;
|
mozilla::Maybe<AutoLockWorkerThreadState> lock;
|
||||||
JSRuntime *rt = zone->runtimeFromAnyThread();
|
JSRuntime *rt = zone->runtimeFromAnyThread();
|
||||||
if (rt->exclusiveThreadsPresent()) {
|
if (rt->exclusiveThreadsPresent()) {
|
||||||
lock.construct<WorkerThreadState &>(*rt->workerThreadState);
|
lock.construct();
|
||||||
while (rt->isHeapBusy())
|
while (rt->isHeapBusy())
|
||||||
rt->workerThreadState->wait(WorkerThreadState::PRODUCER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
void *thing = cx->allocator()->arenas.allocateFromArenaInline(zone, thingKind);
|
||||||
@ -4300,7 +4300,7 @@ AutoTraceSession::AutoTraceSession(JSRuntime *rt, js::HeapState heapState)
|
|||||||
// Lock the worker thread state when changing the heap state in the
|
// Lock the worker thread state when changing the heap state in the
|
||||||
// presence of exclusive threads, to avoid racing with refillFreeList.
|
// presence of exclusive threads, to avoid racing with refillFreeList.
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
AutoLockWorkerThreadState lock;
|
||||||
rt->heapState = heapState;
|
rt->heapState = heapState;
|
||||||
#else
|
#else
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
@ -4316,11 +4316,11 @@ AutoTraceSession::~AutoTraceSession()
|
|||||||
|
|
||||||
if (runtime->exclusiveThreadsPresent()) {
|
if (runtime->exclusiveThreadsPresent()) {
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
AutoLockWorkerThreadState lock(*runtime->workerThreadState);
|
AutoLockWorkerThreadState lock;
|
||||||
runtime->heapState = prevState;
|
runtime->heapState = prevState;
|
||||||
|
|
||||||
// Notify any worker threads waiting for the trace session to end.
|
// Notify any worker threads waiting for the trace session to end.
|
||||||
runtime->workerThreadState->notifyAll(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
#else
|
#else
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
#endif
|
#endif
|
||||||
|
@ -1304,8 +1304,8 @@ ScriptSource::setSourceCopy(ExclusiveContext *cx, const jschar *src, uint32_t le
|
|||||||
// progress on our compression task.
|
// progress on our compression task.
|
||||||
const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
|
const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
|
||||||
if (length < HUGE_SCRIPT &&
|
if (length < HUGE_SCRIPT &&
|
||||||
cx->cpuCount() > 1 &&
|
WorkerThreadState().cpuCount > 1 &&
|
||||||
cx->workerThreadCount() >= 2)
|
WorkerThreadState().threadCount >= 2)
|
||||||
{
|
{
|
||||||
task->ss = this;
|
task->ss = this;
|
||||||
task->chars = src;
|
task->chars = src;
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#include "prmjtime.h"
|
#include "prmjtime.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "jit/ExecutionModeInlines.h"
|
|
||||||
#include "jit/IonBuilder.h"
|
#include "jit/IonBuilder.h"
|
||||||
#include "vm/Debugger.h"
|
#include "vm/Debugger.h"
|
||||||
|
|
||||||
@ -28,31 +27,37 @@ using namespace js;
|
|||||||
using mozilla::ArrayLength;
|
using mozilla::ArrayLength;
|
||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
GlobalWorkerThreadState gWorkerThreadState;
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
|
js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
|
||||||
{
|
{
|
||||||
// If 'cx' is not a JSContext, we are already off the main thread and the
|
// If 'cx' is not a JSContext, we are already off the main thread and the
|
||||||
// worker threads would have already been initialized.
|
// worker threads would have already been initialized.
|
||||||
if (!cx->isJSContext()) {
|
if (!cx->isJSContext())
|
||||||
JS_ASSERT(cx->workerThreadState() != nullptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSRuntime *rt = cx->asJSContext()->runtime();
|
|
||||||
if (rt->workerThreadState)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
rt->workerThreadState = rt->new_<WorkerThreadState>(rt);
|
return WorkerThreadState().ensureInitialized();
|
||||||
if (!rt->workerThreadState)
|
}
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!rt->workerThreadState->init()) {
|
static size_t
|
||||||
js_delete(rt->workerThreadState);
|
ThreadCountForCPUCount(size_t cpuCount)
|
||||||
rt->workerThreadState = nullptr;
|
{
|
||||||
return false;
|
return Max(cpuCount, (size_t)2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
void
|
||||||
|
js::SetFakeCPUCount(size_t count)
|
||||||
|
{
|
||||||
|
// This must be called before the threads have been initialized.
|
||||||
|
JS_ASSERT(!WorkerThreadState().threads);
|
||||||
|
|
||||||
|
WorkerThreadState().cpuCount = count;
|
||||||
|
WorkerThreadState().threadCount = ThreadCountForCPUCount(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
@ -61,23 +66,19 @@ bool
|
|||||||
js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
|
js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
|
||||||
{
|
{
|
||||||
// Threads already initialized by the AsmJS compiler.
|
// Threads already initialized by the AsmJS compiler.
|
||||||
JS_ASSERT(cx->workerThreadState() != nullptr);
|
|
||||||
JS_ASSERT(asmData->mir);
|
JS_ASSERT(asmData->mir);
|
||||||
JS_ASSERT(asmData->lir == nullptr);
|
JS_ASSERT(asmData->lir == nullptr);
|
||||||
|
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
AutoLockWorkerThreadState lock;
|
||||||
JS_ASSERT(state.numThreads);
|
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
// Don't append this task if another failed.
|
// Don't append this task if another failed.
|
||||||
if (state.asmJSWorkerFailed())
|
if (WorkerThreadState().asmJSWorkerFailed())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!state.asmJSWorklist.append(asmData))
|
if (!WorkerThreadState().asmJSWorklist().append(asmData))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.notifyOne(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,33 +88,26 @@ js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
|
|||||||
if (!EnsureWorkerThreadsInitialized(cx))
|
if (!EnsureWorkerThreadsInitialized(cx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorkerThreadState &state = *cx->runtime()->workerThreadState;
|
AutoLockWorkerThreadState lock;
|
||||||
JS_ASSERT(state.numThreads);
|
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
if (!WorkerThreadState().ionWorklist().append(builder))
|
||||||
|
|
||||||
if (!state.ionWorklist.append(builder))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cx->runtime()->addCompilationThread();
|
cx->runtime()->addCompilationThread();
|
||||||
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Move an IonBuilder for which compilation has either finished, failed, or
|
* Move an IonBuilder for which compilation has either finished, failed, or
|
||||||
* been cancelled into the Ion compartment's finished compilations list.
|
* been cancelled into the global finished compilation list. All off thread
|
||||||
* All off thread compilations which are started must eventually be finished.
|
* compilations which are started must eventually be finished.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
FinishOffThreadIonCompile(jit::IonBuilder *builder)
|
FinishOffThreadIonCompile(jit::IonBuilder *builder)
|
||||||
{
|
{
|
||||||
JSCompartment *compartment = builder->script()->compartment();
|
WorkerThreadState().ionFinishedList().append(builder);
|
||||||
JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState);
|
|
||||||
JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState->isLocked());
|
|
||||||
|
|
||||||
compartment->jitCompartment()->finishedOffThreadCompilations().append(builder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // JS_ION
|
#endif // JS_ION
|
||||||
@ -130,49 +124,43 @@ void
|
|||||||
js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
||||||
{
|
{
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
JSRuntime *rt = compartment->runtimeFromMainThread();
|
|
||||||
|
|
||||||
if (!rt->workerThreadState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WorkerThreadState &state = *rt->workerThreadState;
|
|
||||||
|
|
||||||
jit::JitCompartment *jitComp = compartment->jitCompartment();
|
jit::JitCompartment *jitComp = compartment->jitCompartment();
|
||||||
if (!jitComp)
|
if (!jitComp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
|
if (!WorkerThreadState().threads)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Cancel any pending entries for which processing hasn't started. */
|
/* Cancel any pending entries for which processing hasn't started. */
|
||||||
for (size_t i = 0; i < state.ionWorklist.length(); i++) {
|
GlobalWorkerThreadState::IonBuilderVector &worklist = WorkerThreadState().ionWorklist();
|
||||||
jit::IonBuilder *builder = state.ionWorklist[i];
|
for (size_t i = 0; i < worklist.length(); i++) {
|
||||||
|
jit::IonBuilder *builder = worklist[i];
|
||||||
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
||||||
FinishOffThreadIonCompile(builder);
|
FinishOffThreadIonCompile(builder);
|
||||||
state.ionWorklist[i--] = state.ionWorklist.back();
|
WorkerThreadState().remove(worklist, &i);
|
||||||
state.ionWorklist.popBack();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for in progress entries to finish up. */
|
/* Wait for in progress entries to finish up. */
|
||||||
for (size_t i = 0; i < state.numThreads; i++) {
|
for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
|
||||||
const WorkerThread &helper = state.threads[i];
|
const WorkerThread &helper = WorkerThreadState().threads[i];
|
||||||
while (helper.ionBuilder &&
|
while (helper.ionBuilder &&
|
||||||
CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
|
CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
|
||||||
{
|
{
|
||||||
helper.ionBuilder->cancel();
|
helper.ionBuilder->cancel();
|
||||||
state.wait(WorkerThreadState::CONSUMER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jit::OffThreadCompilationVector &compilations = jitComp->finishedOffThreadCompilations();
|
|
||||||
|
|
||||||
/* Cancel code generation for any completed entries. */
|
/* Cancel code generation for any completed entries. */
|
||||||
for (size_t i = 0; i < compilations.length(); i++) {
|
GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
|
||||||
jit::IonBuilder *builder = compilations[i];
|
for (size_t i = 0; i < finished.length(); i++) {
|
||||||
|
jit::IonBuilder *builder = finished[i];
|
||||||
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
||||||
jit::FinishOffThreadBuilder(builder);
|
jit::FinishOffThreadBuilder(builder);
|
||||||
compilations[i--] = compilations.back();
|
WorkerThreadState().remove(finished, &i);
|
||||||
compilations.popBack();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // JS_ION
|
#endif // JS_ION
|
||||||
@ -237,6 +225,55 @@ ParseTask::~ParseTask()
|
|||||||
js_delete(errors[i]);
|
js_delete(errors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::CancelOffThreadParses(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
|
if (!WorkerThreadState().threads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Instead of forcibly canceling pending parse tasks, just wait for all scheduled
|
||||||
|
// and in progress ones to complete. Otherwise the final GC may not collect
|
||||||
|
// everything due to zones being used off thread.
|
||||||
|
while (true) {
|
||||||
|
bool pending = false;
|
||||||
|
GlobalWorkerThreadState::ParseTaskVector &worklist = WorkerThreadState().parseWorklist();
|
||||||
|
for (size_t i = 0; i < worklist.length(); i++) {
|
||||||
|
ParseTask *task = worklist[i];
|
||||||
|
if (task->runtimeMatches(rt))
|
||||||
|
pending = true;
|
||||||
|
}
|
||||||
|
if (!pending) {
|
||||||
|
bool inProgress = false;
|
||||||
|
for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
|
||||||
|
ParseTask *task = WorkerThreadState().threads[i].parseTask;
|
||||||
|
if (task && task->runtimeMatches(rt))
|
||||||
|
inProgress = true;
|
||||||
|
}
|
||||||
|
if (!inProgress)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up any parse tasks which haven't been finished by the main thread.
|
||||||
|
GlobalWorkerThreadState::ParseTaskVector &finished = WorkerThreadState().parseFinishedList();
|
||||||
|
while (true) {
|
||||||
|
bool found = false;
|
||||||
|
for (size_t i = 0; i < finished.length(); i++) {
|
||||||
|
ParseTask *task = finished[i];
|
||||||
|
if (task->runtimeMatches(rt)) {
|
||||||
|
found = true;
|
||||||
|
AutoUnlockWorkerThreadState unlock;
|
||||||
|
WorkerThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
|
js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
@ -316,21 +353,19 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
|||||||
if (!task->init(cx, options))
|
if (!task->init(cx, options))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorkerThreadState &state = *cx->runtime()->workerThreadState;
|
|
||||||
JS_ASSERT(state.numThreads);
|
|
||||||
|
|
||||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||||
if (!state.parseWaitingOnGC.append(task.get()))
|
AutoLockWorkerThreadState lock;
|
||||||
|
if (!WorkerThreadState().parseWaitingOnGC().append(task.get()))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
task->activate(cx->runtime());
|
task->activate(cx->runtime());
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
if (!state.parseWorklist.append(task.get()))
|
if (!WorkerThreadState().parseWorklist().append(task.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
task.forget();
|
task.forget();
|
||||||
@ -343,45 +378,35 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime *rt)
|
|||||||
{
|
{
|
||||||
JS_ASSERT(!OffThreadParsingMustWaitForGC(rt));
|
JS_ASSERT(!OffThreadParsingMustWaitForGC(rt));
|
||||||
|
|
||||||
if (!rt->workerThreadState || rt->workerThreadState->parseWaitingOnGC.empty())
|
GlobalWorkerThreadState::ParseTaskVector newTasks;
|
||||||
|
{
|
||||||
|
AutoLockWorkerThreadState lock;
|
||||||
|
GlobalWorkerThreadState::ParseTaskVector &waiting = WorkerThreadState().parseWaitingOnGC();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < waiting.length(); i++) {
|
||||||
|
ParseTask *task = waiting[i];
|
||||||
|
if (task->runtimeMatches(rt)) {
|
||||||
|
newTasks.append(task);
|
||||||
|
WorkerThreadState().remove(waiting, &i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newTasks.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// This logic should mirror the contents of the !activeGCInAtomsZone()
|
// This logic should mirror the contents of the !activeGCInAtomsZone()
|
||||||
// branch in StartOffThreadParseScript:
|
// branch in StartOffThreadParseScript:
|
||||||
|
|
||||||
WorkerThreadState &state = *rt->workerThreadState;
|
for (size_t i = 0; i < newTasks.length(); i++)
|
||||||
|
newTasks[i]->activate(rt);
|
||||||
|
|
||||||
for (size_t i = 0; i < state.parseWaitingOnGC.length(); i++)
|
AutoLockWorkerThreadState lock;
|
||||||
state.parseWaitingOnGC[i]->activate(rt);
|
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
for (size_t i = 0; i < newTasks.length(); i++)
|
||||||
|
WorkerThreadState().parseWorklist().append(newTasks[i]);
|
||||||
|
|
||||||
JS_ASSERT(state.parseWorklist.empty());
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
state.parseWorklist.swap(state.parseWaitingOnGC);
|
|
||||||
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
|
|
||||||
{
|
|
||||||
if (!rt->workerThreadState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WorkerThreadState &state = *rt->workerThreadState;
|
|
||||||
|
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (state.parseWorklist.empty()) {
|
|
||||||
bool parseInProgress = false;
|
|
||||||
for (size_t i = 0; i < state.numThreads; i++)
|
|
||||||
parseInProgress |= !!state.threads[i].parseTask;
|
|
||||||
if (!parseInProgress)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state.wait(WorkerThreadState::CONSUMER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
@ -396,39 +421,26 @@ static const uint32_t WORKER_STACK_SIZE = 512 * 1024;
|
|||||||
static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
|
static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::init()
|
GlobalWorkerThreadState::ensureInitialized()
|
||||||
{
|
{
|
||||||
JS_ASSERT(numThreads == 0);
|
JS_ASSERT(this == &WorkerThreadState());
|
||||||
|
AutoLockWorkerThreadState lock;
|
||||||
|
|
||||||
if (!runtime->useHelperThreads())
|
if (threads)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
workerLock = PR_NewLock();
|
threads = js_pod_calloc<WorkerThread>(threadCount);
|
||||||
if (!workerLock)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
consumerWakeup = PR_NewCondVar(workerLock);
|
|
||||||
if (!consumerWakeup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
producerWakeup = PR_NewCondVar(workerLock);
|
|
||||||
if (!producerWakeup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
threads = (WorkerThread*) js_pod_calloc<WorkerThread>(runtime->workerThreadCount());
|
|
||||||
if (!threads)
|
if (!threads)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (size_t i = 0; i < runtime->workerThreadCount(); i++) {
|
for (size_t i = 0; i < threadCount; i++) {
|
||||||
WorkerThread &helper = threads[i];
|
WorkerThread &helper = threads[i];
|
||||||
helper.runtime = runtime;
|
helper.threadData.construct(static_cast<JSRuntime *>(nullptr));
|
||||||
helper.threadData.construct(runtime);
|
|
||||||
helper.threadData.ref().addToThreadList();
|
|
||||||
helper.thread = PR_CreateThread(PR_USER_THREAD,
|
helper.thread = PR_CreateThread(PR_USER_THREAD,
|
||||||
WorkerThread::ThreadMain, &helper,
|
WorkerThread::ThreadMain, &helper,
|
||||||
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, WORKER_STACK_SIZE);
|
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, WORKER_STACK_SIZE);
|
||||||
if (!helper.thread || !helper.threadData.ref().init()) {
|
if (!helper.thread || !helper.threadData.ref().init()) {
|
||||||
for (size_t j = 0; j < runtime->workerThreadCount(); j++)
|
for (size_t j = 0; j < threadCount; j++)
|
||||||
threads[j].destroy();
|
threads[j].destroy();
|
||||||
js_free(threads);
|
js_free(threads);
|
||||||
threads = nullptr;
|
threads = nullptr;
|
||||||
@ -436,50 +448,29 @@ WorkerThreadState::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
numThreads = runtime->workerThreadCount();
|
|
||||||
resetAsmJSFailureState();
|
resetAsmJSFailureState();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
GlobalWorkerThreadState::GlobalWorkerThreadState()
|
||||||
WorkerThreadState::cleanup()
|
|
||||||
{
|
{
|
||||||
// Do preparatory work for shutdown before the final GC has destroyed most
|
mozilla::PodZero(this);
|
||||||
// of the GC heap.
|
|
||||||
|
|
||||||
// Join created threads, to ensure there is no in progress work.
|
cpuCount = GetCPUCount();
|
||||||
if (threads) {
|
threadCount = ThreadCountForCPUCount(cpuCount);
|
||||||
for (size_t i = 0; i < numThreads; i++)
|
|
||||||
threads[i].destroy();
|
|
||||||
js_free(threads);
|
|
||||||
threads = nullptr;
|
|
||||||
numThreads = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up any parse tasks which haven't been finished yet.
|
MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
|
||||||
while (!parseFinishedList.empty())
|
|
||||||
finishParseTask(/* maybecx = */ nullptr, runtime, parseFinishedList[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkerThreadState::~WorkerThreadState()
|
workerLock = PR_NewLock();
|
||||||
{
|
consumerWakeup = PR_NewCondVar(workerLock);
|
||||||
JS_ASSERT(!threads);
|
producerWakeup = PR_NewCondVar(workerLock);
|
||||||
JS_ASSERT(parseFinishedList.empty());
|
|
||||||
|
|
||||||
if (workerLock)
|
|
||||||
PR_DestroyLock(workerLock);
|
|
||||||
|
|
||||||
if (consumerWakeup)
|
|
||||||
PR_DestroyCondVar(consumerWakeup);
|
|
||||||
|
|
||||||
if (producerWakeup)
|
|
||||||
PR_DestroyCondVar(producerWakeup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::lock()
|
GlobalWorkerThreadState::lock()
|
||||||
{
|
{
|
||||||
runtime->assertCanLock(JSRuntime::WorkerThreadStateLock);
|
JS_ASSERT(!isLocked());
|
||||||
|
AssertCurrentThreadCanLock(WorkerThreadStateLock);
|
||||||
PR_Lock(workerLock);
|
PR_Lock(workerLock);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
lockOwner = PR_GetCurrentThread();
|
lockOwner = PR_GetCurrentThread();
|
||||||
@ -487,7 +478,7 @@ WorkerThreadState::lock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::unlock()
|
GlobalWorkerThreadState::unlock()
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -498,14 +489,14 @@ WorkerThreadState::unlock()
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::isLocked()
|
GlobalWorkerThreadState::isLocked()
|
||||||
{
|
{
|
||||||
return lockOwner == PR_GetCurrentThread();
|
return lockOwner == PR_GetCurrentThread();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::wait(CondVar which, uint32_t millis)
|
GlobalWorkerThreadState::wait(CondVar which, uint32_t millis)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -521,37 +512,37 @@ WorkerThreadState::wait(CondVar which, uint32_t millis)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::notifyAll(CondVar which)
|
GlobalWorkerThreadState::notifyAll(CondVar which)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThreadState::notifyOne(CondVar which)
|
GlobalWorkerThreadState::notifyOne(CondVar which)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
PR_NotifyCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
PR_NotifyCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::canStartAsmJSCompile()
|
GlobalWorkerThreadState::canStartAsmJSCompile()
|
||||||
{
|
{
|
||||||
// Don't execute an AsmJS job if an earlier one failed.
|
// Don't execute an AsmJS job if an earlier one failed.
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
return (!asmJSWorklist.empty() && !numAsmJSFailedJobs);
|
return (!asmJSWorklist().empty() && !numAsmJSFailedJobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::canStartIonCompile()
|
GlobalWorkerThreadState::canStartIonCompile()
|
||||||
{
|
{
|
||||||
// A worker thread can begin an Ion compilation if (a) there is some script
|
// A worker thread can begin an Ion compilation if (a) there is some script
|
||||||
// which is waiting to be compiled, and (b) no other worker thread is
|
// which is waiting to be compiled, and (b) no other worker thread is
|
||||||
// currently compiling a script. The latter condition ensures that two
|
// currently compiling a script. The latter condition ensures that two
|
||||||
// compilations cannot simultaneously occur.
|
// compilations cannot simultaneously occur.
|
||||||
if (ionWorklist.empty())
|
if (ionWorklist().empty())
|
||||||
return false;
|
return false;
|
||||||
for (size_t i = 0; i < numThreads; i++) {
|
for (size_t i = 0; i < threadCount; i++) {
|
||||||
if (threads[i].ionBuilder)
|
if (threads[i].ionBuilder)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -559,16 +550,16 @@ WorkerThreadState::canStartIonCompile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::canStartParseTask()
|
GlobalWorkerThreadState::canStartParseTask()
|
||||||
{
|
{
|
||||||
// Don't allow simultaneous off thread parses, to reduce contention on the
|
// Don't allow simultaneous off thread parses, to reduce contention on the
|
||||||
// atoms table. Note that asm.js compilation depends on this to avoid
|
// atoms table. Note that asm.js compilation depends on this to avoid
|
||||||
// stalling the worker thread, as off thread parse tasks can trigger and
|
// stalling the worker thread, as off thread parse tasks can trigger and
|
||||||
// block on other off thread asm.js compilation tasks.
|
// block on other off thread asm.js compilation tasks.
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
if (parseWorklist.empty())
|
if (parseWorklist().empty())
|
||||||
return false;
|
return false;
|
||||||
for (size_t i = 0; i < numThreads; i++) {
|
for (size_t i = 0; i < threadCount; i++) {
|
||||||
if (threads[i].parseTask)
|
if (threads[i].parseTask)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -576,9 +567,9 @@ WorkerThreadState::canStartParseTask()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::canStartCompressionTask()
|
GlobalWorkerThreadState::canStartCompressionTask()
|
||||||
{
|
{
|
||||||
return !compressionWorklist.empty();
|
return !compressionWorklist().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -609,19 +600,19 @@ CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSScript *
|
JSScript *
|
||||||
WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
|
GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
|
||||||
{
|
{
|
||||||
ParseTask *parseTask = nullptr;
|
ParseTask *parseTask = nullptr;
|
||||||
|
|
||||||
// The token is a ParseTask* which should be in the finished list.
|
// The token is a ParseTask* which should be in the finished list.
|
||||||
// Find and remove its entry.
|
// Find and remove its entry.
|
||||||
{
|
{
|
||||||
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
AutoLockWorkerThreadState lock;
|
||||||
for (size_t i = 0; i < parseFinishedList.length(); i++) {
|
ParseTaskVector &finished = parseFinishedList();
|
||||||
if (parseFinishedList[i] == token) {
|
for (size_t i = 0; i < finished.length(); i++) {
|
||||||
parseTask = parseFinishedList[i];
|
if (finished[i] == token) {
|
||||||
parseFinishedList[i] = parseFinishedList.back();
|
parseTask = finished[i];
|
||||||
parseFinishedList.popBack();
|
remove(finished, &i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,24 +682,20 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
|
|||||||
void
|
void
|
||||||
WorkerThread::destroy()
|
WorkerThread::destroy()
|
||||||
{
|
{
|
||||||
WorkerThreadState &state = *runtime->workerThreadState;
|
|
||||||
|
|
||||||
if (thread) {
|
if (thread) {
|
||||||
{
|
{
|
||||||
AutoLockWorkerThreadState lock(state);
|
AutoLockWorkerThreadState lock;
|
||||||
terminate = true;
|
terminate = true;
|
||||||
|
|
||||||
/* Notify all workers, to ensure that this thread wakes up. */
|
/* Notify all workers, to ensure that this thread wakes up. */
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
PR_JoinThread(thread);
|
PR_JoinThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!threadData.empty()) {
|
if (!threadData.empty())
|
||||||
threadData.ref().removeFromThreadList();
|
|
||||||
threadData.destroy();
|
threadData.destroy();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
@ -720,19 +707,23 @@ WorkerThread::ThreadMain(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
WorkerThread::handleAsmJSWorkload()
|
||||||
{
|
{
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
JS_ASSERT(state.isLocked());
|
JS_ASSERT(WorkerThreadState().isLocked());
|
||||||
JS_ASSERT(state.canStartAsmJSCompile());
|
JS_ASSERT(WorkerThreadState().canStartAsmJSCompile());
|
||||||
JS_ASSERT(idle());
|
JS_ASSERT(idle());
|
||||||
|
|
||||||
asmData = state.asmJSWorklist.popCopy();
|
asmData = WorkerThreadState().asmJSWorklist().popCopy();
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
state.unlock();
|
|
||||||
do {
|
do {
|
||||||
jit::IonContext icx(jit::CompileRuntime::get(runtime), asmData->mir->compartment, &asmData->mir->alloc());
|
AutoUnlockWorkerThreadState unlock;
|
||||||
|
PerThreadData::AutoEnterRuntime enter(threadData.addr(), asmData->runtime);
|
||||||
|
|
||||||
|
jit::IonContext icx(asmData->mir->compartment->runtime(),
|
||||||
|
asmData->mir->compartment,
|
||||||
|
&asmData->mir->alloc());
|
||||||
|
|
||||||
int64_t before = PRMJ_Now();
|
int64_t before = PRMJ_Now();
|
||||||
|
|
||||||
@ -748,38 +739,35 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
|||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
state.lock();
|
|
||||||
|
|
||||||
// On failure, signal parent for harvesting in CancelOutstandingJobs().
|
// On failure, signal parent for harvesting in CancelOutstandingJobs().
|
||||||
if (!success) {
|
if (!success) {
|
||||||
state.noteAsmJSFailure(asmData->func);
|
WorkerThreadState().noteAsmJSFailure(asmData->func);
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||||
asmData = nullptr;
|
asmData = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On success, move work to the finished list.
|
// On success, move work to the finished list.
|
||||||
state.asmJSFinishedList.append(asmData);
|
WorkerThreadState().asmJSFinishedList().append(asmData);
|
||||||
asmData = nullptr;
|
asmData = nullptr;
|
||||||
|
|
||||||
// Notify the main thread in case it's blocked waiting for a LifoAlloc.
|
// Notify the main thread in case it's blocked waiting for a LifoAlloc.
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||||
#else
|
#else
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
#endif // JS_ION
|
#endif // JS_ION
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
WorkerThread::handleIonWorkload()
|
||||||
{
|
{
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
JS_ASSERT(state.isLocked());
|
JS_ASSERT(WorkerThreadState().isLocked());
|
||||||
JS_ASSERT(state.canStartIonCompile());
|
JS_ASSERT(WorkerThreadState().canStartIonCompile());
|
||||||
JS_ASSERT(idle());
|
JS_ASSERT(idle());
|
||||||
|
|
||||||
ionBuilder = state.ionWorklist.popCopy();
|
ionBuilder = WorkerThreadState().ionWorklist().popCopy();
|
||||||
|
|
||||||
DebugOnly<ExecutionMode> executionMode = ionBuilder->info().executionMode();
|
|
||||||
|
|
||||||
#if JS_TRACE_LOGGING
|
#if JS_TRACE_LOGGING
|
||||||
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
|
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
|
||||||
@ -788,9 +776,13 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
|||||||
ionBuilder->script());
|
ionBuilder->script());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
state.unlock();
|
JSRuntime *rt = ionBuilder->script()->compartment()->runtimeFromAnyThread();
|
||||||
|
|
||||||
{
|
{
|
||||||
jit::IonContext ictx(jit::CompileRuntime::get(runtime),
|
AutoUnlockWorkerThreadState unlock;
|
||||||
|
PerThreadData::AutoEnterRuntime enter(threadData.addr(),
|
||||||
|
ionBuilder->script()->runtimeFromAnyThread());
|
||||||
|
jit::IonContext ictx(jit::CompileRuntime::get(rt),
|
||||||
jit::CompileCompartment::get(ionBuilder->script()->compartment()),
|
jit::CompileCompartment::get(ionBuilder->script()->compartment()),
|
||||||
&ionBuilder->alloc());
|
&ionBuilder->alloc());
|
||||||
AutoEnterIonCompilation ionCompiling;
|
AutoEnterIonCompilation ionCompiling;
|
||||||
@ -799,7 +791,6 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
|||||||
if (succeeded)
|
if (succeeded)
|
||||||
ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
|
ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
|
||||||
}
|
}
|
||||||
state.lock();
|
|
||||||
|
|
||||||
FinishOffThreadIonCompile(ionBuilder);
|
FinishOffThreadIonCompile(ionBuilder);
|
||||||
ionBuilder = nullptr;
|
ionBuilder = nullptr;
|
||||||
@ -808,10 +799,10 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
|||||||
// at the next operation callback. Don't interrupt Ion code for this, as
|
// at the next operation callback. Don't interrupt Ion code for this, as
|
||||||
// this incorporation can be delayed indefinitely without affecting
|
// this incorporation can be delayed indefinitely without affecting
|
||||||
// performance as long as the main thread is actually executing Ion code.
|
// performance as long as the main thread is actually executing Ion code.
|
||||||
runtime->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon);
|
rt->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon);
|
||||||
|
|
||||||
// Notify the main thread in case it is waiting for the compilation to finish.
|
// Notify the main thread in case it is waiting for the compilation to finish.
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||||
#else
|
#else
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
#endif // JS_ION
|
#endif // JS_ION
|
||||||
@ -843,17 +834,19 @@ ExclusiveContext::addPendingOverRecursed()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThread::handleParseWorkload(WorkerThreadState &state)
|
WorkerThread::handleParseWorkload()
|
||||||
{
|
{
|
||||||
JS_ASSERT(state.isLocked());
|
JS_ASSERT(WorkerThreadState().isLocked());
|
||||||
JS_ASSERT(state.canStartParseTask());
|
JS_ASSERT(WorkerThreadState().canStartParseTask());
|
||||||
JS_ASSERT(idle());
|
JS_ASSERT(idle());
|
||||||
|
|
||||||
parseTask = state.parseWorklist.popCopy();
|
parseTask = WorkerThreadState().parseWorklist().popCopy();
|
||||||
parseTask->cx->setWorkerThread(this);
|
parseTask->cx->setWorkerThread(this);
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoUnlockWorkerThreadState unlock(runtime);
|
AutoUnlockWorkerThreadState unlock;
|
||||||
|
PerThreadData::AutoEnterRuntime enter(threadData.addr(),
|
||||||
|
parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
|
||||||
parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
|
parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
|
||||||
NullPtr(), NullPtr(),
|
NullPtr(), NullPtr(),
|
||||||
parseTask->options,
|
parseTask->options,
|
||||||
@ -865,26 +858,26 @@ WorkerThread::handleParseWorkload(WorkerThreadState &state)
|
|||||||
|
|
||||||
// FinishOffThreadScript will need to be called on the script to
|
// FinishOffThreadScript will need to be called on the script to
|
||||||
// migrate it into the correct compartment.
|
// migrate it into the correct compartment.
|
||||||
state.parseFinishedList.append(parseTask);
|
WorkerThreadState().parseFinishedList().append(parseTask);
|
||||||
|
|
||||||
parseTask = nullptr;
|
parseTask = nullptr;
|
||||||
|
|
||||||
// Notify the main thread in case it is waiting for the parse/emit to finish.
|
// Notify the main thread in case it is waiting for the parse/emit to finish.
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerThread::handleCompressionWorkload(WorkerThreadState &state)
|
WorkerThread::handleCompressionWorkload()
|
||||||
{
|
{
|
||||||
JS_ASSERT(state.isLocked());
|
JS_ASSERT(WorkerThreadState().isLocked());
|
||||||
JS_ASSERT(state.canStartCompressionTask());
|
JS_ASSERT(WorkerThreadState().canStartCompressionTask());
|
||||||
JS_ASSERT(idle());
|
JS_ASSERT(idle());
|
||||||
|
|
||||||
compressionTask = state.compressionWorklist.popCopy();
|
compressionTask = WorkerThreadState().compressionWorklist().popCopy();
|
||||||
compressionTask->workerThread = this;
|
compressionTask->workerThread = this;
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoUnlockWorkerThreadState unlock(runtime);
|
AutoUnlockWorkerThreadState unlock;
|
||||||
if (!compressionTask->work())
|
if (!compressionTask->work())
|
||||||
compressionTask->setOOM();
|
compressionTask->setOOM();
|
||||||
}
|
}
|
||||||
@ -893,7 +886,7 @@ WorkerThread::handleCompressionWorkload(WorkerThreadState &state)
|
|||||||
compressionTask = nullptr;
|
compressionTask = nullptr;
|
||||||
|
|
||||||
// Notify the main thread in case it is waiting for the compression to finish.
|
// Notify the main thread in case it is waiting for the compression to finish.
|
||||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -902,25 +895,24 @@ js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
|||||||
if (!EnsureWorkerThreadsInitialized(cx))
|
if (!EnsureWorkerThreadsInitialized(cx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
AutoLockWorkerThreadState lock;
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
if (!state.compressionWorklist.append(task))
|
if (!WorkerThreadState().compressionWorklist().append(task))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerThreadState::compressionInProgress(SourceCompressionTask *task)
|
GlobalWorkerThreadState::compressionInProgress(SourceCompressionTask *task)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
for (size_t i = 0; i < compressionWorklist.length(); i++) {
|
for (size_t i = 0; i < compressionWorklist().length(); i++) {
|
||||||
if (compressionWorklist[i] == task)
|
if (compressionWorklist()[i] == task)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < numThreads; i++) {
|
for (size_t i = 0; i < threadCount; i++) {
|
||||||
if (threads[i].compressionTask == task)
|
if (threads[i].compressionTask == task)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -932,11 +924,10 @@ SourceCompressionTask::complete()
|
|||||||
{
|
{
|
||||||
JS_ASSERT_IF(!ss, !chars);
|
JS_ASSERT_IF(!ss, !chars);
|
||||||
if (active()) {
|
if (active()) {
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
AutoLockWorkerThreadState lock;
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
while (state.compressionInProgress(this))
|
while (WorkerThreadState().compressionInProgress(this))
|
||||||
state.wait(WorkerThreadState::CONSUMER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||||
|
|
||||||
ss->ready_ = true;
|
ss->ready_ = true;
|
||||||
|
|
||||||
@ -955,15 +946,15 @@ SourceCompressionTask::complete()
|
|||||||
}
|
}
|
||||||
|
|
||||||
SourceCompressionTask *
|
SourceCompressionTask *
|
||||||
WorkerThreadState::compressionTaskForSource(ScriptSource *ss)
|
GlobalWorkerThreadState::compressionTaskForSource(ScriptSource *ss)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isLocked());
|
JS_ASSERT(isLocked());
|
||||||
for (size_t i = 0; i < compressionWorklist.length(); i++) {
|
for (size_t i = 0; i < compressionWorklist().length(); i++) {
|
||||||
SourceCompressionTask *task = compressionWorklist[i];
|
SourceCompressionTask *task = compressionWorklist()[i];
|
||||||
if (task->source() == ss)
|
if (task->source() == ss)
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < numThreads; i++) {
|
for (size_t i = 0; i < threadCount; i++) {
|
||||||
SourceCompressionTask *task = threads[i].compressionTask;
|
SourceCompressionTask *task = threads[i].compressionTask;
|
||||||
if (task && task->source() == ss)
|
if (task && task->source() == ss)
|
||||||
return task;
|
return task;
|
||||||
@ -981,12 +972,11 @@ ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerThreadState &state = *cx->workerThreadState();
|
AutoLockWorkerThreadState lock;
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
// Look for a token that hasn't finished compressing and whose source is
|
// Look for a token that hasn't finished compressing and whose source is
|
||||||
// the given ScriptSource.
|
// the given ScriptSource.
|
||||||
if (SourceCompressionTask *task = state.compressionTaskForSource(this))
|
if (SourceCompressionTask *task = WorkerThreadState().compressionTaskForSource(this))
|
||||||
return task->uncompressedChars();
|
return task->uncompressedChars();
|
||||||
|
|
||||||
// Compressing has finished, so this ScriptSource is ready. Avoid future
|
// Compressing has finished, so this ScriptSource is ready. Avoid future
|
||||||
@ -1000,8 +990,7 @@ void
|
|||||||
WorkerThread::threadLoop()
|
WorkerThread::threadLoop()
|
||||||
{
|
{
|
||||||
JS::AutoAssertNoGC nogc;
|
JS::AutoAssertNoGC nogc;
|
||||||
WorkerThreadState &state = *runtime->workerThreadState;
|
AutoLockWorkerThreadState lock;
|
||||||
AutoLockWorkerThreadState lock(state);
|
|
||||||
|
|
||||||
js::TlsPerThreadData.set(threadData.addr());
|
js::TlsPerThreadData.set(threadData.addr());
|
||||||
|
|
||||||
@ -1022,25 +1011,25 @@ WorkerThread::threadLoop()
|
|||||||
while (true) {
|
while (true) {
|
||||||
if (terminate)
|
if (terminate)
|
||||||
return;
|
return;
|
||||||
if (state.canStartIonCompile() ||
|
if (WorkerThreadState().canStartIonCompile() ||
|
||||||
state.canStartAsmJSCompile() ||
|
WorkerThreadState().canStartAsmJSCompile() ||
|
||||||
state.canStartParseTask() ||
|
WorkerThreadState().canStartParseTask() ||
|
||||||
state.canStartCompressionTask())
|
WorkerThreadState().canStartCompressionTask())
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
state.wait(WorkerThreadState::PRODUCER);
|
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch tasks, prioritizing AsmJS work.
|
// Dispatch tasks, prioritizing AsmJS work.
|
||||||
if (state.canStartAsmJSCompile())
|
if (WorkerThreadState().canStartAsmJSCompile())
|
||||||
handleAsmJSWorkload(state);
|
handleAsmJSWorkload();
|
||||||
else if (state.canStartIonCompile())
|
else if (WorkerThreadState().canStartIonCompile())
|
||||||
handleIonWorkload(state);
|
handleIonWorkload();
|
||||||
else if (state.canStartParseTask())
|
else if (WorkerThreadState().canStartParseTask())
|
||||||
handleParseWorkload(state);
|
handleParseWorkload();
|
||||||
else if (state.canStartCompressionTask())
|
else if (WorkerThreadState().canStartCompressionTask())
|
||||||
handleCompressionWorkload(state);
|
handleCompressionWorkload();
|
||||||
else
|
else
|
||||||
MOZ_ASSUME_UNREACHABLE("No task to perform");
|
MOZ_ASSUME_UNREACHABLE("No task to perform");
|
||||||
}
|
}
|
||||||
@ -1071,6 +1060,11 @@ js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::CancelOffThreadParses(JSRuntime *rt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
||||||
const jschar *chars, size_t length, HandleObject scopeChain,
|
const jschar *chars, size_t length, HandleObject scopeChain,
|
||||||
@ -1079,11 +1073,6 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
|||||||
MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
|
MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
||||||
{
|
{
|
||||||
|
@ -33,59 +33,58 @@ namespace jit {
|
|||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
/* Per-runtime state for off thread work items. */
|
// Per-process state for off thread work items.
|
||||||
class WorkerThreadState
|
class GlobalWorkerThreadState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/* Available threads. */
|
// Number of CPUs to treat this machine as having when creating threads.
|
||||||
|
// May be accessed without locking.
|
||||||
|
size_t cpuCount;
|
||||||
|
|
||||||
|
// Number of threads to create. May be accessed without locking.
|
||||||
|
size_t threadCount;
|
||||||
|
|
||||||
|
typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
|
||||||
|
typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
|
||||||
|
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
|
||||||
|
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
|
||||||
|
|
||||||
|
// List of available threads, or null if the thread state has not been initialized.
|
||||||
WorkerThread *threads;
|
WorkerThread *threads;
|
||||||
size_t numThreads;
|
|
||||||
|
|
||||||
enum CondVar {
|
private:
|
||||||
/* For notifying threads waiting for work that they may be able to make progress. */
|
// The lists below are all protected by |lock|.
|
||||||
CONSUMER,
|
|
||||||
|
|
||||||
/* For notifying threads doing work that they may be able to make progress. */
|
// Ion compilation worklist and finished jobs.
|
||||||
PRODUCER
|
IonBuilderVector ionWorklist_, ionFinishedList_;
|
||||||
};
|
|
||||||
|
|
||||||
/* Shared worklist for Ion worker threads. */
|
// AsmJS worklist and finished jobs.
|
||||||
Vector<jit::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
|
//
|
||||||
|
// Simultaneous AsmJS compilations all service the same AsmJS module.
|
||||||
|
// The main thread must pick up finished optimizations and perform codegen.
|
||||||
|
// |asmJSCompilationInProgress| is used to avoid triggering compilations
|
||||||
|
// for more than one module at a time.
|
||||||
|
AsmJSParallelTaskVector asmJSWorklist_, asmJSFinishedList_;
|
||||||
|
|
||||||
/* Worklist for AsmJS worker threads. */
|
public:
|
||||||
Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSWorklist;
|
// For now, only allow a single parallel asm.js compilation to happen at a
|
||||||
|
// time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc.
|
||||||
/*
|
|
||||||
* Finished list for AsmJS worker threads.
|
|
||||||
* Simultaneous AsmJS compilations all service the same AsmJS module.
|
|
||||||
* The main thread must pick up finished optimizations and perform codegen.
|
|
||||||
*/
|
|
||||||
Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSFinishedList;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For now, only allow a single parallel asm.js compilation to happen at a
|
|
||||||
* time. This avoids race conditions on asmJSWorklist/asmJSFinishedList/etc.
|
|
||||||
*/
|
|
||||||
mozilla::Atomic<uint32_t> asmJSCompilationInProgress;
|
mozilla::Atomic<uint32_t> asmJSCompilationInProgress;
|
||||||
|
|
||||||
/* Shared worklist for parsing/emitting scripts on worker threads. */
|
private:
|
||||||
Vector<ParseTask*, 0, SystemAllocPolicy> parseWorklist, parseFinishedList;
|
// Script parsing/emitting worklist and finished jobs.
|
||||||
|
ParseTaskVector parseWorklist_, parseFinishedList_;
|
||||||
|
|
||||||
/* Main-thread-only list of parse tasks waiting for an atoms-zone GC to complete. */
|
// Parse tasks waiting for an atoms-zone GC to complete.
|
||||||
Vector<ParseTask*, 0, SystemAllocPolicy> parseWaitingOnGC;
|
ParseTaskVector parseWaitingOnGC_;
|
||||||
|
|
||||||
/* Worklist for source compression worker threads. */
|
// Source compression worklist.
|
||||||
Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
|
SourceCompressionTaskVector compressionWorklist_;
|
||||||
|
|
||||||
WorkerThreadState(JSRuntime *rt) {
|
public:
|
||||||
mozilla::PodZero(this);
|
GlobalWorkerThreadState();
|
||||||
runtime = rt;
|
|
||||||
}
|
|
||||||
~WorkerThreadState();
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void cleanup();
|
|
||||||
|
|
||||||
|
bool ensureInitialized();
|
||||||
void lock();
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
@ -93,10 +92,62 @@ class WorkerThreadState
|
|||||||
bool isLocked();
|
bool isLocked();
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
enum CondVar {
|
||||||
|
// For notifying threads waiting for work that they may be able to make progress.
|
||||||
|
CONSUMER,
|
||||||
|
|
||||||
|
// For notifying threads doing work that they may be able to make progress.
|
||||||
|
PRODUCER
|
||||||
|
};
|
||||||
|
|
||||||
void wait(CondVar which, uint32_t timeoutMillis = 0);
|
void wait(CondVar which, uint32_t timeoutMillis = 0);
|
||||||
void notifyAll(CondVar which);
|
void notifyAll(CondVar which);
|
||||||
void notifyOne(CondVar which);
|
void notifyOne(CondVar which);
|
||||||
|
|
||||||
|
// Helper method for removing items from the vectors below while iterating over them.
|
||||||
|
template <typename T>
|
||||||
|
void remove(T &vector, size_t *index)
|
||||||
|
{
|
||||||
|
vector[(*index)--] = vector.back();
|
||||||
|
vector.popBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilderVector &ionWorklist() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return ionWorklist_;
|
||||||
|
}
|
||||||
|
IonBuilderVector &ionFinishedList() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return ionFinishedList_;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsmJSParallelTaskVector &asmJSWorklist() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return asmJSWorklist_;
|
||||||
|
}
|
||||||
|
AsmJSParallelTaskVector &asmJSFinishedList() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return asmJSFinishedList_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseTaskVector &parseWorklist() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return parseWorklist_;
|
||||||
|
}
|
||||||
|
ParseTaskVector &parseFinishedList() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return parseFinishedList_;
|
||||||
|
}
|
||||||
|
ParseTaskVector &parseWaitingOnGC() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return parseWaitingOnGC_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceCompressionTaskVector &compressionWorklist() {
|
||||||
|
JS_ASSERT(isLocked());
|
||||||
|
return compressionWorklist_;
|
||||||
|
}
|
||||||
|
|
||||||
bool canStartAsmJSCompile();
|
bool canStartAsmJSCompile();
|
||||||
bool canStartIonCompile();
|
bool canStartIonCompile();
|
||||||
bool canStartParseTask();
|
bool canStartParseTask();
|
||||||
@ -132,8 +183,6 @@ class WorkerThreadState
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
JSRuntime *runtime;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock protecting all mutable shared state accessed by helper threads, and
|
* Lock protecting all mutable shared state accessed by helper threads, and
|
||||||
* used by all condition variables.
|
* used by all condition variables.
|
||||||
@ -161,11 +210,16 @@ class WorkerThreadState
|
|||||||
void *asmJSFailedFunction;
|
void *asmJSFailedFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline GlobalWorkerThreadState &
|
||||||
|
WorkerThreadState()
|
||||||
|
{
|
||||||
|
extern GlobalWorkerThreadState gWorkerThreadState;
|
||||||
|
return gWorkerThreadState;
|
||||||
|
}
|
||||||
|
|
||||||
/* Individual helper thread, one allocated per core. */
|
/* Individual helper thread, one allocated per core. */
|
||||||
struct WorkerThread
|
struct WorkerThread
|
||||||
{
|
{
|
||||||
JSRuntime *runtime;
|
|
||||||
|
|
||||||
mozilla::Maybe<PerThreadData> threadData;
|
mozilla::Maybe<PerThreadData> threadData;
|
||||||
PRThread *thread;
|
PRThread *thread;
|
||||||
|
|
||||||
@ -190,10 +244,10 @@ struct WorkerThread
|
|||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
void handleAsmJSWorkload(WorkerThreadState &state);
|
void handleAsmJSWorkload();
|
||||||
void handleIonWorkload(WorkerThreadState &state);
|
void handleIonWorkload();
|
||||||
void handleParseWorkload(WorkerThreadState &state);
|
void handleParseWorkload();
|
||||||
void handleCompressionWorkload(WorkerThreadState &state);
|
void handleCompressionWorkload();
|
||||||
|
|
||||||
static void ThreadMain(void *arg);
|
static void ThreadMain(void *arg);
|
||||||
void threadLoop();
|
void threadLoop();
|
||||||
@ -203,10 +257,15 @@ struct WorkerThread
|
|||||||
|
|
||||||
/* Methods for interacting with worker threads. */
|
/* Methods for interacting with worker threads. */
|
||||||
|
|
||||||
/* Initialize worker threads unless already initialized. */
|
// Initialize worker threads unless already initialized.
|
||||||
bool
|
bool
|
||||||
EnsureWorkerThreadsInitialized(ExclusiveContext *cx);
|
EnsureWorkerThreadsInitialized(ExclusiveContext *cx);
|
||||||
|
|
||||||
|
// This allows the JS shell to override GetCPUCount() when passed the
|
||||||
|
// --thread-count=N option.
|
||||||
|
void
|
||||||
|
SetFakeCPUCount(size_t count);
|
||||||
|
|
||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
|
|
||||||
/* Perform MIR optimization and LIR generation on a single function. */
|
/* Perform MIR optimization and LIR generation on a single function. */
|
||||||
@ -229,6 +288,10 @@ StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder);
|
|||||||
void
|
void
|
||||||
CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
|
CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
|
||||||
|
|
||||||
|
/* Cancel all scheduled, in progress or finished parses for runtime. */
|
||||||
|
void
|
||||||
|
CancelOffThreadParses(JSRuntime *runtime);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a parse/emit cycle for a stream of source. The characters must stay
|
* Start a parse/emit cycle for a stream of source. The characters must stay
|
||||||
* alive until the compilation finishes.
|
* alive until the compilation finishes.
|
||||||
@ -245,10 +308,6 @@ StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
|||||||
void
|
void
|
||||||
EnqueuePendingParseTasksAfterGC(JSRuntime *rt);
|
EnqueuePendingParseTasksAfterGC(JSRuntime *rt);
|
||||||
|
|
||||||
/* Block until in progress and pending off thread parse jobs have finished. */
|
|
||||||
void
|
|
||||||
WaitForOffThreadParsingToFinish(JSRuntime *rt);
|
|
||||||
|
|
||||||
/* Start a compression job for the specified token. */
|
/* Start a compression job for the specified token. */
|
||||||
bool
|
bool
|
||||||
StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
|
StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
|
||||||
@ -258,24 +317,19 @@ class AutoLockWorkerThreadState
|
|||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
WorkerThreadState &state;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AutoLockWorkerThreadState(WorkerThreadState &state
|
AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
||||||
: state(state)
|
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
state.lock();
|
WorkerThreadState().lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
~AutoLockWorkerThreadState() {
|
~AutoLockWorkerThreadState() {
|
||||||
state.unlock();
|
WorkerThreadState().unlock();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public:
|
public:
|
||||||
AutoLockWorkerThreadState(WorkerThreadState &state
|
AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
}
|
}
|
||||||
@ -284,28 +338,22 @@ class AutoLockWorkerThreadState
|
|||||||
|
|
||||||
class AutoUnlockWorkerThreadState
|
class AutoUnlockWorkerThreadState
|
||||||
{
|
{
|
||||||
JSRuntime *rt;
|
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AutoUnlockWorkerThreadState(JSRuntime *rt
|
AutoUnlockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
||||||
: rt(rt)
|
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JS_ASSERT(rt->workerThreadState);
|
WorkerThreadState().unlock();
|
||||||
rt->workerThreadState->unlock();
|
|
||||||
#else
|
|
||||||
(void)this->rt;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
~AutoUnlockWorkerThreadState()
|
~AutoUnlockWorkerThreadState()
|
||||||
{
|
{
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
rt->workerThreadState->lock();
|
WorkerThreadState().lock();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -313,6 +361,7 @@ class AutoUnlockWorkerThreadState
|
|||||||
#ifdef JS_ION
|
#ifdef JS_ION
|
||||||
struct AsmJSParallelTask
|
struct AsmJSParallelTask
|
||||||
{
|
{
|
||||||
|
JSRuntime *runtime; // Associated runtime.
|
||||||
LifoAlloc lifo; // Provider of all heap memory used for compilation.
|
LifoAlloc lifo; // Provider of all heap memory used for compilation.
|
||||||
void *func; // Really, a ModuleCompiler::Func*
|
void *func; // Really, a ModuleCompiler::Func*
|
||||||
jit::MIRGenerator *mir; // Passed from main thread to worker.
|
jit::MIRGenerator *mir; // Passed from main thread to worker.
|
||||||
@ -320,10 +369,11 @@ struct AsmJSParallelTask
|
|||||||
unsigned compileTime;
|
unsigned compileTime;
|
||||||
|
|
||||||
AsmJSParallelTask(size_t defaultChunkSize)
|
AsmJSParallelTask(size_t defaultChunkSize)
|
||||||
: lifo(defaultChunkSize), func(nullptr), mir(nullptr), lir(nullptr), compileTime(0)
|
: runtime(nullptr), lifo(defaultChunkSize), func(nullptr), mir(nullptr), lir(nullptr), compileTime(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void init(void *func, jit::MIRGenerator *mir) {
|
void init(JSRuntime *rt, void *func, jit::MIRGenerator *mir) {
|
||||||
|
this->runtime = rt;
|
||||||
this->func = func;
|
this->func = func;
|
||||||
this->mir = mir;
|
this->mir = mir;
|
||||||
this->lir = nullptr;
|
this->lir = nullptr;
|
||||||
@ -376,6 +426,10 @@ struct ParseTask
|
|||||||
void activate(JSRuntime *rt);
|
void activate(JSRuntime *rt);
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
|
bool runtimeMatches(JSRuntime *rt) {
|
||||||
|
return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
|
||||||
|
}
|
||||||
|
|
||||||
~ParseTask();
|
~ParseTask();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5456,7 +5456,7 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
|||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
int32_t threadCount = op->getIntOption("thread-count");
|
int32_t threadCount = op->getIntOption("thread-count");
|
||||||
if (threadCount >= 0)
|
if (threadCount >= 0)
|
||||||
cx->runtime()->setFakeCPUCount(threadCount);
|
SetFakeCPUCount(threadCount);
|
||||||
#endif /* JS_THREADSAFE */
|
#endif /* JS_THREADSAFE */
|
||||||
|
|
||||||
#if defined(JS_ION)
|
#if defined(JS_ION)
|
||||||
|
@ -91,9 +91,6 @@ PerThreadData::~PerThreadData()
|
|||||||
if (dtoaState)
|
if (dtoaState)
|
||||||
js_DestroyDtoaState(dtoaState);
|
js_DestroyDtoaState(dtoaState);
|
||||||
|
|
||||||
if (isInList())
|
|
||||||
removeFromThreadList();
|
|
||||||
|
|
||||||
#ifdef JS_ARM_SIMULATOR
|
#ifdef JS_ARM_SIMULATOR
|
||||||
js_delete(simulator_);
|
js_delete(simulator_);
|
||||||
#endif
|
#endif
|
||||||
@ -109,22 +106,6 @@ PerThreadData::init()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PerThreadData::addToThreadList()
|
|
||||||
{
|
|
||||||
// PerThreadData which are created/destroyed off the main thread do not
|
|
||||||
// show up in the runtime's thread list.
|
|
||||||
JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
|
|
||||||
runtime_->threadList.insertBack(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PerThreadData::removeFromThreadList()
|
|
||||||
{
|
|
||||||
JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
|
|
||||||
removeFrom(runtime_->threadList);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
|
static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
|
||||||
TransparentObjectWrapper,
|
TransparentObjectWrapper,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -144,7 +125,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
operationCallbackLock(nullptr),
|
operationCallbackLock(nullptr),
|
||||||
operationCallbackOwner(nullptr),
|
operationCallbackOwner(nullptr),
|
||||||
workerThreadState(nullptr),
|
|
||||||
exclusiveAccessLock(nullptr),
|
exclusiveAccessLock(nullptr),
|
||||||
exclusiveAccessOwner(nullptr),
|
exclusiveAccessOwner(nullptr),
|
||||||
mainThreadHasExclusiveAccess(false),
|
mainThreadHasExclusiveAccess(false),
|
||||||
@ -316,11 +296,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
parallelWarmup(0),
|
parallelWarmup(0),
|
||||||
ionReturnOverride_(MagicValue(JS_ARG_POISON)),
|
ionReturnOverride_(MagicValue(JS_ARG_POISON)),
|
||||||
useHelperThreads_(useHelperThreads),
|
useHelperThreads_(useHelperThreads),
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
cpuCount_(GetCPUCount()),
|
|
||||||
#else
|
|
||||||
cpuCount_(1),
|
|
||||||
#endif
|
|
||||||
parallelIonCompilationEnabled_(true),
|
parallelIonCompilationEnabled_(true),
|
||||||
parallelParsingEnabled_(true),
|
parallelParsingEnabled_(true),
|
||||||
isWorkerRuntime_(false)
|
isWorkerRuntime_(false)
|
||||||
@ -328,8 +303,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||||||
, enteredPolicy(nullptr)
|
, enteredPolicy(nullptr)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(cpuCount_ > 0, "GetCPUCount() seems broken");
|
|
||||||
|
|
||||||
liveRuntimesCount++;
|
liveRuntimesCount++;
|
||||||
|
|
||||||
setGCMode(JSGC_MODE_GLOBAL);
|
setGCMode(JSGC_MODE_GLOBAL);
|
||||||
@ -392,7 +365,6 @@ JSRuntime::init(uint32_t maxbytes)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
js::TlsPerThreadData.set(&mainThread);
|
js::TlsPerThreadData.set(&mainThread);
|
||||||
mainThread.addToThreadList();
|
|
||||||
|
|
||||||
if (!threadPool.init())
|
if (!threadPool.init())
|
||||||
return false;
|
return false;
|
||||||
@ -463,15 +435,15 @@ JSRuntime::~JSRuntime()
|
|||||||
/* Free source hook early, as its destructor may want to delete roots. */
|
/* Free source hook early, as its destructor may want to delete roots. */
|
||||||
sourceHook = nullptr;
|
sourceHook = nullptr;
|
||||||
|
|
||||||
/* Off thread compilation and parsing depend on atoms still existing. */
|
/*
|
||||||
|
* Cancel any pending, in progress or completed Ion compilations and
|
||||||
|
* parse tasks. Waiting for AsmJS and compression tasks is done
|
||||||
|
* synchronously (on the main thread or during parse tasks), so no
|
||||||
|
* explicit canceling is needed for these.
|
||||||
|
*/
|
||||||
for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
|
for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next())
|
||||||
CancelOffThreadIonCompile(comp, nullptr);
|
CancelOffThreadIonCompile(comp, nullptr);
|
||||||
WaitForOffThreadParsingToFinish(this);
|
CancelOffThreadParses(this);
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
|
||||||
if (workerThreadState)
|
|
||||||
workerThreadState->cleanup();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Poison common names before final GC. */
|
/* Poison common names before final GC. */
|
||||||
FinishCommonNames(this);
|
FinishCommonNames(this);
|
||||||
@ -504,11 +476,7 @@ JSRuntime::~JSRuntime()
|
|||||||
*/
|
*/
|
||||||
finishSelfHosting();
|
finishSelfHosting();
|
||||||
|
|
||||||
mainThread.removeFromThreadList();
|
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
js_delete(workerThreadState);
|
|
||||||
|
|
||||||
JS_ASSERT(!exclusiveAccessOwner);
|
JS_ASSERT(!exclusiveAccessOwner);
|
||||||
if (exclusiveAccessLock)
|
if (exclusiveAccessLock)
|
||||||
PR_DestroyLock(exclusiveAccessLock);
|
PR_DestroyLock(exclusiveAccessLock);
|
||||||
@ -1010,7 +978,7 @@ JSRuntime::assertCanLock(RuntimeLock which)
|
|||||||
case ExclusiveAccessLock:
|
case ExclusiveAccessLock:
|
||||||
JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
|
JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
|
||||||
case WorkerThreadStateLock:
|
case WorkerThreadStateLock:
|
||||||
JS_ASSERT_IF(workerThreadState, !workerThreadState->isLocked());
|
JS_ASSERT(!WorkerThreadState().isLocked());
|
||||||
case CompilationLock:
|
case CompilationLock:
|
||||||
JS_ASSERT(compilationLockOwner != PR_GetCurrentThread());
|
JS_ASSERT(compilationLockOwner != PR_GetCurrentThread());
|
||||||
case OperationCallbackLock:
|
case OperationCallbackLock:
|
||||||
@ -1082,4 +1050,14 @@ js::CurrentThreadCanReadCompilationData()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::AssertCurrentThreadCanLock(RuntimeLock which)
|
||||||
|
{
|
||||||
|
#ifdef JS_THREADSAFE
|
||||||
|
PerThreadData *pt = TlsPerThreadData.get();
|
||||||
|
if (pt && pt->runtime_)
|
||||||
|
pt->runtime_->assertCanLock(which);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
@ -84,7 +84,6 @@ class Activation;
|
|||||||
class ActivationIterator;
|
class ActivationIterator;
|
||||||
class AsmJSActivation;
|
class AsmJSActivation;
|
||||||
class MathCache;
|
class MathCache;
|
||||||
class WorkerThreadState;
|
|
||||||
|
|
||||||
namespace jit {
|
namespace jit {
|
||||||
class JitRuntime;
|
class JitRuntime;
|
||||||
@ -477,15 +476,31 @@ AtomStateOffsetToName(const JSAtomState &atomState, size_t offset)
|
|||||||
return *(js::FixedHeapPtr<js::PropertyName>*)((char*)&atomState + offset);
|
return *(js::FixedHeapPtr<js::PropertyName>*)((char*)&atomState + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There are several coarse locks in the enum below. These may be either
|
||||||
|
// per-runtime or per-process. When acquiring more than one of these locks,
|
||||||
|
// the acquisition must be done in the order below to avoid deadlocks.
|
||||||
|
enum RuntimeLock {
|
||||||
|
ExclusiveAccessLock,
|
||||||
|
WorkerThreadStateLock,
|
||||||
|
CompilationLock,
|
||||||
|
OperationCallbackLock,
|
||||||
|
GCLock
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void AssertCurrentThreadCanLock(RuntimeLock which);
|
||||||
|
#else
|
||||||
|
inline void AssertCurrentThreadCanLock(RuntimeLock which) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encapsulates portions of the runtime/context that are tied to a
|
* Encapsulates portions of the runtime/context that are tied to a
|
||||||
* single active thread. Normally, as most JS is single-threaded,
|
* single active thread. Instances of this structure can occur for
|
||||||
* there is only one instance of this struct, embedded in the
|
* the main thread as |JSRuntime::mainThread|, for select operations
|
||||||
* JSRuntime as the field |mainThread|. During Parallel JS sections,
|
* performed off thread, such as parsing, and for Parallel JS worker
|
||||||
* however, there will be one instance per worker thread.
|
* threads.
|
||||||
*/
|
*/
|
||||||
class PerThreadData : public PerThreadDataFriendFields,
|
class PerThreadData : public PerThreadDataFriendFields
|
||||||
public mozilla::LinkedListElement<PerThreadData>
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Backpointer to the full shared JSRuntime* with which this
|
* Backpointer to the full shared JSRuntime* with which this
|
||||||
@ -538,6 +553,7 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||||||
friend class js::AsmJSActivation;
|
friend class js::AsmJSActivation;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
friend bool js::CurrentThreadCanReadCompilationData();
|
friend bool js::CurrentThreadCanReadCompilationData();
|
||||||
|
friend void js::AssertCurrentThreadCanLock(RuntimeLock which);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -598,8 +614,6 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||||||
~PerThreadData();
|
~PerThreadData();
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
void addToThreadList();
|
|
||||||
void removeFromThreadList();
|
|
||||||
|
|
||||||
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
||||||
inline JSRuntime *runtimeFromMainThread();
|
inline JSRuntime *runtimeFromMainThread();
|
||||||
@ -609,6 +623,25 @@ class PerThreadData : public PerThreadDataFriendFields,
|
|||||||
inline void addActiveCompilation();
|
inline void addActiveCompilation();
|
||||||
inline void removeActiveCompilation();
|
inline void removeActiveCompilation();
|
||||||
|
|
||||||
|
// For threads which may be associated with different runtimes, depending
|
||||||
|
// on the work they are doing.
|
||||||
|
class AutoEnterRuntime
|
||||||
|
{
|
||||||
|
PerThreadData *pt;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoEnterRuntime(PerThreadData *pt, JSRuntime *rt)
|
||||||
|
: pt(pt)
|
||||||
|
{
|
||||||
|
JS_ASSERT(!pt->runtime_);
|
||||||
|
pt->runtime_ = rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoEnterRuntime() {
|
||||||
|
pt->runtime_ = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef JS_ARM_SIMULATOR
|
#ifdef JS_ARM_SIMULATOR
|
||||||
js::jit::Simulator *simulator() const;
|
js::jit::Simulator *simulator() const;
|
||||||
void setSimulator(js::jit::Simulator *sim);
|
void setSimulator(js::jit::Simulator *sim);
|
||||||
@ -646,12 +679,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
*/
|
*/
|
||||||
js::PerThreadData mainThread;
|
js::PerThreadData mainThread;
|
||||||
|
|
||||||
/*
|
|
||||||
* List of per-thread data in the runtime, including mainThread. Currently
|
|
||||||
* this does not include instances of PerThreadData created for PJS.
|
|
||||||
*/
|
|
||||||
mozilla::LinkedList<js::PerThreadData> threadList;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If non-zero, we were been asked to call the operation callback as soon
|
* If non-zero, we were been asked to call the operation callback as soon
|
||||||
* as possible.
|
* as possible.
|
||||||
@ -668,20 +695,10 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
/* Branch callback */
|
/* Branch callback */
|
||||||
JSOperationCallback operationCallback;
|
JSOperationCallback operationCallback;
|
||||||
|
|
||||||
// There are several per-runtime locks indicated by the enum below. When
|
|
||||||
// acquiring multiple of these locks, the acquisition must be done in the
|
|
||||||
// order below to avoid deadlocks.
|
|
||||||
enum RuntimeLock {
|
|
||||||
ExclusiveAccessLock,
|
|
||||||
WorkerThreadStateLock,
|
|
||||||
CompilationLock,
|
|
||||||
OperationCallbackLock,
|
|
||||||
GCLock
|
|
||||||
};
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void assertCanLock(RuntimeLock which);
|
void assertCanLock(js::RuntimeLock which);
|
||||||
#else
|
#else
|
||||||
void assertCanLock(RuntimeLock which) {}
|
void assertCanLock(js::RuntimeLock which) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -702,7 +719,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
public:
|
public:
|
||||||
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
rt->assertCanLock(JSRuntime::OperationCallbackLock);
|
rt->assertCanLock(js::OperationCallbackLock);
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
PR_Lock(rt->operationCallbackLock);
|
PR_Lock(rt->operationCallbackLock);
|
||||||
rt->operationCallbackOwner = PR_GetCurrentThread();
|
rt->operationCallbackOwner = PR_GetCurrentThread();
|
||||||
@ -733,8 +750,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
|
|
||||||
js::WorkerThreadState *workerThreadState;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* Lock taken when using per-runtime or per-zone data that could otherwise
|
* Lock taken when using per-runtime or per-zone data that could otherwise
|
||||||
@ -1384,7 +1399,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
|
|
||||||
void lockGC() {
|
void lockGC() {
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
assertCanLock(GCLock);
|
assertCanLock(js::GCLock);
|
||||||
PR_Lock(gcLock);
|
PR_Lock(gcLock);
|
||||||
JS_ASSERT(!gcLockOwner);
|
JS_ASSERT(!gcLockOwner);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -1717,7 +1732,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
JSUseHelperThreads useHelperThreads_;
|
JSUseHelperThreads useHelperThreads_;
|
||||||
unsigned cpuCount_;
|
|
||||||
|
|
||||||
// Settings for how helper threads can be used.
|
// Settings for how helper threads can be used.
|
||||||
bool parallelIonCompilationEnabled_;
|
bool parallelIonCompilationEnabled_;
|
||||||
@ -1739,39 +1753,14 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// This allows the JS shell to override GetCPUCount() when passed the
|
|
||||||
// --thread-count=N option.
|
|
||||||
void setFakeCPUCount(size_t count) {
|
|
||||||
cpuCount_ = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a cached value of GetCPUCount() to avoid making the syscall all
|
|
||||||
// the time. Furthermore, this avoids pathological cases where the result of
|
|
||||||
// GetCPUCount() changes during execution.
|
|
||||||
unsigned cpuCount() const {
|
|
||||||
JS_ASSERT(cpuCount_ > 0);
|
|
||||||
return cpuCount_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of worker threads that will be available after
|
|
||||||
// EnsureWorkerThreadsInitialized has been called successfully.
|
|
||||||
unsigned workerThreadCount() const {
|
|
||||||
if (!useHelperThreads())
|
|
||||||
return 0;
|
|
||||||
return js::Max(2u, cpuCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: these values may be toggled dynamically (in response to about:config
|
// Note: these values may be toggled dynamically (in response to about:config
|
||||||
// prefs changing).
|
// prefs changing).
|
||||||
void setParallelIonCompilationEnabled(bool value) {
|
void setParallelIonCompilationEnabled(bool value) {
|
||||||
parallelIonCompilationEnabled_ = value;
|
parallelIonCompilationEnabled_ = value;
|
||||||
}
|
}
|
||||||
bool canUseParallelIonCompilation() const {
|
bool canUseParallelIonCompilation() const {
|
||||||
// Require cpuCount_ > 1 so that Ion compilation jobs and main-thread
|
|
||||||
// execution are not competing for the same resources.
|
|
||||||
return useHelperThreads() &&
|
return useHelperThreads() &&
|
||||||
parallelIonCompilationEnabled_ &&
|
parallelIonCompilationEnabled_;
|
||||||
cpuCount_ > 1;
|
|
||||||
}
|
}
|
||||||
void setParallelParsingEnabled(bool value) {
|
void setParallelParsingEnabled(bool value) {
|
||||||
parallelParsingEnabled_ = value;
|
parallelParsingEnabled_ = value;
|
||||||
|
@ -392,7 +392,7 @@ uint32_t
|
|||||||
ThreadPool::numWorkers() const
|
ThreadPool::numWorkers() const
|
||||||
{
|
{
|
||||||
// Subtract one for the main thread, which always exists.
|
// Subtract one for the main thread, which always exists.
|
||||||
return runtime_->cpuCount() - 1;
|
return WorkerThreadState().cpuCount - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
Loading…
Reference in New Issue
Block a user