From 68b0327ea98a11a04d4880adaf5ed402ecf4b25a Mon Sep 17 00:00:00 2001
From: Brian Hackett <bhackett1024@gmail.com>
Date: Wed, 4 Dec 2013 12:41:36 -0800
Subject: [PATCH] Bug 942984 - Set native stack limit for JS worker threads,
 r=billm a=lsblakk.

---
 js/src/jscntxt.cpp   |  5 ++++-
 js/src/jscntxt.h     |  1 +
 js/src/jsworkers.cpp | 35 +++++++++++++++++++++++++++++++++--
 js/src/jsworkers.h   |  1 +
 4 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 013f1fae599ff..f68e2109b2161 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -423,7 +423,10 @@ js_ReportOverRecursed(JSContext *maybecx)
 void
 js_ReportOverRecursed(ThreadSafeContext *cx)
 {
-    js_ReportOverRecursed(cx->maybeJSContext());
+    if (cx->isJSContext())
+        js_ReportOverRecursed(cx->asJSContext());
+    else if (cx->isExclusiveContext())
+        cx->asExclusiveContext()->addPendingOverRecursed();
 }
 
 void
diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h
index cd3879104b92c..cd155e0ef35ee 100644
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -402,6 +402,7 @@ class ExclusiveContext : public ThreadSafeContext
 
     // Methods specific to any WorkerThread for the context.
     frontend::CompileError &addPendingCompileError();
+    void addPendingOverRecursed();
 };
 
 inline void
diff --git a/js/src/jsworkers.cpp b/js/src/jsworkers.cpp
index 9f78d642aea19..9e32046703500 100644
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -9,6 +9,7 @@
 #ifdef JS_WORKER_THREADS
 #include "mozilla/DebugOnly.h"
 
+#include "jsnativestack.h"
 #include "prmjtime.h"
 
 #include "frontend/BytecodeCompiler.h"
@@ -22,6 +23,7 @@
 
 using namespace js;
 
+using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 
 bool
@@ -179,7 +181,8 @@ ParseTask::ParseTask(ExclusiveContext *cx, const CompileOptions &options,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(options), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(scopeChain),
-    callback(callback), callbackData(callbackData), script(nullptr), errors(cx)
+    callback(callback), callbackData(callbackData), script(nullptr),
+    errors(cx), overRecursed(false)
 {
     JSRuntime *rt = scopeChain->runtimeFromMainThread();
 
@@ -310,6 +313,9 @@ js::WaitForOffThreadParsingToFinish(JSRuntime *rt)
     }
 }
 
+static const uint32_t WORKER_STACK_SIZE = 512 * 1024;
+static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
+
 bool
 WorkerThreadState::init(JSRuntime *rt)
 {
@@ -345,7 +351,7 @@ WorkerThreadState::init(JSRuntime *rt)
         helper.threadData.ref().addToThreadList();
         helper.thread = PR_CreateThread(PR_USER_THREAD,
                                         WorkerThread::ThreadMain, &helper,
-                                        PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+                                        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 < numThreads; j++)
                 threads[j].destroy();
@@ -578,6 +584,8 @@ WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *toke
         AutoCompartment ac(maybecx, parseTask->scopeChain);
         for (size_t i = 0; i < parseTask->errors.length(); i++)
             parseTask->errors[i]->throwError(maybecx);
+        if (parseTask->overRecursed)
+            js_ReportOverRecursed(maybecx);
 
         if (script) {
             // The Debugger only needs to be told about the topmost script that was compiled.
@@ -729,6 +737,13 @@ ExclusiveContext::addPendingCompileError()
     return *error;
 }
 
+void
+ExclusiveContext::addPendingOverRecursed()
+{
+    if (workerThread()->parseTask)
+        workerThread()->parseTask->overRecursed = true;
+}
+
 void
 WorkerThread::handleParseWorkload(WorkerThreadState &state)
 {
@@ -894,6 +909,16 @@ WorkerThread::threadLoop()
 
     js::TlsPerThreadData.set(threadData.addr());
 
+    // Compute the thread's stack limit, for over-recursed checks.
+    uintptr_t stackLimit = GetNativeStackBase();
+#if JS_STACK_GROWTH_DIRECTION > 0
+    stackLimit += WORKER_STACK_QUOTA;
+#else
+    stackLimit -= WORKER_STACK_QUOTA;
+#endif
+    for (size_t i = 0; i < ArrayLength(threadData.ref().nativeStackLimit); i++)
+        threadData.ref().nativeStackLimit[i] = stackLimit;
+
     while (true) {
         JS_ASSERT(!ionBuilder && !asmData);
 
@@ -1113,4 +1138,10 @@ ExclusiveContext::addPendingCompileError()
     MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
 }
 
+void
+ExclusiveContext::addPendingOverRecursed()
+{
+    MOZ_ASSUME_UNREACHABLE("Off thread compilation not available.");
+}
+
 #endif /* JS_WORKER_THREADS */
diff --git a/js/src/jsworkers.h b/js/src/jsworkers.h
index bfec31af59cbe..32bd43716e98c 100644
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -413,6 +413,7 @@ struct ParseTask
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError *> errors;
+    bool overRecursed;
 
     ParseTask(ExclusiveContext *cx, const CompileOptions &options,
               const jschar *chars, size_t length, JSObject *scopeChain,
-- 
GitLab