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
f419ff98ef
commit
874c41cf84
@ -1406,7 +1406,11 @@ static bool
|
||||
WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setNumber(static_cast<double>(cx->runtime()->workerThreadCount()));
|
||||
#ifdef JS_THREADSAFE
|
||||
args.rval().setInt32(cx->runtime()->useHelperThreads() ? WorkerThreadState().threadCount : 0);
|
||||
#else
|
||||
args.rval().setInt32(0);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5418,20 +5418,20 @@ CheckFunctionsSequential(ModuleCompiler &m)
|
||||
// on rt->workerThreadState->asmJSCompilationInProgress.
|
||||
class ParallelCompilationGuard
|
||||
{
|
||||
WorkerThreadState *parallelState_;
|
||||
bool parallelState_;
|
||||
public:
|
||||
ParallelCompilationGuard() : parallelState_(nullptr) {}
|
||||
ParallelCompilationGuard() : parallelState_(false) {}
|
||||
~ParallelCompilationGuard() {
|
||||
if (parallelState_) {
|
||||
JS_ASSERT(parallelState_->asmJSCompilationInProgress == true);
|
||||
parallelState_->asmJSCompilationInProgress = false;
|
||||
JS_ASSERT(WorkerThreadState().asmJSCompilationInProgress == true);
|
||||
WorkerThreadState().asmJSCompilationInProgress = false;
|
||||
}
|
||||
}
|
||||
bool claim(WorkerThreadState *state) {
|
||||
bool claim() {
|
||||
JS_ASSERT(!parallelState_);
|
||||
if (!state->asmJSCompilationInProgress.compareExchange(false, true))
|
||||
if (!WorkerThreadState().asmJSCompilationInProgress.compareExchange(false, true))
|
||||
return false;
|
||||
parallelState_ = state;
|
||||
parallelState_ = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -5445,22 +5445,23 @@ ParallelCompilationEnabled(ExclusiveContext *cx)
|
||||
// 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
|
||||
// worry about general dining philosophers.)
|
||||
if (!cx->isJSContext())
|
||||
return cx->workerThreadState()->numThreads > 1;
|
||||
if (WorkerThreadState().threadCount <= 1)
|
||||
return false;
|
||||
|
||||
if (!cx->isJSContext())
|
||||
return true;
|
||||
return cx->asJSContext()->runtime()->canUseParallelIonCompilation();
|
||||
}
|
||||
|
||||
// State of compilation as tracked and updated by the main thread.
|
||||
struct ParallelGroupState
|
||||
{
|
||||
WorkerThreadState &state;
|
||||
js::Vector<AsmJSParallelTask> &tasks;
|
||||
int32_t outstandingJobs; // Good work, jobs!
|
||||
uint32_t compiledJobs;
|
||||
|
||||
ParallelGroupState(WorkerThreadState &state, js::Vector<AsmJSParallelTask> &tasks)
|
||||
: state(state), tasks(tasks), outstandingJobs(0), compiledJobs(0)
|
||||
ParallelGroupState(js::Vector<AsmJSParallelTask> &tasks)
|
||||
: tasks(tasks), outstandingJobs(0), compiledJobs(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -5468,14 +5469,14 @@ struct ParallelGroupState
|
||||
static AsmJSParallelTask *
|
||||
GetFinishedCompilation(ModuleCompiler &m, ParallelGroupState &group)
|
||||
{
|
||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
while (!group.state.asmJSWorkerFailed()) {
|
||||
if (!group.state.asmJSFinishedList.empty()) {
|
||||
while (!WorkerThreadState().asmJSWorkerFailed()) {
|
||||
if (!WorkerThreadState().asmJSFinishedList().empty()) {
|
||||
group.outstandingJobs--;
|
||||
return group.state.asmJSFinishedList.popCopy();
|
||||
return WorkerThreadState().asmJSFinishedList().popCopy();
|
||||
}
|
||||
group.state.wait(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -5525,9 +5526,14 @@ GetUnusedTask(ParallelGroupState &group, uint32_t i, AsmJSParallelTask **outTask
|
||||
static bool
|
||||
CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
||||
{
|
||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
||||
group.state.resetAsmJSFailureState();
|
||||
#ifdef DEBUG
|
||||
{
|
||||
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++) {
|
||||
// Get exclusive access to an empty LifoAlloc from the thread group's pool.
|
||||
@ -5542,7 +5548,7 @@ CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
||||
return false;
|
||||
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
@ -5561,9 +5567,14 @@ CheckFunctionsParallelImpl(ModuleCompiler &m, ParallelGroupState &group)
|
||||
|
||||
JS_ASSERT(group.outstandingJobs == 0);
|
||||
JS_ASSERT(group.compiledJobs == m.numFunctions());
|
||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
||||
JS_ASSERT(!group.state.asmJSWorkerFailed());
|
||||
#ifdef DEBUG
|
||||
{
|
||||
AutoLockWorkerThreadState lock;
|
||||
JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
|
||||
JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
|
||||
}
|
||||
#endif
|
||||
JS_ASSERT(!WorkerThreadState().asmJSWorkerFailed());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5580,32 +5591,32 @@ CancelOutstandingJobs(ModuleCompiler &m, ParallelGroupState &group)
|
||||
if (!group.outstandingJobs)
|
||||
return;
|
||||
|
||||
AutoLockWorkerThreadState lock(*m.cx()->workerThreadState());
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
// From the compiling tasks, eliminate those waiting for worker assignation.
|
||||
group.outstandingJobs -= group.state.asmJSWorklist.length();
|
||||
group.state.asmJSWorklist.clear();
|
||||
group.outstandingJobs -= WorkerThreadState().asmJSWorklist().length();
|
||||
WorkerThreadState().asmJSWorklist().clear();
|
||||
|
||||
// From the compiling tasks, eliminate those waiting for codegen.
|
||||
group.outstandingJobs -= group.state.asmJSFinishedList.length();
|
||||
group.state.asmJSFinishedList.clear();
|
||||
group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
|
||||
WorkerThreadState().asmJSFinishedList().clear();
|
||||
|
||||
// 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.
|
||||
JS_ASSERT(group.outstandingJobs >= 0);
|
||||
while (group.outstandingJobs > 0) {
|
||||
group.state.wait(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||
|
||||
group.outstandingJobs -= group.state.harvestFailedAsmJSJobs();
|
||||
group.outstandingJobs -= group.state.asmJSFinishedList.length();
|
||||
group.state.asmJSFinishedList.clear();
|
||||
group.outstandingJobs -= WorkerThreadState().harvestFailedAsmJSJobs();
|
||||
group.outstandingJobs -= WorkerThreadState().asmJSFinishedList().length();
|
||||
WorkerThreadState().asmJSFinishedList().clear();
|
||||
}
|
||||
|
||||
JS_ASSERT(group.outstandingJobs == 0);
|
||||
JS_ASSERT(group.state.asmJSWorklist.empty());
|
||||
JS_ASSERT(group.state.asmJSFinishedList.empty());
|
||||
JS_ASSERT(WorkerThreadState().asmJSWorklist().empty());
|
||||
JS_ASSERT(WorkerThreadState().asmJSFinishedList().empty());
|
||||
}
|
||||
|
||||
static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
|
||||
@ -5619,12 +5630,11 @@ CheckFunctionsParallel(ModuleCompiler &m)
|
||||
// constraint by hoisting asmJS* state out of WorkerThreadState so multiple
|
||||
// concurrent asm.js parallel compilations don't race.)
|
||||
ParallelCompilationGuard g;
|
||||
if (!ParallelCompilationEnabled(m.cx()) || !g.claim(m.cx()->workerThreadState()))
|
||||
if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
|
||||
return CheckFunctionsSequential(m);
|
||||
|
||||
// Saturate all worker threads plus the main thread.
|
||||
WorkerThreadState &state = *m.cx()->workerThreadState();
|
||||
size_t numParallelJobs = state.numThreads + 1;
|
||||
size_t numParallelJobs = WorkerThreadState().threadCount + 1;
|
||||
|
||||
// Allocate scoped AsmJSParallelTask objects. Each contains a unique
|
||||
// LifoAlloc that provides all necessary memory for compilation.
|
||||
@ -5636,12 +5646,12 @@ CheckFunctionsParallel(ModuleCompiler &m)
|
||||
tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
|
||||
|
||||
// With compilation memory in-scope, dispatch worker threads.
|
||||
ParallelGroupState group(state, tasks);
|
||||
ParallelGroupState group(tasks);
|
||||
if (!CheckFunctionsParallelImpl(m, group)) {
|
||||
CancelOutstandingJobs(m, group);
|
||||
|
||||
// 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);
|
||||
return m.failOffset(func->srcOffset(), "allocation failure during compilation");
|
||||
}
|
||||
|
@ -512,15 +512,20 @@ jit::FinishOffThreadBuilder(IonBuilder *builder)
|
||||
}
|
||||
|
||||
static inline void
|
||||
FinishAllOffThreadCompilations(JitCompartment *ion)
|
||||
FinishAllOffThreadCompilations(JSCompartment *comp)
|
||||
{
|
||||
OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
|
||||
#ifdef JS_THREADSAFE
|
||||
AutoLockWorkerThreadState lock;
|
||||
GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
|
||||
|
||||
for (size_t i = 0; i < compilations.length(); i++) {
|
||||
IonBuilder *builder = compilations[i];
|
||||
for (size_t i = 0; i < finished.length(); i++) {
|
||||
IonBuilder *builder = finished[i];
|
||||
if (builder->compartment == CompileCompartment::get(comp)) {
|
||||
FinishOffThreadBuilder(builder);
|
||||
WorkerThreadState().remove(finished, &i);
|
||||
}
|
||||
compilations.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
@ -542,7 +547,7 @@ JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
|
||||
// do this for minor GCs.
|
||||
JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
|
||||
CancelOffThreadIonCompile(compartment, nullptr);
|
||||
FinishAllOffThreadCompilations(this);
|
||||
FinishAllOffThreadCompilations(compartment);
|
||||
|
||||
// Free temporary OSR buffer.
|
||||
rt->freeOsrTempData();
|
||||
@ -1511,18 +1516,30 @@ AttachFinishedCompilations(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
JitCompartment *ion = cx->compartment()->jitCompartment();
|
||||
if (!ion || !cx->runtime()->workerThreadState)
|
||||
if (!ion)
|
||||
return;
|
||||
|
||||
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
|
||||
// have been cancelled.
|
||||
while (!compilations.empty()) {
|
||||
IonBuilder *builder = compilations.popCopy();
|
||||
// Incorporate any off thread compilations for the compartment which have
|
||||
// finished, failed or have been cancelled.
|
||||
while (true) {
|
||||
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()) {
|
||||
RootedScript script(cx, builder->script());
|
||||
@ -1537,7 +1554,7 @@ AttachFinishedCompilations(JSContext *cx)
|
||||
{
|
||||
// Release the worker thread lock and root the compiler for GC.
|
||||
AutoTempAllocatorRooter root(cx, &builder->alloc());
|
||||
AutoUnlockWorkerThreadState unlock(cx->runtime());
|
||||
AutoUnlockWorkerThreadState unlock;
|
||||
AutoFlushCache afc("AttachFinishedCompilations", cx->runtime()->jitRuntime());
|
||||
success = codegen->link(cx, builder->constraints());
|
||||
}
|
||||
@ -1554,8 +1571,6 @@ AttachFinishedCompilations(JSContext *cx)
|
||||
|
||||
FinishOffThreadBuilder(builder);
|
||||
}
|
||||
|
||||
compilations.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1564,16 +1579,24 @@ static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
|
||||
static inline bool
|
||||
OffThreadCompilationAvailable(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
// Even if off thread compilation is enabled, compilation must still occur
|
||||
// on the main thread in some cases. Do not compile off thread during an
|
||||
// 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
|
||||
// CodeGenerator::maybeCreateScriptCounts will not attach script profiles
|
||||
// when running off thread.
|
||||
return cx->runtime()->canUseParallelIonCompilation()
|
||||
&& WorkerThreadState().cpuCount > 1
|
||||
&& cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
|
||||
&& !cx->runtime()->profilingScripts;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1831,7 +1854,12 @@ CheckScriptSize(JSContext *cx, JSScript* script)
|
||||
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
|
||||
numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
|
||||
{
|
||||
if (cx->runtime()->canUseParallelIonCompilation()) {
|
||||
#ifdef JS_THREADSAFE
|
||||
size_t cpuCount = WorkerThreadState().cpuCount;
|
||||
#else
|
||||
size_t cpuCount = 1;
|
||||
#endif
|
||||
if (cx->runtime()->canUseParallelIonCompilation() && cpuCount > 1) {
|
||||
// Even if off thread compilation is enabled, there are cases where
|
||||
// compilation must still occur on the main thread. Don't compile
|
||||
// in these cases (except when profiling scripts, as compilations
|
||||
@ -2488,7 +2516,7 @@ jit::StopAllOffThreadCompilations(JSCompartment *comp)
|
||||
if (!comp->jitCompartment())
|
||||
return;
|
||||
CancelOffThreadIonCompile(comp, nullptr);
|
||||
FinishAllOffThreadCompilations(comp->jitCompartment());
|
||||
FinishAllOffThreadCompilations(comp);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -59,8 +59,6 @@ typedef void (*EnterJitCode)(void *code, unsigned argc, Value *argv, StackFrame
|
||||
|
||||
class IonBuilder;
|
||||
|
||||
typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
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.
|
||||
typedef WeakValueCache<uint32_t, ReadBarriered<JitCode> > ICStubCodeMap;
|
||||
ICStubCodeMap *stubCodes_;
|
||||
@ -361,10 +353,6 @@ class JitCompartment
|
||||
JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
|
||||
|
||||
public:
|
||||
OffThreadCompilationVector &finishedOffThreadCompilations() {
|
||||
return finishedOffThreadCompilations_;
|
||||
}
|
||||
|
||||
JitCode *getStubCode(uint32_t key) {
|
||||
ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
|
||||
if (p)
|
||||
|
@ -615,6 +615,8 @@ JS_ShutDown(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
WorkerThreadState().finish();
|
||||
|
||||
PRMJ_NowShutdown();
|
||||
|
||||
#if EXPOSE_INTL_API
|
||||
@ -4552,7 +4554,7 @@ JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token)
|
||||
if (maybecx)
|
||||
lfc.construct(maybecx);
|
||||
|
||||
return rt->workerThreadState->finishParseTask(maybecx, rt, token);
|
||||
return WorkerThreadState().finishParseTask(maybecx, rt, token);
|
||||
#else
|
||||
MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
|
||||
#endif
|
||||
|
@ -1052,9 +1052,6 @@ js::ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, Conte
|
||||
perThreadData(pt),
|
||||
allocator_(nullptr)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT_IF(kind == Context_Exclusive, rt->workerThreadState != nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -282,8 +282,6 @@ struct ThreadSafeContext : ContextFriendFields,
|
||||
PropertyName *emptyString() { return runtime_->emptyString; }
|
||||
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
|
||||
bool useHelperThreads() { return runtime_->useHelperThreads(); }
|
||||
unsigned cpuCount() { return runtime_->cpuCount(); }
|
||||
size_t workerThreadCount() { return runtime_->workerThreadCount(); }
|
||||
void *runtimeAddressForJit() { return runtime_; }
|
||||
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
|
||||
void *stackLimitAddressForJitCode(StackKind kind);
|
||||
@ -390,15 +388,6 @@ class ExclusiveContext : public ThreadSafeContext
|
||||
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.
|
||||
frontend::CompileError &addPendingCompileError();
|
||||
void addPendingOverRecursed();
|
||||
@ -1040,7 +1029,7 @@ class AutoLockForExclusiveAccess
|
||||
void init(JSRuntime *rt) {
|
||||
runtime = rt;
|
||||
if (runtime->numExclusiveThreads) {
|
||||
runtime->assertCanLock(JSRuntime::ExclusiveAccessLock);
|
||||
runtime->assertCanLock(ExclusiveAccessLock);
|
||||
PR_Lock(runtime->exclusiveAccessLock);
|
||||
#ifdef DEBUG
|
||||
runtime->exclusiveAccessOwner = PR_GetCurrentThread();
|
||||
@ -1095,7 +1084,7 @@ class AutoLockForCompilation
|
||||
void init(JSRuntime *rt) {
|
||||
runtime = rt;
|
||||
if (runtime->numCompilationThreads) {
|
||||
runtime->assertCanLock(JSRuntime::CompilationLock);
|
||||
runtime->assertCanLock(CompilationLock);
|
||||
PR_Lock(runtime->compilationLock);
|
||||
#ifdef DEBUG
|
||||
runtime->compilationLockOwner = PR_GetCurrentThread();
|
||||
|
@ -1713,9 +1713,9 @@ ArenaLists::refillFreeList(ThreadSafeContext *cx, AllocKind thingKind)
|
||||
mozilla::Maybe<AutoLockWorkerThreadState> lock;
|
||||
JSRuntime *rt = zone->runtimeFromAnyThread();
|
||||
if (rt->exclusiveThreadsPresent()) {
|
||||
lock.construct<WorkerThreadState &>(*rt->workerThreadState);
|
||||
lock.construct();
|
||||
while (rt->isHeapBusy())
|
||||
rt->workerThreadState->wait(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
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
|
||||
// presence of exclusive threads, to avoid racing with refillFreeList.
|
||||
#ifdef JS_THREADSAFE
|
||||
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
||||
AutoLockWorkerThreadState lock;
|
||||
rt->heapState = heapState;
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
@ -4316,11 +4316,11 @@ AutoTraceSession::~AutoTraceSession()
|
||||
|
||||
if (runtime->exclusiveThreadsPresent()) {
|
||||
#ifdef JS_THREADSAFE
|
||||
AutoLockWorkerThreadState lock(*runtime->workerThreadState);
|
||||
AutoLockWorkerThreadState lock;
|
||||
runtime->heapState = prevState;
|
||||
|
||||
// Notify any worker threads waiting for the trace session to end.
|
||||
runtime->workerThreadState->notifyAll(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
|
@ -1313,11 +1313,15 @@ ScriptSource::setSourceCopy(ExclusiveContext *cx, const jschar *src, uint32_t le
|
||||
// thread (see WorkerThreadState::canStartParseTask) which would cause a
|
||||
// deadlock if there wasn't a second worker thread that could make
|
||||
// progress on our compression task.
|
||||
#ifdef JS_THREADSAFE
|
||||
bool canCompressOffThread =
|
||||
WorkerThreadState().cpuCount > 1 &&
|
||||
WorkerThreadState().threadCount >= 2;
|
||||
#else
|
||||
bool canCompressOffThread = false;
|
||||
#endif
|
||||
const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
|
||||
if (length < HUGE_SCRIPT &&
|
||||
cx->cpuCount() > 1 &&
|
||||
cx->workerThreadCount() >= 2)
|
||||
{
|
||||
if (length < HUGE_SCRIPT && canCompressOffThread) {
|
||||
task->ss = this;
|
||||
task->chars = src;
|
||||
ready_ = false;
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "prmjtime.h"
|
||||
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "jit/ExecutionModeInlines.h"
|
||||
#include "jit/IonBuilder.h"
|
||||
#include "vm/Debugger.h"
|
||||
|
||||
@ -28,31 +27,37 @@ using namespace js;
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::DebugOnly;
|
||||
|
||||
namespace js {
|
||||
|
||||
GlobalWorkerThreadState gWorkerThreadState;
|
||||
|
||||
} // namespace js
|
||||
|
||||
bool
|
||||
js::EnsureWorkerThreadsInitialized(ExclusiveContext *cx)
|
||||
{
|
||||
// If 'cx' is not a JSContext, we are already off the main thread and the
|
||||
// worker threads would have already been initialized.
|
||||
if (!cx->isJSContext()) {
|
||||
JS_ASSERT(cx->workerThreadState() != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSRuntime *rt = cx->asJSContext()->runtime();
|
||||
if (rt->workerThreadState)
|
||||
if (!cx->isJSContext())
|
||||
return true;
|
||||
|
||||
rt->workerThreadState = rt->new_<WorkerThreadState>(rt);
|
||||
if (!rt->workerThreadState)
|
||||
return false;
|
||||
return WorkerThreadState().ensureInitialized();
|
||||
}
|
||||
|
||||
if (!rt->workerThreadState->init()) {
|
||||
js_delete(rt->workerThreadState);
|
||||
rt->workerThreadState = nullptr;
|
||||
return false;
|
||||
}
|
||||
static size_t
|
||||
ThreadCountForCPUCount(size_t cpuCount)
|
||||
{
|
||||
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
|
||||
@ -61,23 +66,19 @@ bool
|
||||
js::StartOffThreadAsmJSCompile(ExclusiveContext *cx, AsmJSParallelTask *asmData)
|
||||
{
|
||||
// Threads already initialized by the AsmJS compiler.
|
||||
JS_ASSERT(cx->workerThreadState() != nullptr);
|
||||
JS_ASSERT(asmData->mir);
|
||||
JS_ASSERT(asmData->lir == nullptr);
|
||||
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
JS_ASSERT(state.numThreads);
|
||||
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
// Don't append this task if another failed.
|
||||
if (state.asmJSWorkerFailed())
|
||||
if (WorkerThreadState().asmJSWorkerFailed())
|
||||
return false;
|
||||
|
||||
if (!state.asmJSWorklist.append(asmData))
|
||||
if (!WorkerThreadState().asmJSWorklist().append(asmData))
|
||||
return false;
|
||||
|
||||
state.notifyOne(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyOne(GlobalWorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -87,33 +88,26 @@ js::StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder)
|
||||
if (!EnsureWorkerThreadsInitialized(cx))
|
||||
return false;
|
||||
|
||||
WorkerThreadState &state = *cx->runtime()->workerThreadState;
|
||||
JS_ASSERT(state.numThreads);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
|
||||
if (!state.ionWorklist.append(builder))
|
||||
if (!WorkerThreadState().ionWorklist().append(builder))
|
||||
return false;
|
||||
|
||||
cx->runtime()->addCompilationThread();
|
||||
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move an IonBuilder for which compilation has either finished, failed, or
|
||||
* been cancelled into the Ion compartment's finished compilations list.
|
||||
* All off thread compilations which are started must eventually be finished.
|
||||
* been cancelled into the global finished compilation list. All off thread
|
||||
* compilations which are started must eventually be finished.
|
||||
*/
|
||||
static void
|
||||
FinishOffThreadIonCompile(jit::IonBuilder *builder)
|
||||
{
|
||||
JSCompartment *compartment = builder->script()->compartment();
|
||||
JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState);
|
||||
JS_ASSERT(compartment->runtimeFromAnyThread()->workerThreadState->isLocked());
|
||||
|
||||
compartment->jitCompartment()->finishedOffThreadCompilations().append(builder);
|
||||
WorkerThreadState().ionFinishedList().append(builder);
|
||||
}
|
||||
|
||||
#endif // JS_ION
|
||||
@ -130,49 +124,43 @@ void
|
||||
js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
||||
{
|
||||
#ifdef JS_ION
|
||||
JSRuntime *rt = compartment->runtimeFromMainThread();
|
||||
|
||||
if (!rt->workerThreadState)
|
||||
return;
|
||||
|
||||
WorkerThreadState &state = *rt->workerThreadState;
|
||||
|
||||
jit::JitCompartment *jitComp = compartment->jitCompartment();
|
||||
if (!jitComp)
|
||||
return;
|
||||
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
if (!WorkerThreadState().threads)
|
||||
return;
|
||||
|
||||
/* Cancel any pending entries for which processing hasn't started. */
|
||||
for (size_t i = 0; i < state.ionWorklist.length(); i++) {
|
||||
jit::IonBuilder *builder = state.ionWorklist[i];
|
||||
GlobalWorkerThreadState::IonBuilderVector &worklist = WorkerThreadState().ionWorklist();
|
||||
for (size_t i = 0; i < worklist.length(); i++) {
|
||||
jit::IonBuilder *builder = worklist[i];
|
||||
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
||||
FinishOffThreadIonCompile(builder);
|
||||
state.ionWorklist[i--] = state.ionWorklist.back();
|
||||
state.ionWorklist.popBack();
|
||||
WorkerThreadState().remove(worklist, &i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for in progress entries to finish up. */
|
||||
for (size_t i = 0; i < state.numThreads; i++) {
|
||||
const WorkerThread &helper = state.threads[i];
|
||||
for (size_t i = 0; i < WorkerThreadState().threadCount; i++) {
|
||||
const WorkerThread &helper = WorkerThreadState().threads[i];
|
||||
while (helper.ionBuilder &&
|
||||
CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
|
||||
{
|
||||
helper.ionBuilder->cancel();
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||
}
|
||||
}
|
||||
|
||||
jit::OffThreadCompilationVector &compilations = jitComp->finishedOffThreadCompilations();
|
||||
|
||||
/* Cancel code generation for any completed entries. */
|
||||
for (size_t i = 0; i < compilations.length(); i++) {
|
||||
jit::IonBuilder *builder = compilations[i];
|
||||
GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
|
||||
for (size_t i = 0; i < finished.length(); i++) {
|
||||
jit::IonBuilder *builder = finished[i];
|
||||
if (CompiledScriptMatches(compartment, script, builder->script())) {
|
||||
jit::FinishOffThreadBuilder(builder);
|
||||
compilations[i--] = compilations.back();
|
||||
compilations.popBack();
|
||||
WorkerThreadState().remove(finished, &i);
|
||||
}
|
||||
}
|
||||
#endif // JS_ION
|
||||
@ -237,6 +225,55 @@ ParseTask::~ParseTask()
|
||||
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
|
||||
js::OffThreadParsingMustWaitForGC(JSRuntime *rt)
|
||||
{
|
||||
@ -316,21 +353,19 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
||||
if (!task->init(cx, options))
|
||||
return false;
|
||||
|
||||
WorkerThreadState &state = *cx->runtime()->workerThreadState;
|
||||
JS_ASSERT(state.numThreads);
|
||||
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||
if (!state.parseWaitingOnGC.append(task.get()))
|
||||
AutoLockWorkerThreadState lock;
|
||||
if (!WorkerThreadState().parseWaitingOnGC().append(task.get()))
|
||||
return false;
|
||||
} else {
|
||||
task->activate(cx->runtime());
|
||||
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
if (!state.parseWorklist.append(task.get()))
|
||||
if (!WorkerThreadState().parseWorklist().append(task.get()))
|
||||
return false;
|
||||
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
task.forget();
|
||||
@ -343,45 +378,35 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime *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;
|
||||
|
||||
// This logic should mirror the contents of the !activeGCInAtomsZone()
|
||||
// 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++)
|
||||
state.parseWaitingOnGC[i]->activate(rt);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
for (size_t i = 0; i < newTasks.length(); i++)
|
||||
WorkerThreadState().parseWorklist().append(newTasks[i]);
|
||||
|
||||
JS_ASSERT(state.parseWorklist.empty());
|
||||
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);
|
||||
}
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
bool
|
||||
WorkerThreadState::init()
|
||||
GlobalWorkerThreadState::ensureInitialized()
|
||||
{
|
||||
JS_ASSERT(numThreads == 0);
|
||||
JS_ASSERT(this == &WorkerThreadState());
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
if (!runtime->useHelperThreads())
|
||||
if (threads)
|
||||
return true;
|
||||
|
||||
workerLock = PR_NewLock();
|
||||
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());
|
||||
threads = js_pod_calloc<WorkerThread>(threadCount);
|
||||
if (!threads)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < runtime->workerThreadCount(); i++) {
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
WorkerThread &helper = threads[i];
|
||||
helper.runtime = runtime;
|
||||
helper.threadData.construct(runtime);
|
||||
helper.threadData.ref().addToThreadList();
|
||||
helper.threadData.construct(static_cast<JSRuntime *>(nullptr));
|
||||
helper.thread = PR_CreateThread(PR_USER_THREAD,
|
||||
WorkerThread::ThreadMain, &helper,
|
||||
PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, WORKER_STACK_SIZE);
|
||||
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();
|
||||
js_free(threads);
|
||||
threads = nullptr;
|
||||
@ -436,50 +448,43 @@ WorkerThreadState::init()
|
||||
}
|
||||
}
|
||||
|
||||
numThreads = runtime->workerThreadCount();
|
||||
resetAsmJSFailureState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::cleanup()
|
||||
GlobalWorkerThreadState::GlobalWorkerThreadState()
|
||||
{
|
||||
// Do preparatory work for shutdown before the final GC has destroyed most
|
||||
// of the GC heap.
|
||||
mozilla::PodZero(this);
|
||||
|
||||
// Join created threads, to ensure there is no in progress work.
|
||||
cpuCount = GetCPUCount();
|
||||
threadCount = ThreadCountForCPUCount(cpuCount);
|
||||
|
||||
MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
|
||||
|
||||
workerLock = PR_NewLock();
|
||||
consumerWakeup = PR_NewCondVar(workerLock);
|
||||
producerWakeup = PR_NewCondVar(workerLock);
|
||||
}
|
||||
|
||||
void
|
||||
GlobalWorkerThreadState::finish()
|
||||
{
|
||||
if (threads) {
|
||||
for (size_t i = 0; i < numThreads; i++)
|
||||
for (size_t i = 0; i < threadCount; i++)
|
||||
threads[i].destroy();
|
||||
js_free(threads);
|
||||
threads = nullptr;
|
||||
numThreads = 0;
|
||||
}
|
||||
|
||||
// Clean up any parse tasks which haven't been finished yet.
|
||||
while (!parseFinishedList.empty())
|
||||
finishParseTask(/* maybecx = */ nullptr, runtime, parseFinishedList[0]);
|
||||
}
|
||||
|
||||
WorkerThreadState::~WorkerThreadState()
|
||||
{
|
||||
JS_ASSERT(!threads);
|
||||
JS_ASSERT(parseFinishedList.empty());
|
||||
|
||||
if (workerLock)
|
||||
PR_DestroyLock(workerLock);
|
||||
|
||||
if (consumerWakeup)
|
||||
PR_DestroyCondVar(consumerWakeup);
|
||||
|
||||
if (producerWakeup)
|
||||
PR_DestroyCondVar(producerWakeup);
|
||||
PR_DestroyLock(workerLock);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::lock()
|
||||
GlobalWorkerThreadState::lock()
|
||||
{
|
||||
runtime->assertCanLock(JSRuntime::WorkerThreadStateLock);
|
||||
JS_ASSERT(!isLocked());
|
||||
AssertCurrentThreadCanLock(WorkerThreadStateLock);
|
||||
PR_Lock(workerLock);
|
||||
#ifdef DEBUG
|
||||
lockOwner = PR_GetCurrentThread();
|
||||
@ -487,7 +492,7 @@ WorkerThreadState::lock()
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::unlock()
|
||||
GlobalWorkerThreadState::unlock()
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
#ifdef DEBUG
|
||||
@ -498,14 +503,14 @@ WorkerThreadState::unlock()
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
WorkerThreadState::isLocked()
|
||||
GlobalWorkerThreadState::isLocked()
|
||||
{
|
||||
return lockOwner == PR_GetCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
WorkerThreadState::wait(CondVar which, uint32_t millis)
|
||||
GlobalWorkerThreadState::wait(CondVar which, uint32_t millis)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
#ifdef DEBUG
|
||||
@ -521,37 +526,37 @@ WorkerThreadState::wait(CondVar which, uint32_t millis)
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::notifyAll(CondVar which)
|
||||
GlobalWorkerThreadState::notifyAll(CondVar which)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
PR_NotifyAllCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThreadState::notifyOne(CondVar which)
|
||||
GlobalWorkerThreadState::notifyOne(CondVar which)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
PR_NotifyCondVar((which == CONSUMER) ? consumerWakeup : producerWakeup);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerThreadState::canStartAsmJSCompile()
|
||||
GlobalWorkerThreadState::canStartAsmJSCompile()
|
||||
{
|
||||
// Don't execute an AsmJS job if an earlier one failed.
|
||||
JS_ASSERT(isLocked());
|
||||
return (!asmJSWorklist.empty() && !numAsmJSFailedJobs);
|
||||
return (!asmJSWorklist().empty() && !numAsmJSFailedJobs);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerThreadState::canStartIonCompile()
|
||||
GlobalWorkerThreadState::canStartIonCompile()
|
||||
{
|
||||
// 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
|
||||
// currently compiling a script. The latter condition ensures that two
|
||||
// compilations cannot simultaneously occur.
|
||||
if (ionWorklist.empty())
|
||||
if (ionWorklist().empty())
|
||||
return false;
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
if (threads[i].ionBuilder)
|
||||
return false;
|
||||
}
|
||||
@ -559,16 +564,16 @@ WorkerThreadState::canStartIonCompile()
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerThreadState::canStartParseTask()
|
||||
GlobalWorkerThreadState::canStartParseTask()
|
||||
{
|
||||
// Don't allow simultaneous off thread parses, to reduce contention on the
|
||||
// atoms table. Note that asm.js compilation depends on this to avoid
|
||||
// stalling the worker thread, as off thread parse tasks can trigger and
|
||||
// block on other off thread asm.js compilation tasks.
|
||||
JS_ASSERT(isLocked());
|
||||
if (parseWorklist.empty())
|
||||
if (parseWorklist().empty())
|
||||
return false;
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
if (threads[i].parseTask)
|
||||
return false;
|
||||
}
|
||||
@ -576,9 +581,9 @@ WorkerThreadState::canStartParseTask()
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerThreadState::canStartCompressionTask()
|
||||
GlobalWorkerThreadState::canStartCompressionTask()
|
||||
{
|
||||
return !compressionWorklist.empty();
|
||||
return !compressionWorklist().empty();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -609,19 +614,19 @@ CallNewScriptHookForAllScripts(JSContext *cx, HandleScript script)
|
||||
}
|
||||
|
||||
JSScript *
|
||||
WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
|
||||
GlobalWorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
|
||||
{
|
||||
ParseTask *parseTask = nullptr;
|
||||
|
||||
// The token is a ParseTask* which should be in the finished list.
|
||||
// Find and remove its entry.
|
||||
{
|
||||
AutoLockWorkerThreadState lock(*rt->workerThreadState);
|
||||
for (size_t i = 0; i < parseFinishedList.length(); i++) {
|
||||
if (parseFinishedList[i] == token) {
|
||||
parseTask = parseFinishedList[i];
|
||||
parseFinishedList[i] = parseFinishedList.back();
|
||||
parseFinishedList.popBack();
|
||||
AutoLockWorkerThreadState lock;
|
||||
ParseTaskVector &finished = parseFinishedList();
|
||||
for (size_t i = 0; i < finished.length(); i++) {
|
||||
if (finished[i] == token) {
|
||||
parseTask = finished[i];
|
||||
remove(finished, &i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -691,24 +696,20 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
|
||||
void
|
||||
WorkerThread::destroy()
|
||||
{
|
||||
WorkerThreadState &state = *runtime->workerThreadState;
|
||||
|
||||
if (thread) {
|
||||
{
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
terminate = true;
|
||||
|
||||
/* Notify all workers, to ensure that this thread wakes up. */
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
PR_JoinThread(thread);
|
||||
}
|
||||
|
||||
if (!threadData.empty()) {
|
||||
threadData.ref().removeFromThreadList();
|
||||
if (!threadData.empty())
|
||||
threadData.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -720,19 +721,23 @@ WorkerThread::ThreadMain(void *arg)
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
||||
WorkerThread::handleAsmJSWorkload()
|
||||
{
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(state.isLocked());
|
||||
JS_ASSERT(state.canStartAsmJSCompile());
|
||||
JS_ASSERT(WorkerThreadState().isLocked());
|
||||
JS_ASSERT(WorkerThreadState().canStartAsmJSCompile());
|
||||
JS_ASSERT(idle());
|
||||
|
||||
asmData = state.asmJSWorklist.popCopy();
|
||||
asmData = WorkerThreadState().asmJSWorklist().popCopy();
|
||||
bool success = false;
|
||||
|
||||
state.unlock();
|
||||
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();
|
||||
|
||||
@ -748,38 +753,35 @@ WorkerThread::handleAsmJSWorkload(WorkerThreadState &state)
|
||||
|
||||
success = true;
|
||||
} while(0);
|
||||
state.lock();
|
||||
|
||||
// On failure, signal parent for harvesting in CancelOutstandingJobs().
|
||||
if (!success) {
|
||||
state.noteAsmJSFailure(asmData->func);
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().noteAsmJSFailure(asmData->func);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||
asmData = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// On success, move work to the finished list.
|
||||
state.asmJSFinishedList.append(asmData);
|
||||
WorkerThreadState().asmJSFinishedList().append(asmData);
|
||||
asmData = nullptr;
|
||||
|
||||
// Notify the main thread in case it's blocked waiting for a LifoAlloc.
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif // JS_ION
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
||||
WorkerThread::handleIonWorkload()
|
||||
{
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(state.isLocked());
|
||||
JS_ASSERT(state.canStartIonCompile());
|
||||
JS_ASSERT(WorkerThreadState().isLocked());
|
||||
JS_ASSERT(WorkerThreadState().canStartIonCompile());
|
||||
JS_ASSERT(idle());
|
||||
|
||||
ionBuilder = state.ionWorklist.popCopy();
|
||||
|
||||
DebugOnly<ExecutionMode> executionMode = ionBuilder->info().executionMode();
|
||||
ionBuilder = WorkerThreadState().ionWorklist().popCopy();
|
||||
|
||||
#if JS_TRACE_LOGGING
|
||||
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
|
||||
@ -788,9 +790,13 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
||||
ionBuilder->script());
|
||||
#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()),
|
||||
&ionBuilder->alloc());
|
||||
AutoEnterIonCompilation ionCompiling;
|
||||
@ -799,7 +805,6 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
||||
if (succeeded)
|
||||
ionBuilder->setBackgroundCodegen(jit::CompileBackEnd(ionBuilder));
|
||||
}
|
||||
state.lock();
|
||||
|
||||
FinishOffThreadIonCompile(ionBuilder);
|
||||
ionBuilder = nullptr;
|
||||
@ -808,10 +813,10 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
|
||||
// at the next operation callback. Don't interrupt Ion code for this, as
|
||||
// this incorporation can be delayed indefinitely without affecting
|
||||
// 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.
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif // JS_ION
|
||||
@ -843,17 +848,19 @@ ExclusiveContext::addPendingOverRecursed()
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::handleParseWorkload(WorkerThreadState &state)
|
||||
WorkerThread::handleParseWorkload()
|
||||
{
|
||||
JS_ASSERT(state.isLocked());
|
||||
JS_ASSERT(state.canStartParseTask());
|
||||
JS_ASSERT(WorkerThreadState().isLocked());
|
||||
JS_ASSERT(WorkerThreadState().canStartParseTask());
|
||||
JS_ASSERT(idle());
|
||||
|
||||
parseTask = state.parseWorklist.popCopy();
|
||||
parseTask = WorkerThreadState().parseWorklist().popCopy();
|
||||
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,
|
||||
NullPtr(), NullPtr(),
|
||||
parseTask->options,
|
||||
@ -865,26 +872,26 @@ WorkerThread::handleParseWorkload(WorkerThreadState &state)
|
||||
|
||||
// FinishOffThreadScript will need to be called on the script to
|
||||
// migrate it into the correct compartment.
|
||||
state.parseFinishedList.append(parseTask);
|
||||
WorkerThreadState().parseFinishedList().append(parseTask);
|
||||
|
||||
parseTask = nullptr;
|
||||
|
||||
// Notify the main thread in case it is waiting for the parse/emit to finish.
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::handleCompressionWorkload(WorkerThreadState &state)
|
||||
WorkerThread::handleCompressionWorkload()
|
||||
{
|
||||
JS_ASSERT(state.isLocked());
|
||||
JS_ASSERT(state.canStartCompressionTask());
|
||||
JS_ASSERT(WorkerThreadState().isLocked());
|
||||
JS_ASSERT(WorkerThreadState().canStartCompressionTask());
|
||||
JS_ASSERT(idle());
|
||||
|
||||
compressionTask = state.compressionWorklist.popCopy();
|
||||
compressionTask = WorkerThreadState().compressionWorklist().popCopy();
|
||||
compressionTask->workerThread = this;
|
||||
|
||||
{
|
||||
AutoUnlockWorkerThreadState unlock(runtime);
|
||||
AutoUnlockWorkerThreadState unlock;
|
||||
if (!compressionTask->work())
|
||||
compressionTask->setOOM();
|
||||
}
|
||||
@ -893,7 +900,7 @@ WorkerThread::handleCompressionWorkload(WorkerThreadState &state)
|
||||
compressionTask = nullptr;
|
||||
|
||||
// Notify the main thread in case it is waiting for the compression to finish.
|
||||
state.notifyAll(WorkerThreadState::CONSUMER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::CONSUMER);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -902,25 +909,24 @@ js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
||||
if (!EnsureWorkerThreadsInitialized(cx))
|
||||
return false;
|
||||
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
if (!state.compressionWorklist.append(task))
|
||||
if (!WorkerThreadState().compressionWorklist().append(task))
|
||||
return false;
|
||||
|
||||
state.notifyAll(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().notifyAll(GlobalWorkerThreadState::PRODUCER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerThreadState::compressionInProgress(SourceCompressionTask *task)
|
||||
GlobalWorkerThreadState::compressionInProgress(SourceCompressionTask *task)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
for (size_t i = 0; i < compressionWorklist.length(); i++) {
|
||||
if (compressionWorklist[i] == task)
|
||||
for (size_t i = 0; i < compressionWorklist().length(); i++) {
|
||||
if (compressionWorklist()[i] == task)
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
if (threads[i].compressionTask == task)
|
||||
return true;
|
||||
}
|
||||
@ -932,11 +938,10 @@ SourceCompressionTask::complete()
|
||||
{
|
||||
JS_ASSERT_IF(!ss, !chars);
|
||||
if (active()) {
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
while (state.compressionInProgress(this))
|
||||
state.wait(WorkerThreadState::CONSUMER);
|
||||
while (WorkerThreadState().compressionInProgress(this))
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::CONSUMER);
|
||||
|
||||
ss->ready_ = true;
|
||||
|
||||
@ -955,15 +960,15 @@ SourceCompressionTask::complete()
|
||||
}
|
||||
|
||||
SourceCompressionTask *
|
||||
WorkerThreadState::compressionTaskForSource(ScriptSource *ss)
|
||||
GlobalWorkerThreadState::compressionTaskForSource(ScriptSource *ss)
|
||||
{
|
||||
JS_ASSERT(isLocked());
|
||||
for (size_t i = 0; i < compressionWorklist.length(); i++) {
|
||||
SourceCompressionTask *task = compressionWorklist[i];
|
||||
for (size_t i = 0; i < compressionWorklist().length(); i++) {
|
||||
SourceCompressionTask *task = compressionWorklist()[i];
|
||||
if (task->source() == ss)
|
||||
return task;
|
||||
}
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
SourceCompressionTask *task = threads[i].compressionTask;
|
||||
if (task && task->source() == ss)
|
||||
return task;
|
||||
@ -981,12 +986,11 @@ ScriptSource::getOffThreadCompressionChars(ExclusiveContext *cx)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WorkerThreadState &state = *cx->workerThreadState();
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
// Look for a token that hasn't finished compressing and whose source is
|
||||
// the given ScriptSource.
|
||||
if (SourceCompressionTask *task = state.compressionTaskForSource(this))
|
||||
if (SourceCompressionTask *task = WorkerThreadState().compressionTaskForSource(this))
|
||||
return task->uncompressedChars();
|
||||
|
||||
// Compressing has finished, so this ScriptSource is ready. Avoid future
|
||||
@ -1000,8 +1004,7 @@ void
|
||||
WorkerThread::threadLoop()
|
||||
{
|
||||
JS::AutoAssertNoGC nogc;
|
||||
WorkerThreadState &state = *runtime->workerThreadState;
|
||||
AutoLockWorkerThreadState lock(state);
|
||||
AutoLockWorkerThreadState lock;
|
||||
|
||||
js::TlsPerThreadData.set(threadData.addr());
|
||||
|
||||
@ -1022,25 +1025,25 @@ WorkerThread::threadLoop()
|
||||
while (true) {
|
||||
if (terminate)
|
||||
return;
|
||||
if (state.canStartIonCompile() ||
|
||||
state.canStartAsmJSCompile() ||
|
||||
state.canStartParseTask() ||
|
||||
state.canStartCompressionTask())
|
||||
if (WorkerThreadState().canStartIonCompile() ||
|
||||
WorkerThreadState().canStartAsmJSCompile() ||
|
||||
WorkerThreadState().canStartParseTask() ||
|
||||
WorkerThreadState().canStartCompressionTask())
|
||||
{
|
||||
break;
|
||||
}
|
||||
state.wait(WorkerThreadState::PRODUCER);
|
||||
WorkerThreadState().wait(GlobalWorkerThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
// Dispatch tasks, prioritizing AsmJS work.
|
||||
if (state.canStartAsmJSCompile())
|
||||
handleAsmJSWorkload(state);
|
||||
else if (state.canStartIonCompile())
|
||||
handleIonWorkload(state);
|
||||
else if (state.canStartParseTask())
|
||||
handleParseWorkload(state);
|
||||
else if (state.canStartCompressionTask())
|
||||
handleCompressionWorkload(state);
|
||||
if (WorkerThreadState().canStartAsmJSCompile())
|
||||
handleAsmJSWorkload();
|
||||
else if (WorkerThreadState().canStartIonCompile())
|
||||
handleIonWorkload();
|
||||
else if (WorkerThreadState().canStartParseTask())
|
||||
handleParseWorkload();
|
||||
else if (WorkerThreadState().canStartCompressionTask())
|
||||
handleCompressionWorkload();
|
||||
else
|
||||
MOZ_ASSUME_UNREACHABLE("No task to perform");
|
||||
}
|
||||
@ -1071,6 +1074,11 @@ js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
js::CancelOffThreadParses(JSRuntime *rt)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
||||
const jschar *chars, size_t length, HandleObject scopeChain,
|
||||
@ -1079,11 +1087,6 @@ js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &optio
|
||||
MOZ_ASSUME_UNREACHABLE("Off thread compilation not available in non-THREADSAFE builds");
|
||||
}
|
||||
|
||||
void
|
||||
js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task)
|
||||
{
|
||||
|
@ -33,58 +33,59 @@ namespace jit {
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
/* Per-runtime state for off thread work items. */
|
||||
class WorkerThreadState
|
||||
// Per-process state for off thread work items.
|
||||
class GlobalWorkerThreadState
|
||||
{
|
||||
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;
|
||||
size_t numThreads;
|
||||
|
||||
enum CondVar {
|
||||
/* For notifying threads waiting for work that they may be able to make progress. */
|
||||
CONSUMER,
|
||||
private:
|
||||
// The lists below are all protected by |lock|.
|
||||
|
||||
/* For notifying threads doing work that they may be able to make progress. */
|
||||
PRODUCER
|
||||
};
|
||||
// Ion compilation worklist and finished jobs.
|
||||
IonBuilderVector ionWorklist_, ionFinishedList_;
|
||||
|
||||
/* Shared worklist for Ion worker threads. */
|
||||
Vector<jit::IonBuilder*, 0, SystemAllocPolicy> ionWorklist;
|
||||
// AsmJS worklist and finished jobs.
|
||||
//
|
||||
// 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. */
|
||||
Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> asmJSWorklist;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
public:
|
||||
// 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;
|
||||
|
||||
/* Shared worklist for parsing/emitting scripts on worker threads. */
|
||||
Vector<ParseTask*, 0, SystemAllocPolicy> parseWorklist, parseFinishedList;
|
||||
private:
|
||||
// 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. */
|
||||
Vector<ParseTask*, 0, SystemAllocPolicy> parseWaitingOnGC;
|
||||
// Parse tasks waiting for an atoms-zone GC to complete.
|
||||
ParseTaskVector parseWaitingOnGC_;
|
||||
|
||||
/* Worklist for source compression worker threads. */
|
||||
Vector<SourceCompressionTask *, 0, SystemAllocPolicy> compressionWorklist;
|
||||
// Source compression worklist.
|
||||
SourceCompressionTaskVector compressionWorklist_;
|
||||
|
||||
WorkerThreadState(JSRuntime *rt) {
|
||||
mozilla::PodZero(this);
|
||||
runtime = rt;
|
||||
}
|
||||
~WorkerThreadState();
|
||||
public:
|
||||
GlobalWorkerThreadState();
|
||||
|
||||
bool init();
|
||||
void cleanup();
|
||||
bool ensureInitialized();
|
||||
void finish();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
@ -93,10 +94,62 @@ class WorkerThreadState
|
||||
bool isLocked();
|
||||
# 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 notifyAll(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 canStartIonCompile();
|
||||
bool canStartParseTask();
|
||||
@ -132,8 +185,6 @@ class WorkerThreadState
|
||||
|
||||
private:
|
||||
|
||||
JSRuntime *runtime;
|
||||
|
||||
/*
|
||||
* Lock protecting all mutable shared state accessed by helper threads, and
|
||||
* used by all condition variables.
|
||||
@ -161,11 +212,16 @@ class WorkerThreadState
|
||||
void *asmJSFailedFunction;
|
||||
};
|
||||
|
||||
static inline GlobalWorkerThreadState &
|
||||
WorkerThreadState()
|
||||
{
|
||||
extern GlobalWorkerThreadState gWorkerThreadState;
|
||||
return gWorkerThreadState;
|
||||
}
|
||||
|
||||
/* Individual helper thread, one allocated per core. */
|
||||
struct WorkerThread
|
||||
{
|
||||
JSRuntime *runtime;
|
||||
|
||||
mozilla::Maybe<PerThreadData> threadData;
|
||||
PRThread *thread;
|
||||
|
||||
@ -190,10 +246,10 @@ struct WorkerThread
|
||||
|
||||
void destroy();
|
||||
|
||||
void handleAsmJSWorkload(WorkerThreadState &state);
|
||||
void handleIonWorkload(WorkerThreadState &state);
|
||||
void handleParseWorkload(WorkerThreadState &state);
|
||||
void handleCompressionWorkload(WorkerThreadState &state);
|
||||
void handleAsmJSWorkload();
|
||||
void handleIonWorkload();
|
||||
void handleParseWorkload();
|
||||
void handleCompressionWorkload();
|
||||
|
||||
static void ThreadMain(void *arg);
|
||||
void threadLoop();
|
||||
@ -203,10 +259,15 @@ struct WorkerThread
|
||||
|
||||
/* Methods for interacting with worker threads. */
|
||||
|
||||
/* Initialize worker threads unless already initialized. */
|
||||
// Initialize worker threads unless already initialized.
|
||||
bool
|
||||
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
|
||||
|
||||
/* Perform MIR optimization and LIR generation on a single function. */
|
||||
@ -229,6 +290,10 @@ StartOffThreadIonCompile(JSContext *cx, jit::IonBuilder *builder);
|
||||
void
|
||||
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
|
||||
* alive until the compilation finishes.
|
||||
@ -245,10 +310,6 @@ StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
|
||||
void
|
||||
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. */
|
||||
bool
|
||||
StartOffThreadCompression(ExclusiveContext *cx, SourceCompressionTask *task);
|
||||
@ -258,24 +319,19 @@ class AutoLockWorkerThreadState
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
WorkerThreadState &state;
|
||||
|
||||
public:
|
||||
AutoLockWorkerThreadState(WorkerThreadState &state
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: state(state)
|
||||
AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
state.lock();
|
||||
WorkerThreadState().lock();
|
||||
}
|
||||
|
||||
~AutoLockWorkerThreadState() {
|
||||
state.unlock();
|
||||
WorkerThreadState().unlock();
|
||||
}
|
||||
#else
|
||||
public:
|
||||
AutoLockWorkerThreadState(WorkerThreadState &state
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
AutoLockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
@ -284,28 +340,22 @@ class AutoLockWorkerThreadState
|
||||
|
||||
class AutoUnlockWorkerThreadState
|
||||
{
|
||||
JSRuntime *rt;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
public:
|
||||
|
||||
AutoUnlockWorkerThreadState(JSRuntime *rt
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: rt(rt)
|
||||
AutoUnlockWorkerThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(rt->workerThreadState);
|
||||
rt->workerThreadState->unlock();
|
||||
#else
|
||||
(void)this->rt;
|
||||
WorkerThreadState().unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoUnlockWorkerThreadState()
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->workerThreadState->lock();
|
||||
WorkerThreadState().lock();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@ -313,6 +363,7 @@ class AutoUnlockWorkerThreadState
|
||||
#ifdef JS_ION
|
||||
struct AsmJSParallelTask
|
||||
{
|
||||
JSRuntime *runtime; // Associated runtime.
|
||||
LifoAlloc lifo; // Provider of all heap memory used for compilation.
|
||||
void *func; // Really, a ModuleCompiler::Func*
|
||||
jit::MIRGenerator *mir; // Passed from main thread to worker.
|
||||
@ -320,10 +371,11 @@ struct AsmJSParallelTask
|
||||
unsigned compileTime;
|
||||
|
||||
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->mir = mir;
|
||||
this->lir = nullptr;
|
||||
@ -376,6 +428,10 @@ struct ParseTask
|
||||
void activate(JSRuntime *rt);
|
||||
void finish();
|
||||
|
||||
bool runtimeMatches(JSRuntime *rt) {
|
||||
return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
|
||||
}
|
||||
|
||||
~ParseTask();
|
||||
};
|
||||
|
||||
|
@ -5470,7 +5470,7 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
||||
#ifdef JS_THREADSAFE
|
||||
int32_t threadCount = op->getIntOption("thread-count");
|
||||
if (threadCount >= 0)
|
||||
cx->runtime()->setFakeCPUCount(threadCount);
|
||||
SetFakeCPUCount(threadCount);
|
||||
#endif /* JS_THREADSAFE */
|
||||
|
||||
#if defined(JS_ION)
|
||||
|
@ -91,9 +91,6 @@ PerThreadData::~PerThreadData()
|
||||
if (dtoaState)
|
||||
js_DestroyDtoaState(dtoaState);
|
||||
|
||||
if (isInList())
|
||||
removeFromThreadList();
|
||||
|
||||
#ifdef JS_ARM_SIMULATOR
|
||||
js_delete(simulator_);
|
||||
#endif
|
||||
@ -109,22 +106,6 @@ PerThreadData::init()
|
||||
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 = {
|
||||
TransparentObjectWrapper,
|
||||
nullptr,
|
||||
@ -144,7 +125,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
||||
#ifdef JS_THREADSAFE
|
||||
operationCallbackLock(nullptr),
|
||||
operationCallbackOwner(nullptr),
|
||||
workerThreadState(nullptr),
|
||||
exclusiveAccessLock(nullptr),
|
||||
exclusiveAccessOwner(nullptr),
|
||||
mainThreadHasExclusiveAccess(false),
|
||||
@ -316,11 +296,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
||||
parallelWarmup(0),
|
||||
ionReturnOverride_(MagicValue(JS_ARG_POISON)),
|
||||
useHelperThreads_(useHelperThreads),
|
||||
#ifdef JS_THREADSAFE
|
||||
cpuCount_(GetCPUCount()),
|
||||
#else
|
||||
cpuCount_(1),
|
||||
#endif
|
||||
parallelIonCompilationEnabled_(true),
|
||||
parallelParsingEnabled_(true),
|
||||
isWorkerRuntime_(false)
|
||||
@ -328,8 +303,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
||||
, enteredPolicy(nullptr)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(cpuCount_ > 0, "GetCPUCount() seems broken");
|
||||
|
||||
liveRuntimesCount++;
|
||||
|
||||
setGCMode(JSGC_MODE_GLOBAL);
|
||||
@ -392,7 +365,6 @@ JSRuntime::init(uint32_t maxbytes)
|
||||
return false;
|
||||
|
||||
js::TlsPerThreadData.set(&mainThread);
|
||||
mainThread.addToThreadList();
|
||||
|
||||
if (!threadPool.init())
|
||||
return false;
|
||||
@ -463,15 +435,15 @@ JSRuntime::~JSRuntime()
|
||||
/* Free source hook early, as its destructor may want to delete roots. */
|
||||
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())
|
||||
CancelOffThreadIonCompile(comp, nullptr);
|
||||
WaitForOffThreadParsingToFinish(this);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
if (workerThreadState)
|
||||
workerThreadState->cleanup();
|
||||
#endif
|
||||
CancelOffThreadParses(this);
|
||||
|
||||
/* Poison common names before final GC. */
|
||||
FinishCommonNames(this);
|
||||
@ -504,11 +476,7 @@ JSRuntime::~JSRuntime()
|
||||
*/
|
||||
finishSelfHosting();
|
||||
|
||||
mainThread.removeFromThreadList();
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
js_delete(workerThreadState);
|
||||
|
||||
JS_ASSERT(!exclusiveAccessOwner);
|
||||
if (exclusiveAccessLock)
|
||||
PR_DestroyLock(exclusiveAccessLock);
|
||||
@ -1010,7 +978,7 @@ JSRuntime::assertCanLock(RuntimeLock which)
|
||||
case ExclusiveAccessLock:
|
||||
JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
|
||||
case WorkerThreadStateLock:
|
||||
JS_ASSERT_IF(workerThreadState, !workerThreadState->isLocked());
|
||||
JS_ASSERT(!WorkerThreadState().isLocked());
|
||||
case CompilationLock:
|
||||
JS_ASSERT(compilationLockOwner != PR_GetCurrentThread());
|
||||
case OperationCallbackLock:
|
||||
@ -1082,4 +1050,14 @@ js::CurrentThreadCanReadCompilationData()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js::AssertCurrentThreadCanLock(RuntimeLock which)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
PerThreadData *pt = TlsPerThreadData.get();
|
||||
if (pt && pt->runtime_)
|
||||
pt->runtime_->assertCanLock(which);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
@ -84,7 +84,6 @@ class Activation;
|
||||
class ActivationIterator;
|
||||
class AsmJSActivation;
|
||||
class MathCache;
|
||||
class WorkerThreadState;
|
||||
|
||||
namespace jit {
|
||||
class JitRuntime;
|
||||
@ -477,15 +476,31 @@ AtomStateOffsetToName(const JSAtomState &atomState, size_t 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
|
||||
* single active thread. Normally, as most JS is single-threaded,
|
||||
* there is only one instance of this struct, embedded in the
|
||||
* JSRuntime as the field |mainThread|. During Parallel JS sections,
|
||||
* however, there will be one instance per worker thread.
|
||||
* single active thread. Instances of this structure can occur for
|
||||
* the main thread as |JSRuntime::mainThread|, for select operations
|
||||
* performed off thread, such as parsing, and for Parallel JS worker
|
||||
* threads.
|
||||
*/
|
||||
class PerThreadData : public PerThreadDataFriendFields,
|
||||
public mozilla::LinkedListElement<PerThreadData>
|
||||
class PerThreadData : public PerThreadDataFriendFields
|
||||
{
|
||||
/*
|
||||
* Backpointer to the full shared JSRuntime* with which this
|
||||
@ -538,6 +553,7 @@ class PerThreadData : public PerThreadDataFriendFields,
|
||||
friend class js::AsmJSActivation;
|
||||
#ifdef DEBUG
|
||||
friend bool js::CurrentThreadCanReadCompilationData();
|
||||
friend void js::AssertCurrentThreadCanLock(RuntimeLock which);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -598,8 +614,6 @@ class PerThreadData : public PerThreadDataFriendFields,
|
||||
~PerThreadData();
|
||||
|
||||
bool init();
|
||||
void addToThreadList();
|
||||
void removeFromThreadList();
|
||||
|
||||
bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
|
||||
inline JSRuntime *runtimeFromMainThread();
|
||||
@ -609,6 +623,25 @@ class PerThreadData : public PerThreadDataFriendFields,
|
||||
inline void addActiveCompilation();
|
||||
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
|
||||
js::jit::Simulator *simulator() const;
|
||||
void setSimulator(js::jit::Simulator *sim);
|
||||
@ -646,12 +679,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
*/
|
||||
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
|
||||
* as possible.
|
||||
@ -668,20 +695,10 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
/* Branch callback */
|
||||
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
|
||||
void assertCanLock(RuntimeLock which);
|
||||
void assertCanLock(js::RuntimeLock which);
|
||||
#else
|
||||
void assertCanLock(RuntimeLock which) {}
|
||||
void assertCanLock(js::RuntimeLock which) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
@ -702,7 +719,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
public:
|
||||
AutoLockForOperationCallback(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
rt->assertCanLock(JSRuntime::OperationCallbackLock);
|
||||
rt->assertCanLock(js::OperationCallbackLock);
|
||||
#ifdef JS_THREADSAFE
|
||||
PR_Lock(rt->operationCallbackLock);
|
||||
rt->operationCallbackOwner = PR_GetCurrentThread();
|
||||
@ -733,8 +750,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
js::WorkerThreadState *workerThreadState;
|
||||
|
||||
private:
|
||||
/*
|
||||
* 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() {
|
||||
#ifdef JS_THREADSAFE
|
||||
assertCanLock(GCLock);
|
||||
assertCanLock(js::GCLock);
|
||||
PR_Lock(gcLock);
|
||||
JS_ASSERT(!gcLockOwner);
|
||||
#ifdef DEBUG
|
||||
@ -1717,7 +1732,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
private:
|
||||
|
||||
JSUseHelperThreads useHelperThreads_;
|
||||
unsigned cpuCount_;
|
||||
|
||||
// Settings for how helper threads can be used.
|
||||
bool parallelIonCompilationEnabled_;
|
||||
@ -1739,39 +1753,14 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
#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
|
||||
// prefs changing).
|
||||
void setParallelIonCompilationEnabled(bool value) {
|
||||
parallelIonCompilationEnabled_ = value;
|
||||
}
|
||||
bool canUseParallelIonCompilation() const {
|
||||
// Require cpuCount_ > 1 so that Ion compilation jobs and main-thread
|
||||
// execution are not competing for the same resources.
|
||||
return useHelperThreads() &&
|
||||
parallelIonCompilationEnabled_ &&
|
||||
cpuCount_ > 1;
|
||||
parallelIonCompilationEnabled_;
|
||||
}
|
||||
void setParallelParsingEnabled(bool value) {
|
||||
parallelParsingEnabled_ = value;
|
||||
|
@ -391,8 +391,12 @@ ThreadPool::init()
|
||||
uint32_t
|
||||
ThreadPool::numWorkers() const
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
// Subtract one for the main thread, which always exists.
|
||||
return runtime_->cpuCount() - 1;
|
||||
return WorkerThreadState().cpuCount - 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
Loading…
Reference in New Issue
Block a user