From 5530c7cf8ed2406bc4e8d29ed38f04b7fb714ff4 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 1 Oct 2013 17:23:55 -0500 Subject: [PATCH] Bug 922096 - OdinMonkey: prevent multiple concurrent parallel asm.js compilations (r=sstangl) --- .../tests/asm.js/testParallelCompile.js | 26 ++++++++++++ js/src/jit/AsmJS.cpp | 41 +++++++++++++++---- js/src/jsworkers.h | 6 +++ 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 js/src/jit-test/tests/asm.js/testParallelCompile.js diff --git a/js/src/jit-test/tests/asm.js/testParallelCompile.js b/js/src/jit-test/tests/asm.js/testParallelCompile.js new file mode 100644 index 00000000000..41f87973521 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testParallelCompile.js @@ -0,0 +1,26 @@ +load(libdir + "asm.js"); + +if (!isAsmJSCompilationAvailable()) + quit(); + +var module = "'use asm';\n"; +for (var i = 0; i < 100; i++) { + module += "function f" + i + "(i) {\n"; + module += " i=i|0; var j=0; j=(i+1)|0; i=(j-4)|0; i=(i+j)|0; return i|0\n"; + module += "}\n"; +} +module += "return f0"; +var script = "(function() {\n" + module + "})"; + +for (var i = 0; i < 10; i++) { + try { + offThreadCompileScript(script); + var f = new Function(module); + var g = runOffThreadScript(); + assertEq(isAsmJSModule(f), true); + assertEq(isAsmJSModule(g), true); + } catch (e) { + // ignore spurious error when offThreadCompileScript can't run in + // parallel + } +} diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 0588ea6bdb0..beb11b62467 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -5074,6 +5074,29 @@ CheckFunctionsSequential(ModuleCompiler &m) #ifdef JS_WORKER_THREADS +// Currently, only one asm.js parallel compilation is allowed at a time. +// This RAII class attempts to claim this parallel compilation using atomic ops +// on rt->workerThreadState->asmJSCompilationInProgress. +class ParallelCompilationGuard +{ + WorkerThreadState *parallelState_; + public: + ParallelCompilationGuard() : parallelState_(NULL) {} + ~ParallelCompilationGuard() { + if (parallelState_) { + JS_ASSERT(parallelState_->asmJSCompilationInProgress == true); + parallelState_->asmJSCompilationInProgress = false; + } + } + bool claim(WorkerThreadState *state) { + JS_ASSERT(!parallelState_); + if (!state->asmJSCompilationInProgress.compareExchange(false, true)) + return false; + parallelState_ = state; + return true; + } +}; + static bool ParallelCompilationEnabled(ExclusiveContext *cx) { @@ -5254,6 +5277,15 @@ static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12; static bool CheckFunctionsParallel(ModuleCompiler &m) { + // If parallel compilation isn't enabled (not enough cores, disabled by + // pref, etc) or another thread is currently compiling asm.js in parallel, + // fall back to sequential compilation. (We could lift the latter + // 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())) + return CheckFunctionsSequential(m); + // Saturate all worker threads plus the main thread. WorkerThreadState &state = *m.cx()->workerThreadState(); size_t numParallelJobs = state.numThreads + 1; @@ -6393,13 +6425,8 @@ CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList, return false; #ifdef JS_WORKER_THREADS - if (ParallelCompilationEnabled(cx)) { - if (!CheckFunctionsParallel(m)) - return false; - } else { - if (!CheckFunctionsSequential(m)) - return false; - } + if (!CheckFunctionsParallel(m)) + return false; #else if (!CheckFunctionsSequential(m)) return false; diff --git a/js/src/jsworkers.h b/js/src/jsworkers.h index e4977427c31..2bb73232d9d 100644 --- a/js/src/jsworkers.h +++ b/js/src/jsworkers.h @@ -71,6 +71,12 @@ class WorkerThreadState */ Vector 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 asmJSCompilationInProgress; + /* Shared worklist for parsing/emitting scripts on worker threads. */ Vector parseWorklist, parseFinishedList;