Bug 1238575 - Fix shell evalInWorker() to wait for thread if it exits early r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 14 Jan 2016 13:49:35 +0000
changeset 279970 98fad04be92df2d0ae694ad60a92c253153a4a2d
parent 279969 a79e9b6b0da5753b095049dfd9ce43851ca7e37d
child 279971 9df268f67bb3bdf5debd9f9e4f4967381d36f972
push id70279
push userjcoppeard@mozilla.com
push dateThu, 14 Jan 2016 14:01:28 +0000
treeherdermozilla-inbound@0fff5b609f71 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1238575
milestone46.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 1238575 - Fix shell evalInWorker() to wait for thread if it exits early r=terrence
js/src/jit-test/tests/gc/bug-1238575-2.js
js/src/jit-test/tests/gc/bug-1238575.js
js/src/shell/js.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238575-2.js
@@ -0,0 +1,4 @@
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => evalInWorker("1"));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238575.js
@@ -0,0 +1,8 @@
+// |jit-test| allow-oom; allow-unhandlable-oom
+
+if (!('oomAfterAllocations' in this))
+    quit();
+
+oomAfterAllocations(5)
+gcslice(11);
+evalInWorker("1");
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2833,29 +2833,42 @@ EvalInWorker(JSContext* cx, unsigned arg
     }
 
     if (!args[0].toString()->ensureLinear(cx))
         return false;
 
     JSLinearString* str = &args[0].toString()->asLinear();
 
     char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t));
-    if (!chars)
-        return false;
+    if (!chars) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     CopyChars(chars, *str);
 
     WorkerInput* input = js_new<WorkerInput>(cx->runtime(), chars, str->length());
-    if (!input)
-        return false;
+    if (!input) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
 
     PRThread* thread = PR_CreateThread(PR_USER_THREAD, WorkerMain, input,
                                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
                                        gMaxStackSize + 128 * 1024);
-    if (!thread || !workerThreads.append(thread))
-        return false;
+    if (!thread) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    if (!workerThreads.append(thread)) {
+        ReportOutOfMemory(cx);
+        PR_JoinThread(thread);
+        return false;
+    }
 
     return true;
 }
 
 static bool
 ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -138,16 +138,19 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     profilerSampleBufferGen_(0),
     profilerSampleBufferLapCount_(1),
     wasmActivationStack_(nullptr),
     asyncStackForNewActivations(this),
     asyncCauseForNewActivations(this),
     asyncCallIsExplicit(false),
     entryMonitor(nullptr),
     parentRuntime(parentRuntime),
+#ifdef DEBUG
+    updateChildRuntimeCount(parentRuntime),
+#endif
     interrupt_(false),
     telemetryCallback(nullptr),
     handlingSegFault(false),
     handlingJitInterrupt_(false),
     interruptCallback(nullptr),
     exclusiveAccessLock(nullptr),
     exclusiveAccessOwner(nullptr),
     mainThreadHasExclusiveAccess(false),
@@ -355,16 +358,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
         return false;
 
     return true;
 }
 
 JSRuntime::~JSRuntime()
 {
     MOZ_ASSERT(!isHeapBusy());
+    MOZ_ASSERT(childRuntimeCount == 0);
 
     fx.destroyInstance();
 
     if (gcInitialized) {
         /* Free source hook early, as its destructor may want to delete roots. */
         sourceHook = nullptr;
 
         /*
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -791,16 +791,41 @@ struct JSRuntime : public JS::shadow::Ru
 
     /*
      * If non-null, another runtime guaranteed to outlive this one and whose
      * permanent data may be used by this one where possible.
      */
     JSRuntime* parentRuntime;
 
   private:
+#ifdef DEBUG
+    /* The number of child runtimes that have this runtime as their parent. */
+    mozilla::Atomic<size_t> childRuntimeCount;
+
+    class AutoUpdateChildRuntimeCount
+    {
+        JSRuntime* parent_;
+
+      public:
+        explicit AutoUpdateChildRuntimeCount(JSRuntime* parent)
+          : parent_(parent)
+        {
+            if (parent_)
+                parent_->childRuntimeCount++;
+        }
+
+        ~AutoUpdateChildRuntimeCount() {
+            if (parent_)
+                parent_->childRuntimeCount--;
+        }
+    };
+
+    AutoUpdateChildRuntimeCount updateChildRuntimeCount;
+#endif
+
     mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
 
     /* Call this to accumulate telemetry data. */
     JSAccumulateTelemetryDataCallback telemetryCallback;
   public:
     // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_*
     // histogram. |key| provides an additional key to identify the histogram.
     // |sample| is the data to add to the histogram.