Bug 1411947 - Move OffThreadState from per-context to global JS shell state, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 01 Nov 2017 09:59:13 -0600
changeset 690399 cc4275f8755c2543e4fb05e58ee2b848bcef4950
parent 690398 9cff4b8eb3965ec0aa67be915f36e087bfb32538
child 690400 df82219658dce020aecef0a565c04f7022222fd8
push id87288
push usercholler@mozilla.com
push dateWed, 01 Nov 2017 22:09:51 +0000
reviewersjandem
bugs1411947
milestone58.0a1
Bug 1411947 - Move OffThreadState from per-context to global JS shell state, r=jandem.
js/src/jit-test/tests/basic/bug1411947.js
js/src/shell/js.cpp
js/src/shell/jsshell.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1411947.js
@@ -0,0 +1,6 @@
+if (helperThreadCount() === 0)
+  quit();
+evalInCooperativeThread(`
+startgc(9469, "shrinking");
+offThreadCompileScript("");
+`);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -194,44 +194,85 @@ InstallCoverageSignalHandlers()
     reset_sa.sa_handler = counters_reset;
     reset_sa.sa_flags = SA_RESTART;
     sigemptyset(&reset_sa.sa_mask);
     mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
     MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
 }
 #endif
 
+class OffThreadState {
+    enum State {
+        IDLE,           /* ready to work; no token, no source */
+        COMPILING,      /* working; no token, have source */
+        DONE            /* compilation done: have token and source */
+    };
+
+  public:
+    OffThreadState()
+      : monitor(mutexid::ShellOffThreadState),
+        state(IDLE),
+        runtime(nullptr),
+        token(),
+        source(nullptr)
+    { }
+
+    bool startIfIdle(JSContext* cx, ScriptKind kind, ScopedJSFreePtr<char16_t>& newSource);
+
+    bool startIfIdle(JSContext* cx, ScriptKind kind, JS::TranscodeBuffer&& newXdr);
+
+    void abandon(JSContext* cx);
+
+    void markDone(void* newToken);
+
+    void* waitUntilDone(JSContext* cx, ScriptKind kind);
+
+    JS::TranscodeBuffer& xdrBuffer() { return xdr; }
+
+  private:
+    js::Monitor monitor;
+    ScriptKind scriptKind;
+    State state;
+    JSRuntime* runtime;
+    void* token;
+    char16_t* source;
+    JS::TranscodeBuffer xdr;
+};
+static OffThreadState gOffThreadState;
+
 bool
 OffThreadState::startIfIdle(JSContext* cx, ScriptKind kind, ScopedJSFreePtr<char16_t>& newSource)
 {
     AutoLockMonitor alm(monitor);
     if (state != IDLE)
         return false;
 
     MOZ_ASSERT(!token);
 
     source = newSource.forget();
 
     scriptKind = kind;
+    runtime = cx->runtime();
     state = COMPILING;
     return true;
 }
 
 bool
 OffThreadState::startIfIdle(JSContext* cx, ScriptKind kind, JS::TranscodeBuffer&& newXdr)
 {
     AutoLockMonitor alm(monitor);
     if (state != IDLE)
         return false;
 
     MOZ_ASSERT(!token);
 
     xdr = mozilla::Move(newXdr);
 
     scriptKind = kind;
+    runtime = cx->runtime();
     state = COMPILING;
     return true;
 }
 
 void
 OffThreadState::abandon(JSContext* cx)
 {
     AutoLockMonitor alm(monitor);
@@ -260,17 +301,17 @@ OffThreadState::markDone(void* newToken)
     state = DONE;
     alm.notify();
 }
 
 void*
 OffThreadState::waitUntilDone(JSContext* cx, ScriptKind kind)
 {
     AutoLockMonitor alm(monitor);
-    if (state == IDLE || scriptKind != kind)
+    if (state == IDLE || cx->runtime() != runtime || scriptKind != kind)
         return nullptr;
 
     if (state == COMPILING) {
         while (state != DONE)
             alm.wait();
     }
 
     MOZ_ASSERT(source || !xdr.empty());
@@ -4451,18 +4492,17 @@ SyntaxParse(JSContext* cx, unsigned argc
 
     args.rval().setBoolean(succeeded);
     return true;
 }
 
 static void
 OffThreadCompileScriptCallback(void* token, void* callbackData)
 {
-    ShellContext* sc = static_cast<ShellContext*>(callbackData);
-    sc->offThreadState.markDone(token);
+    gOffThreadState.markDone(token);
 }
 
 static bool
 OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp)
 {
     if (!CanUseExtraThreads()) {
         JS_ReportErrorASCII(cx, "Can't use offThreadCompileScript with --no-threads");
         return false;
@@ -4529,44 +4569,42 @@ OffThreadCompileScript(JSContext* cx, un
         chars = copy;
     }
 
     if (!JS::CanCompileOffThread(cx, options, length)) {
         JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
         return false;
     }
 
-    ShellContext* sc = GetShellContext(cx);
-    if (!sc->offThreadState.startIfIdle(cx, ScriptKind::Script, ownedChars)) {
+    if (!gOffThreadState.startIfIdle(cx, ScriptKind::Script, ownedChars)) {
         JS_ReportErrorASCII(cx, "called offThreadCompileScript without calling runOffThreadScript"
                             " to receive prior off-thread compilation");
         return false;
     }
 
     if (!JS::CompileOffThread(cx, options, chars, length,
-                              OffThreadCompileScriptCallback, sc))
+                              OffThreadCompileScriptCallback, nullptr))
     {
-        sc->offThreadState.abandon(cx);
+        gOffThreadState.abandon(cx);
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 runOffThreadScript(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (OffThreadParsingMustWaitForGC(cx->runtime()))
         gc::FinishGC(cx);
 
-    ShellContext* sc = GetShellContext(cx);
-    void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::Script);
+    void* token = gOffThreadState.waitUntilDone(cx, ScriptKind::Script);
     if (!token) {
         JS_ReportErrorASCII(cx, "called runOffThreadScript when no compilation is pending");
         return false;
     }
 
     RootedScript script(cx, JS::FinishOffThreadScript(cx, token));
     if (!script)
         return false;
@@ -4616,44 +4654,42 @@ OffThreadCompileModule(JSContext* cx, un
         chars = copy;
     }
 
     if (!JS::CanCompileOffThread(cx, options, length)) {
         JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
         return false;
     }
 
-    ShellContext* sc = GetShellContext(cx);
-    if (!sc->offThreadState.startIfIdle(cx, ScriptKind::Module, ownedChars)) {
+    if (!gOffThreadState.startIfIdle(cx, ScriptKind::Module, ownedChars)) {
         JS_ReportErrorASCII(cx, "called offThreadCompileModule without receiving prior off-thread "
                             "compilation");
         return false;
     }
 
     if (!JS::CompileOffThreadModule(cx, options, chars, length,
-                                    OffThreadCompileScriptCallback, sc))
+                                    OffThreadCompileScriptCallback, nullptr))
     {
-        sc->offThreadState.abandon(cx);
+        gOffThreadState.abandon(cx);
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (OffThreadParsingMustWaitForGC(cx->runtime()))
         gc::FinishGC(cx);
 
-    ShellContext* sc = GetShellContext(cx);
-    void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::Module);
+    void* token = gOffThreadState.waitUntilDone(cx, ScriptKind::Module);
     if (!token) {
         JS_ReportErrorASCII(cx, "called finishOffThreadModule when no compilation is pending");
         return false;
     }
 
     RootedObject module(cx, JS::FinishOffThreadModule(cx, token));
     if (!module)
         return false;
@@ -4720,44 +4756,42 @@ OffThreadDecodeScript(JSContext* cx, uns
         return false;
     }
 
     if (!JS::CanDecodeOffThread(cx, options, loadLength)) {
         JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
         return false;
     }
 
-    ShellContext* sc = GetShellContext(cx);
-    if (!sc->offThreadState.startIfIdle(cx, ScriptKind::DecodeScript, mozilla::Move(loadBuffer))) {
+    if (!gOffThreadState.startIfIdle(cx, ScriptKind::DecodeScript, mozilla::Move(loadBuffer))) {
         JS_ReportErrorASCII(cx, "called offThreadDecodeScript without calling "
                             "runOffThreadDecodedScript to receive prior off-thread compilation");
         return false;
     }
 
-    if (!JS::DecodeOffThreadScript(cx, options, sc->offThreadState.xdrBuffer(), 0,
-                                   OffThreadCompileScriptCallback, sc))
+    if (!JS::DecodeOffThreadScript(cx, options, gOffThreadState.xdrBuffer(), 0,
+                                   OffThreadCompileScriptCallback, nullptr))
     {
-        sc->offThreadState.abandon(cx);
+        gOffThreadState.abandon(cx);
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 runOffThreadDecodedScript(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (OffThreadParsingMustWaitForGC(cx->runtime()))
         gc::FinishGC(cx);
 
-    ShellContext* sc = GetShellContext(cx);
-    void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::DecodeScript);
+    void* token = gOffThreadState.waitUntilDone(cx, ScriptKind::DecodeScript);
     if (!token) {
         JS_ReportErrorASCII(cx, "called runOffThreadDecodedScript when no compilation is pending");
         return false;
     }
 
     RootedScript script(cx, JS::FinishOffThreadScriptDecoder(cx, token));
     if (!script)
         return false;
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -109,52 +109,16 @@ CreateAlias(JSContext* cx, const char* d
 
 enum class ScriptKind
 {
     Script,
     DecodeScript,
     Module
 };
 
-class OffThreadState {
-    enum State {
-        IDLE,           /* ready to work; no token, no source */
-        COMPILING,      /* working; no token, have source */
-        DONE            /* compilation done: have token and source */
-    };
-
-  public:
-    OffThreadState()
-      : monitor(mutexid::ShellOffThreadState),
-        state(IDLE),
-        token(),
-        source(nullptr)
-    { }
-
-    bool startIfIdle(JSContext* cx, ScriptKind kind, ScopedJSFreePtr<char16_t>& newSource);
-
-    bool startIfIdle(JSContext* cx, ScriptKind kind, JS::TranscodeBuffer&& newXdr);
-
-    void abandon(JSContext* cx);
-
-    void markDone(void* newToken);
-
-    void* waitUntilDone(JSContext* cx, ScriptKind kind);
-
-    JS::TranscodeBuffer& xdrBuffer() { return xdr; }
-
-  private:
-    js::Monitor monitor;
-    ScriptKind scriptKind;
-    State state;
-    void* token;
-    char16_t* source;
-    JS::TranscodeBuffer xdr;
-};
-
 class NonshrinkingGCObjectVector : public GCVector<JSObject*, 0, SystemAllocPolicy>
 {
   public:
     void sweep() {
         for (uint32_t i = 0; i < this->length(); i++) {
             if (JS::GCPolicy<JSObject*>::needsSweep(&(*this)[i]))
                 (*this)[i] = nullptr;
         }
@@ -201,18 +165,16 @@ struct ShellContext
     JS::UniqueChars readLineBuf;
     size_t readLineBufPos;
 
     js::shell::RCFile** errFilePtr;
     js::shell::RCFile** outFilePtr;
 
     UniquePtr<PseudoStack> geckoProfilingStack;
 
-    OffThreadState offThreadState;
-
     JS::UniqueChars moduleLoadPath;
     UniquePtr<MarkBitObservers> markObservers;
 };
 
 extern ShellContext*
 GetShellContext(JSContext* cx);
 
 } /* namespace shell */