Bug 1245286 - Do not access the shell's interruptFunc off the main thread; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Wed, 03 Feb 2016 15:46:59 -0800
changeset 282935 8d177f82c65dc92c23865654e6b33deccde5551b
parent 282934 3bcdd0107ef0602fc4f8d69103d21e1b3a814618
child 282936 772dbb329a151adf98805a3b0cd5c713f5733e08
push id71370
push usertcole@mozilla.com
push dateWed, 03 Feb 2016 23:50:28 +0000
treeherdermozilla-inbound@772dbb329a15 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1245286
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1245286 - Do not access the shell's interruptFunc off the main thread; r=jonco
js/src/shell/js.cpp
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -139,16 +139,17 @@ static const double MAX_TIMEOUT_INTERVAL
 // Per-runtime shell state.
 struct ShellRuntime
 {
     ShellRuntime();
 
     bool isWorker;
     double timeoutInterval;
     Atomic<bool> serviceInterrupt;
+    Atomic<bool> haveInterruptFunc;
     JS::PersistentRootedValue interruptFunc;
     bool lastWarningEnabled;
     JS::PersistentRootedValue lastWarning;
 
     /*
      * Watchdog thread state.
      */
     PRLock* watchdogLock;
@@ -285,16 +286,17 @@ extern JS_EXPORT_API(char*) readline(con
 extern JS_EXPORT_API(void)   add_history(char* line);
 } // extern "C"
 #endif
 
 ShellRuntime::ShellRuntime()
   : isWorker(false),
     timeoutInterval(-1.0),
     serviceInterrupt(false),
+    haveInterruptFunc(false),
     lastWarningEnabled(false),
     watchdogLock(nullptr),
     watchdogWakeup(nullptr),
     watchdogThread(nullptr),
     watchdogHasTimeout(false),
     watchdogTimeout(0),
     sleepWakeup(nullptr),
     exitCode(0),
@@ -425,22 +427,21 @@ ShellInterruptCallback(JSContext* cx)
 
     // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
     // true to distinguish watchdog or user triggered interrupts.
     // Do this first to prevent other interrupts that may occur while the
     // user-supplied callback is executing from re-entering the handler.
     sr->serviceInterrupt = false;
 
     bool result;
-    RootedValue interruptFunc(cx, sr->interruptFunc);
-    if (!interruptFunc.isNull()) {
+    if (sr->haveInterruptFunc) {
         JS::AutoSaveExceptionState savedExc(cx);
-        JSAutoCompartment ac(cx, &interruptFunc.toObject());
+        JSAutoCompartment ac(cx, &sr->interruptFunc.toObject());
         RootedValue rval(cx);
-        if (!JS_CallFunctionValue(cx, nullptr, interruptFunc,
+        if (!JS_CallFunctionValue(cx, nullptr, sr->interruptFunc,
                                   JS::HandleValueArray::empty(), &rval))
         {
             return false;
         }
         if (rval.isBoolean())
             result = rval.toBoolean();
         else
             result = false;
@@ -3121,17 +3122,17 @@ KillWorkerThreads()
 
 static void
 CancelExecution(JSRuntime* rt)
 {
     ShellRuntime* sr = GetShellRuntime(rt);
     sr->serviceInterrupt = true;
     JS_RequestInterruptCallback(rt);
 
-    if (!sr->interruptFunc.isNull()) {
+    if (sr->haveInterruptFunc) {
         static const char msg[] = "Script runs for too long, terminating.\n";
         fputs(msg, stderr);
     }
 }
 
 static bool
 SetTimeoutValue(JSContext* cx, double t)
 {
@@ -3170,16 +3171,17 @@ Timeout(JSContext* cx, unsigned argc, Va
 
     if (args.length() > 1) {
         RootedValue value(cx, args[1]);
         if (!value.isObject() || !value.toObject().is<JSFunction>()) {
             JS_ReportError(cx, "Second argument must be a timeout function");
             return false;
         }
         sr->interruptFunc = value;
+        sr->haveInterruptFunc = true;
     }
 
     args.rval().setUndefined();
     return SetTimeoutValue(cx, t);
 }
 
 static bool
 InterruptIf(JSContext* cx, unsigned argc, Value* vp)
@@ -3241,16 +3243,17 @@ SetInterruptCallback(JSContext* cx, unsi
     }
 
     RootedValue value(cx, args[0]);
     if (!value.isObject() || !value.toObject().is<JSFunction>()) {
         JS_ReportError(cx, "Argument must be a function");
         return false;
     }
     GetShellRuntime(cx)->interruptFunc = value;
+    GetShellRuntime(cx)->haveInterruptFunc = true;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {