Bug 650411 - assert in debug/release builds that JSRuntime is only used in a single-threaded manner (r=jorendorff,sr=dmandelin)
authorLuke Wagner <luke@mozilla.com>
Fri, 01 Jul 2011 14:11:31 -0700
changeset 74285 53dd7a930bae8c6366c22dfc6c8716a399172e5c
parent 74284 059713859dcc43dffa1e4f107de6d8830c971aaa
child 74286 5812274f4afbbca6ac2d55e1fecb3521b315cc8d
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersjorendorff, dmandelin
bugs650411
milestone8.0a1
Bug 650411 - assert in debug/release builds that JSRuntime is only used in a single-threaded manner (r=jorendorff,sr=dmandelin)
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/Makefile.in
js/src/jsapi-tests/testThreadGC.cpp
js/src/jsapi-tests/testThreads.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/shell/jsworkers.h
js/src/tests/js1_8_5/extensions/worker-fib.js
js/src/tests/tests.py
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpccallcontext.cpp
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcprivate.h
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -5342,16 +5342,18 @@ CClosure::ClosureStub(ffi_cif* cif, void
   JSObject* thisObj = cinfo->thisObj;
   JSObject* jsfnObj = cinfo->jsfnObj;
 
   ScopedContextThread scopedThread(cx);
 
   // Assert that we're on the thread we were created from.
   JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx));
 
+  JS_AbortIfWrongThread(JS_GetRuntime(cx));
+
   JSAutoRequest ar(cx);
 
   JSAutoEnterCompartment ac;
   if (!ac.enter(cx, jsfnObj))
     return;
 
   // Assert that our CIFs agree.
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
--- a/js/src/jsapi-tests/Makefile.in
+++ b/js/src/jsapi-tests/Makefile.in
@@ -75,18 +75,16 @@ CPPSRCS = \
   testPropCache.cpp \
   testRegExp.cpp \
   testResolveRecursion.cpp \
   testSameValue.cpp \
   testScriptInfo.cpp \
   testScriptObject.cpp \
   testSetProperty.cpp \
   testStringBuffer.cpp \
-  testThreadGC.cpp \
-  testThreads.cpp \
   testTrap.cpp \
   testUTF8.cpp \
   testVersion.cpp \
   testXDR.cpp \
   testCustomIterator.cpp \
   testExternalStrings.cpp \
   testChromeBuffer.cpp \
   $(NULL)
deleted file mode 100644
--- a/js/src/jsapi-tests/testThreadGC.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=99:
- */
-
-#ifdef JS_THREADSAFE
-
-#include "tests.h"
-#include "prthread.h"
-
-#include "jscntxt.h"
-
-/*
- * We test that if a GC callback cancels the GC on a child thread the GC can
- * still proceed on the main thread even if the child thread continue to
- * run uninterrupted.
- */
-
-struct SharedData {
-    enum ChildState {
-        CHILD_STARTING,
-        CHILD_RUNNING,
-        CHILD_DONE,
-        CHILD_ERROR
-    };
-
-    JSRuntime   *const runtime;
-    PRThread    *const mainThread;
-    PRLock      *const lock;
-    PRCondVar   *const signal;
-    ChildState  childState;
-    bool        childShouldStop;
-    JSContext   *childContext;
-
-    SharedData(JSRuntime *rt, bool *ok)
-      : runtime(rt),
-        mainThread(PR_GetCurrentThread()),
-        lock(PR_NewLock()),
-        signal(lock ? PR_NewCondVar(lock) : NULL),
-        childState(CHILD_STARTING),
-        childShouldStop(false),
-        childContext(NULL)
-    {
-        JS_ASSERT(!*ok);
-        *ok = !!signal;
-    }
-
-    ~SharedData() {
-        if (signal)
-            PR_DestroyCondVar(signal);
-        if (lock)
-            PR_DestroyLock(lock);
-    }
-};
-
-static SharedData *shared;
-
-static JSBool
-CancelNonMainThreadGCCallback(JSContext *cx, JSGCStatus status)
-{
-    return status != JSGC_BEGIN || PR_GetCurrentThread() == shared->mainThread;
-}
-
-static JSBool
-StopChildOperationCallback(JSContext *cx)
-{
-    bool shouldStop;
-    PR_Lock(shared->lock);
-    shouldStop = shared->childShouldStop;
-    PR_Unlock(shared->lock);
-    return !shouldStop;
-}
-
-static JSBool
-NotifyMainThreadAboutBusyLoop(JSContext *cx, uintN argc, jsval *vp)
-{
-    PR_Lock(shared->lock);
-    JS_ASSERT(shared->childState == SharedData::CHILD_STARTING);
-    shared->childState = SharedData::CHILD_RUNNING;
-    shared->childContext = cx;
-    PR_NotifyCondVar(shared->signal);
-    PR_Unlock(shared->lock);
-
-    return true;
-}
-
-static void
-ChildThreadMain(void *arg)
-{
-    JS_ASSERT(!arg);
-    bool error = true;
-    JSContext *cx = JS_NewContext(shared->runtime, 8192);
-    if (cx) {
-        JS_SetOperationCallback(cx, StopChildOperationCallback);
-        JSAutoRequest ar(cx);
-        JSObject *global = JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(),
-                                                            NULL);
-        if (global) {
-            JS_SetGlobalObject(cx, global);
-            if (JS_InitStandardClasses(cx, global) &&
-                JS_DefineFunction(cx, global, "notify", NotifyMainThreadAboutBusyLoop, 0, 0)) {
-
-                jsval rval;
-                static const char code[] = "var i = 0; notify(); for (var i = 0; ; ++i);";
-                JSBool ok = JS_EvaluateScript(cx, global, code, strlen(code),
-                                              __FILE__, __LINE__, &rval);
-                if (!ok && !JS_IsExceptionPending(cx)) {
-                    /* Evaluate should only return via the callback cancellation. */
-                    error = false;
-                }
-            }
-        }
-    }
-
-    PR_Lock(shared->lock);
-    shared->childState = error ? SharedData::CHILD_DONE : SharedData::CHILD_ERROR;
-    shared->childContext = NULL;
-    PR_NotifyCondVar(shared->signal);
-    PR_Unlock(shared->lock);
-
-    if (cx)
-        JS_DestroyContextNoGC(cx);
-}
-
-BEGIN_TEST(testThreadGC_bug590533)
-    {
-        /*
-         * Test the child thread busy running while the current thread calls
-         * the GC both with JSRuntime->gcIsNeeded set and unset.
-         */
-        bool ok = TestChildThread(true);
-        CHECK(ok);
-        ok = TestChildThread(false);
-        CHECK(ok);
-        return ok;
-    }
-
-    bool TestChildThread(bool setGCIsNeeded)
-    {
-        bool ok = false;
-        shared = new SharedData(rt, &ok);
-        CHECK(ok);
-
-        JSGCCallback oldGCCallback = JS_SetGCCallback(cx, CancelNonMainThreadGCCallback);
-
-        PRThread *thread =
-            PR_CreateThread(PR_USER_THREAD, ChildThreadMain, NULL,
-                            PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
-        if (!thread)
-            return false;
-
-        PR_Lock(shared->lock);
-        while (shared->childState == SharedData::CHILD_STARTING)
-            PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT);
-        JS_ASSERT(shared->childState != SharedData::CHILD_DONE);
-        ok = (shared->childState == SharedData::CHILD_RUNNING);
-        PR_Unlock(shared->lock);
-
-        CHECK(ok);
-
-        if (setGCIsNeeded) {
-            /*
-             * Use JS internal API to set the GC trigger flag after we know
-             * that the child is in a request and is about to run an infinite
-             * loop. Then run the GC with JSRuntime->gcIsNeeded flag set.
-             */
-            js::AutoLockGC lock(rt);
-            js::TriggerGC(rt);
-        }
-
-        JS_GC(cx);
-
-        PR_Lock(shared->lock);
-        shared->childShouldStop = true;
-        while (shared->childState == SharedData::CHILD_RUNNING) {
-            JS_TriggerOperationCallback(shared->childContext);
-            PR_WaitCondVar(shared->signal, PR_INTERVAL_NO_TIMEOUT);
-        }
-        JS_ASSERT(shared->childState != SharedData::CHILD_STARTING);
-        ok = (shared->childState == SharedData::CHILD_DONE);
-        PR_Unlock(shared->lock);
-
-        JS_SetGCCallback(cx, oldGCCallback);
-
-        PR_JoinThread(thread);
-
-        delete shared;
-        shared = NULL;
-
-        return true;
-    }
-
-
-END_TEST(testThreadGC_bug590533)
-
-#endif
deleted file mode 100644
--- a/js/src/jsapi-tests/testThreads.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=99:
- */
-
-#ifdef JS_THREADSAFE
-
-#include "tests.h"
-#include "prthread.h"
-
-struct ThreadData {
-    JSRuntime *rt;
-    JSObject *obj;
-    const char *code;
-    bool ok;
-};
-
-BEGIN_TEST(testThreads_bug561444)
-    {
-        const char *code = "<a><b/></a>.b.@c = '';";
-        EXEC(code);
-
-        jsrefcount rc = JS_SuspendRequest(cx);
-        {
-            ThreadData data = {rt, global, code, false};
-            PRThread *thread = 
-                PR_CreateThread(PR_USER_THREAD, threadMain, &data,
-                                PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
-            CHECK(thread);
-            PR_JoinThread(thread);
-            CHECK(data.ok);
-        }
-        JS_ResumeRequest(cx, rc);
-        return true;
-    }
-
-    static void threadMain(void *arg) {
-        ThreadData *d = (ThreadData *) arg;
-
-        JSContext *cx = JS_NewContext(d->rt, 8192);
-        if (!cx)
-            return;
-        JS_BeginRequest(cx);
-        {
-            JSAutoEnterCompartment ac;
-            jsval v;
-            d->ok = ac.enter(cx, d->obj) &&
-                    JS_EvaluateScript(cx, d->obj, d->code, strlen(d->code), __FILE__, __LINE__,
-                                      &v);
-        }
-        JS_DestroyContext(cx);
-    }
-END_TEST(testThreads_bug561444)
-
-const PRUint32 NATIVE_STACK_SIZE = 64 * 1024;
-const PRUint32 NATIVE_STACK_HEADROOM = 8 * 1024;
-
-template <class T>
-class Repeat {
-    size_t n;
-    const T &t;
-
-  public:
-    Repeat(size_t n, const T &t) : n(n), t(t) {}
-
-    bool operator()() const {
-	for (size_t i = 0; i < n; i++)
-	    if (!t())
-		return false;
-	return true;
-    }
-};
-
-template <class T> Repeat<T> repeat(size_t n, const T &t) { return Repeat<T>(n, t); }
-
-/* Class of callable that does something in n parallel threads. */
-template <class T>
-class Parallel {
-    size_t n;
-    const T &t;
-
-    struct pair { const Parallel *self; bool ok; };
-
-    static void threadMain(void *arg) {
-	pair *p = (pair *) arg;
-	if (!p->self->t())
-	    p->ok = false;
-    }
-
-  public:
-    Parallel(size_t n, const T &t) : n(n), t(t) {}
-
-    bool operator()() const {
-	pair p = {this, true};
-
-        PRThread **thread = new PRThread *[n];
-	if (!thread)
-	    return false;
-
-        size_t i;
-        for (i = 0; i < n; i++) {
-            thread[i] = PR_CreateThread(PR_USER_THREAD, threadMain, &p, PR_PRIORITY_NORMAL,
-                                        PR_LOCAL_THREAD, PR_JOINABLE_THREAD, NATIVE_STACK_SIZE);
-            if (thread[i] == NULL) {
-                p.ok = false;
-                break;
-            }
-        }
-        while (i--)
-            PR_JoinThread(thread[i]);
-
-	delete[] thread;
-        return p.ok;
-    }
-};
-
-template <class T> Parallel<T> parallel(size_t n, const T &t) { return Parallel<T>(n, t); }
-
-/* Class of callable that creates a compartment and runs some code in it. */
-class eval {
-    JSRuntime *rt;
-    const char *code;
-
-  public:
-    eval(JSRuntime *rt, const char *code) : rt(rt), code(code) {}
-
-    bool operator()() const {
-        JSContext *cx = JS_NewContext(rt, 8192);
-	if (!cx)
-	    return false;
-
-        JS_SetNativeStackQuota(cx, NATIVE_STACK_SIZE - NATIVE_STACK_HEADROOM);
-        bool ok = false;
-	{
-	    JSAutoRequest ar(cx);
-	    JSObject *global =
-		JS_NewCompartmentAndGlobalObject(cx, JSAPITest::basicGlobalClass(), NULL);
-	    if (global) {
-		JS_SetGlobalObject(cx, global);
-		jsval rval;
-		ok = JS_InitStandardClasses(cx, global) &&
-		    JS_EvaluateScript(cx, global, code, strlen(code), "", 0, &rval);
-	    }
-	}
-	JS_DestroyContextMaybeGC(cx);
-        return ok;
-    }
-};
-
-BEGIN_TEST(testThreads_bug604782)
-{
-    jsrefcount rc = JS_SuspendRequest(cx);
-    bool ok = repeat(20, parallel(3, eval(rt, "for(i=0;i<1000;i++);")))();
-    JS_ResumeRequest(cx, rc);
-    CHECK(ok);
-    return true;
-}
-END_TEST(testThreads_bug604782)
-
-BEGIN_TEST(testThreads_bug609103)
-{
-    const char *code = 
-        "var x = {};\n"
-        "for (var i = 0; i < 10000; i++)\n"
-        "    x = {next: x};\n";
-
-    jsrefcount rc = JS_SuspendRequest(cx);
-    bool ok = parallel(2, eval(rt, code))();
-    JS_ResumeRequest(cx, rc);
-    CHECK(ok);
-    return true;
-}
-END_TEST(testThreads_bug609103)
-
-#endif
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -591,30 +591,33 @@ JS_GetTypeName(JSContext *cx, JSType typ
     if ((uintN)type >= (uintN)JSTYPE_LIMIT)
         return NULL;
     return JS_TYPE_STR(type);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
 {
+    CHECK_REQUEST(cx);
     assertSameCompartment(cx, v1, v2);
     return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LooselyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
 {
+    CHECK_REQUEST(cx);
     assertSameCompartment(cx, v1, v2);
     return LooselyEqual(cx, Valueify(v1), Valueify(v2), equal);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
 {
+    CHECK_REQUEST(cx);
     assertSameCompartment(cx, v1, v2);
     return SameValue(cx, Valueify(v1), Valueify(v2), same);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsBuiltinEvalFunction(JSFunction *fun)
 {
     return IsAnyBuiltinEval(fun);
@@ -642,16 +645,20 @@ JSRuntime::JSRuntime()
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&trapList);
 }
 
 bool
 JSRuntime::init(uint32 maxbytes)
 {
+#ifdef JS_THREADSAFE
+    ownerThread_ = js_CurrentThreadId();
+#endif
+
 #ifdef JS_METHODJIT_SPEW
     JMCheckLogging();
 #endif
 
 #ifdef JS_TRACER
     InitJIT();
 #endif
 
@@ -737,16 +744,38 @@ JSRuntime::~JSRuntime()
         JS_DESTROY_LOCK(rtLock);
     if (stateChange)
         JS_DESTROY_CONDVAR(stateChange);
     if (debuggerLock)
         JS_DESTROY_LOCK(debuggerLock);
 #endif
 }
 
+#ifdef JS_THREADSAFE
+void
+JSRuntime::setOwnerThread()
+{
+    JS_ASSERT(ownerThread_ == (void *)-1);
+    ownerThread_ = js_CurrentThreadId();
+}
+
+void
+JSRuntime::clearOwnerThread()
+{
+    JS_ASSERT(onOwnerThread());
+    ownerThread_ = (void *)-1;
+}
+
+JS_FRIEND_API(bool)
+JSRuntime::onOwnerThread() const
+{
+    return ownerThread_ == js_CurrentThreadId();
+}
+#endif
+
 JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32 maxbytes)
 {
     if (!js_NewRuntimeWasCalled) {
 #ifdef DEBUG
         /*
          * This code asserts that the numbers associated with the error names
          * in jsmsg.def are monotonically increasing.  It uses values for the
@@ -1993,22 +2022,24 @@ JS_ComputeThis(JSContext *cx, jsval *vp)
     if (!BoxNonStrictThis(cx, call))
         return JSVAL_NULL;
     return Jsvalify(call.thisv());
 }
 
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
+    CHECK_REQUEST(cx);
     return cx->malloc_(nbytes);
 }
 
 JS_PUBLIC_API(void *)
 JS_realloc(JSContext *cx, void *p, size_t nbytes)
 {
+    CHECK_REQUEST(cx);
     return cx->realloc_(p, nbytes);
 }
 
 JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p)
 {
     return cx->free_(p);
 }
@@ -5327,22 +5358,26 @@ extern JS_PUBLIC_API(const jschar *)
 JS_GetFlatStringChars(JSFlatString *str)
 {
     return str->chars();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
 {
+    CHECK_REQUEST(cx);
+
     return CompareStrings(cx, str1, str2, result);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match)
 {
+    CHECK_REQUEST(cx);
+
     JSLinearString *linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
     *match = StringEqualsAscii(linearStr, asciiBytes);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
@@ -5518,32 +5553,36 @@ JS_ParseJSONWithReviver(JSContext *cx, c
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes,
                        uint32 version, jsval *vp,
                        const JSStructuredCloneCallbacks *optionalCallbacks,
                        void *closure)
 {
+    CHECK_REQUEST(cx);
+
     if (version > JS_STRUCTURED_CLONE_VERSION) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION);
         return false;
     }
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime->structuredCloneCallbacks;
     return ReadStructuredClone(cx, buf, nbytes, Valueify(vp), callbacks, closure);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp,
                         const JSStructuredCloneCallbacks *optionalCallbacks,
                         void *closure)
 {
+    CHECK_REQUEST(cx);
+
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime->structuredCloneCallbacks;
     return WriteStructuredClone(cx, Valueify(v), (uint64_t **) bufp, nbytesp,
                                 callbacks, closure);
 }
 
@@ -6108,16 +6147,18 @@ JS_GetContextThread(JSContext *cx)
 
 /*
  * Set the current thread as the owning thread of a context. Returns the
  * old owning thread id, or -1 if the operation failed.
  */
 JS_PUBLIC_API(jsword)
 JS_SetContextThread(JSContext *cx)
 {
+    JS_AbortIfWrongThread(cx->runtime);
+
 #ifdef JS_THREADSAFE
     JS_ASSERT(!cx->outstandingRequests);
     if (cx->thread()) {
         JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
         return reinterpret_cast<jsword>(cx->thread()->id);
     }
 
     if (!js_InitContextThreadAndLockGC(cx)) {
@@ -6125,19 +6166,46 @@ JS_SetContextThread(JSContext *cx)
         return -1;
     }
 
     JS_UNLOCK_GC(cx->runtime);
 #endif
     return 0;
 }
 
+extern JS_PUBLIC_API(void)
+JS_ClearRuntimeThread(JSRuntime *rt)
+{
+#ifdef JS_THREADSAFE
+    rt->clearOwnerThread();
+#endif
+}
+
+extern JS_PUBLIC_API(void)
+JS_SetRuntimeThread(JSRuntime *rt)
+{
+#ifdef JS_THREADSAFE
+    rt->setOwnerThread();
+#endif
+}
+
+extern JS_PUBLIC_API(void)
+JS_AbortIfWrongThread(JSRuntime *rt)
+{
+#ifdef JS_THREADSAFE
+    if (!rt->onOwnerThread())
+        JS_Assert("rt->onOwnerThread()", __FILE__, __LINE__);
+#endif
+}
+
 JS_PUBLIC_API(jsword)
 JS_ClearContextThread(JSContext *cx)
 {
+    JS_AbortIfWrongThread(cx->runtime);
+
 #ifdef JS_THREADSAFE
     /*
      * cx must have exited all requests it entered and, if cx is associated
      * with a thread, this must be called only from that thread.  If not, this
      * is a harmless no-op.
      */
     JS_ASSERT(cx->outstandingRequests == 0);
     JSThread *t = cx->thread();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3724,16 +3724,61 @@ extern JS_PUBLIC_API(jsword)
 JS_GetContextThread(JSContext *cx);
 
 extern JS_PUBLIC_API(jsword)
 JS_SetContextThread(JSContext *cx);
 
 extern JS_PUBLIC_API(jsword)
 JS_ClearContextThread(JSContext *cx);
 
+/*
+ * A JS runtime always has an "owner thread". The owner thread is set when the
+ * runtime is created (to the current thread) and practically all entry points
+ * into the JS engine check that a runtime (or anything contained in the
+ * runtime: context, compartment, object, etc) is only touched by its owner
+ * thread. Embeddings may check this invariant outside the JS engine by calling
+ * JS_AbortIfWrongThread (which will abort if not on the owner thread, even for
+ * non-debug builds).
+ *
+ * It is possible to "move" a runtime between threads. This is accomplished by
+ * calling JS_ClearRuntimeThread on a runtime's owner thread and then calling
+ * JS_SetRuntimeThread on the new owner thread. The runtime must not be
+ * accessed between JS_ClearRuntimeThread and JS_SetRuntimeThread. Also, the
+ * caller is responsible for synchronizing the calls to Set/Clear.
+ */
+
+extern JS_PUBLIC_API(void)
+JS_AbortIfWrongThread(JSRuntime *rt);
+
+extern JS_PUBLIC_API(void)
+JS_ClearRuntimeThread(JSRuntime *rt);
+
+extern JS_PUBLIC_API(void)
+JS_SetRuntimeThread(JSRuntime *rt);
+
+#ifdef __cplusplus
+JS_END_EXTERN_C
+
+class JSAutoSetRuntimeThread
+{
+    JSRuntime *runtime;
+
+  public:
+    JSAutoSetRuntimeThread(JSRuntime *runtime) : runtime(runtime) {
+        JS_SetRuntimeThread(runtime);
+    }
+
+    ~JSAutoSetRuntimeThread() {
+        JS_ClearRuntimeThread(runtime);
+    }
+};
+
+JS_BEGIN_EXTERN_C
+#endif
+
 /************************************************************************/
 
 /*
  * JS_IsConstructing must be called from within a native given the
  * native's original cx and vp arguments. If JS_IsConstructing is true,
  * JS_THIS must not be used; the constructor should construct and return a
  * new object. Otherwise, the native is called as an ordinary function and
  * JS_THIS may be used.
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -311,16 +311,18 @@ js_PurgeThreads(JSContext *cx)
 }
 
 static const size_t ARENA_HEADER_SIZE_HACK = 40;
 static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
 
 JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
+    JS_AbortIfWrongThread(rt);
+
     JSContext *cx;
     JSBool first;
     JSContextCallback cxCallback;
 
     /*
      * We need to initialize the new context fully before adding it to the
      * runtime list. After that it can be accessed from another thread via
      * js_ContextIterator.
@@ -422,23 +424,24 @@ js_NewContext(JSRuntime *rt, size_t stac
     }
 
     return cx;
 }
 
 void
 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
 {
-    JSRuntime *rt;
+    JSRuntime *rt = cx->runtime;
+    JS_AbortIfWrongThread(rt);
+
     JSContextCallback cxCallback;
     JSBool last;
 
     JS_ASSERT(!cx->enumerators);
 
-    rt = cx->runtime;
 #ifdef JS_THREADSAFE
     /*
      * For API compatibility we allow to destroy contexts without a thread in
      * optimized builds. We assume that the embedding knows that an OOM error
      * cannot happen in JS_SetContextThread.
      */
     JS_ASSERT(cx->thread() && CURRENT_THREAD_IS_ME(cx->thread()));
     if (!cx->thread())
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -322,29 +322,44 @@ typedef void
 (* JSActivityCallback)(void *arg, JSBool active);
 
 namespace js {
 
 typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> CompartmentVector;
 
 }
 
-struct JSRuntime {
+struct JSRuntime
+{
     /* Default compartment. */
     JSCompartment       *atomsCompartment;
 #ifdef JS_THREADSAFE
     bool                atomsCompartmentIsLocked;
 #endif
 
     /* List of compartments (protected by the GC lock). */
     js::CompartmentVector compartments;
 
     /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
     JSRuntimeState      state;
 
+    /* See comment for JS_AbortIfWrongThread in jsapi.h. */
+#ifdef JS_THREADSAFE
+  public:
+    void clearOwnerThread();
+    void setOwnerThread();
+    JS_FRIEND_API(bool) onOwnerThread() const;
+  private:
+    void                *ownerThread_;
+  public:
+#else
+  public:
+    bool onOwnerThread() const { return true; }
+#endif
+
     /* Context create/destroy callback. */
     JSContextCallback   cxCallback;
 
     /* Compartment create/destroy callback. */
     JSCompartmentCallback compartmentCallback;
 
     /*
      * Sets a callback that is run whenever the runtime goes idle - the
@@ -1329,21 +1344,21 @@ class AutoCheckRequestDepth {
         JS_ASSERT(cx->thread()->checkRequestDepth != 0);
         cx->thread()->checkRequestDepth--;
     }
 };
 
 # define CHECK_REQUEST(cx)                                                    \
     JS_ASSERT((cx)->thread());                                                \
     JS_ASSERT((cx)->thread()->data.requestDepth || (cx)->thread() == (cx)->runtime->gcThread); \
+    JS_ASSERT(cx->runtime->onOwnerThread());                                  \
     AutoCheckRequestDepth _autoCheckRequestDepth(cx);
 
 #else
 # define CHECK_REQUEST(cx)          ((void) 0)
-# define CHECK_REQUEST_THREAD(cx)   ((void) 0)
 #endif
 
 static inline JSAtom **
 FrameAtomBase(JSContext *cx, js::StackFrame *fp)
 {
     return fp->hasImacropc()
            ? cx->runtime->atomState.commonAtomsStart()
            : fp->script()->atomMap.vector;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1900,16 +1900,17 @@ TriggerCompartmentGC(JSCompartment *comp
     rt->gcTriggerCompartment = comp;
     TriggerAllOperationCallbacks(comp->rt);
 }
 
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
+    JS_ASSERT(rt->onOwnerThread());
 
     if (rt->gcZeal()) {
         GCREASON(MAYBEGC);
         js_GC(cx, NULL, GC_NORMAL);
         return;
     }
 
     JSCompartment *comp = cx->compartment;
@@ -2666,16 +2667,17 @@ struct GCCrashData
     int isRegen;
     int isCompartment;
 };
 
 void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
+    JS_AbortIfWrongThread(rt);
 
     /*
      * Don't collect garbage if the runtime isn't up, and cx is not the last
      * context in the runtime.  The last context must force a GC, and nothing
      * should suppress that final collection or there may be shutdown leaks,
      * or runtime bloat until the next context is created.
      */
     if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
@@ -2851,16 +2853,18 @@ IterateCompartmentsArenasCells(JSContext
 }
 
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
     JSRuntime *rt = cx->runtime;
+    JS_AbortIfWrongThread(rt);
+
     JSCompartment *compartment = cx->new_<JSCompartment>(rt);
     if (compartment && compartment->init()) {
         // Any compartment with the trusted principals -- and there can be
         // multiple -- is a system compartment.
         compartment->isSystemCompartment = principals && rt->trustedPrincipals() == principals;
         if (principals) {
             compartment->principals = principals;
             JSPRINCIPALS_HOLD(cx, principals);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1106,17 +1106,17 @@ Help(JSContext *cx, uintN argc, jsval *v
 static JSBool
 Quit(JSContext *cx, uintN argc, jsval *vp)
 {
     JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &gExitCode);
 
     gQuitting = JS_TRUE;
 #ifdef JS_THREADSAFE
     if (gWorkerThreadPool)
-        js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool);
+        js::workers::terminateAll(gWorkerThreadPool);
 #endif
     return JS_FALSE;
 }
 
 static const char *
 ToSource(JSContext *cx, jsval *vp, JSAutoByteString *bytes)
 {
     JSString *str = JS_ValueToSource(cx, *vp);
@@ -3228,255 +3228,16 @@ Sleep_fn(JSContext *cx, uintN argc, jsva
                 break;
             t_ticks = to_wakeup - now;
         }
         PR_Unlock(gWatchdogLock);
     }
     return !gCanceled;
 }
 
-typedef struct ScatterThreadData ScatterThreadData;
-typedef struct ScatterData ScatterData;
-
-typedef enum ScatterStatus {
-    SCATTER_WAIT,
-    SCATTER_GO,
-    SCATTER_CANCEL
-} ScatterStatus;
-
-struct ScatterData {
-    ScatterThreadData   *threads;
-    jsval               *results;
-    PRLock              *lock;
-    PRCondVar           *cvar;
-    ScatterStatus       status;
-};
-
-struct ScatterThreadData {
-    jsint               index;
-    ScatterData         *shared;
-    PRThread            *thr;
-    JSContext           *cx;
-    jsval               fn;
-};
-
-static void
-DoScatteredWork(JSContext *cx, ScatterThreadData *td)
-{
-    jsval *rval = &td->shared->results[td->index];
-
-    if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
-        *rval = JSVAL_VOID;
-        JS_GetPendingException(cx, rval);
-        JS_ClearPendingException(cx);
-    }
-}
-
-static void
-RunScatterThread(void *arg)
-{
-    int stackDummy;
-    ScatterThreadData *td;
-    ScatterStatus st;
-    JSContext *cx;
-
-    if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
-        return;
-
-    td = (ScatterThreadData *)arg;
-    cx = td->cx;
-
-    /* Wait for our signal. */
-    PR_Lock(td->shared->lock);
-    while ((st = td->shared->status) == SCATTER_WAIT)
-        PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
-    PR_Unlock(td->shared->lock);
-
-    if (st == SCATTER_CANCEL)
-        return;
-
-    /* We are good to go. */
-    JS_SetContextThread(cx);
-    JS_SetNativeStackQuota(cx, gMaxStackSize);
-    JS_BeginRequest(cx);
-    DoScatteredWork(cx, td);
-    JS_EndRequest(cx);
-    JS_ClearContextThread(cx);
-}
-
-/*
- * scatter(fnArray) - Call each function in `fnArray` without arguments, each
- * in a different thread. When all threads have finished, return an array: the
- * return values. Errors are not propagated; if any of the function calls
- * fails, the corresponding element in the results array gets the exception
- * object, if any, else (undefined).
- */
-static JSBool
-Scatter(JSContext *cx, uintN argc, jsval *vp)
-{
-    jsuint i;
-    jsuint n;  /* number of threads */
-    JSObject *inArr;
-    JSObject *arr;
-    JSObject *global;
-    ScatterData sd;
-    JSBool ok;
-
-    sd.lock = NULL;
-    sd.cvar = NULL;
-    sd.results = NULL;
-    sd.threads = NULL;
-    sd.status = SCATTER_WAIT;
-
-    if (argc == 0 || JSVAL_IS_PRIMITIVE(JS_ARGV(cx, vp)[0])) {
-        JS_ReportError(cx, "the first argument must be an object");
-        goto fail;
-    }
-
-    inArr = JSVAL_TO_OBJECT(JS_ARGV(cx, vp)[0]);
-    ok = JS_GetArrayLength(cx, inArr, &n);
-    if (!ok)
-        goto out;
-    if (n == 0)
-        goto success;
-
-    sd.lock = PR_NewLock();
-    if (!sd.lock)
-        goto fail;
-
-    sd.cvar = PR_NewCondVar(sd.lock);
-    if (!sd.cvar)
-        goto fail;
-
-    sd.results = (jsval *) malloc(n * sizeof(jsval));
-    if (!sd.results)
-        goto fail;
-    for (i = 0; i < n; i++) {
-        sd.results[i] = JSVAL_VOID;
-        ok = JS_AddValueRoot(cx, &sd.results[i]);
-        if (!ok) {
-            while (i-- > 0)
-                JS_RemoveValueRoot(cx, &sd.results[i]);
-            free(sd.results);
-            sd.results = NULL;
-            goto fail;
-        }
-    }
-
-    sd.threads = (ScatterThreadData *) malloc(n * sizeof(ScatterThreadData));
-    if (!sd.threads)
-        goto fail;
-    for (i = 0; i < n; i++) {
-        sd.threads[i].index = i;
-        sd.threads[i].shared = &sd;
-        sd.threads[i].thr = NULL;
-        sd.threads[i].cx = NULL;
-        sd.threads[i].fn = JSVAL_NULL;
-
-        ok = JS_AddValueRoot(cx, &sd.threads[i].fn);
-        if (ok && !JS_GetElement(cx, inArr, i, &sd.threads[i].fn)) {
-            JS_RemoveValueRoot(cx, &sd.threads[i].fn);
-            ok = JS_FALSE;
-        }
-        if (!ok) {
-            while (i-- > 0)
-                JS_RemoveValueRoot(cx, &sd.threads[i].fn);
-            free(sd.threads);
-            sd.threads = NULL;
-            goto fail;
-        }
-    }
-
-    global = JS_GetGlobalObject(cx);
-    for (i = 1; i < n; i++) {
-        JSContext *newcx = NewContext(JS_GetRuntime(cx));
-        if (!newcx)
-            goto fail;
-
-        {
-            JSAutoRequest req(newcx);
-            JS_SetGlobalObject(newcx, global);
-        }
-        JS_ClearContextThread(newcx);
-        sd.threads[i].cx = newcx;
-    }
-
-    for (i = 1; i < n; i++) {
-        PRThread *t = PR_CreateThread(PR_USER_THREAD,
-                                      RunScatterThread,
-                                      &sd.threads[i],
-                                      PR_PRIORITY_NORMAL,
-                                      PR_GLOBAL_THREAD,
-                                      PR_JOINABLE_THREAD,
-                                      0);
-        if (!t) {
-            /* Failed to start thread. */
-            PR_Lock(sd.lock);
-            sd.status = SCATTER_CANCEL;
-            PR_NotifyAllCondVar(sd.cvar);
-            PR_Unlock(sd.lock);
-            while (i-- > 1)
-                PR_JoinThread(sd.threads[i].thr);
-            goto fail;
-        }
-
-        sd.threads[i].thr = t;
-    }
-    PR_Lock(sd.lock);
-    sd.status = SCATTER_GO;
-    PR_NotifyAllCondVar(sd.cvar);
-    PR_Unlock(sd.lock);
-
-    DoScatteredWork(cx, &sd.threads[0]);
-
-    {
-        JSAutoSuspendRequest suspended(cx);
-        for (i = 1; i < n; i++) {
-            PR_JoinThread(sd.threads[i].thr);
-        }
-    }
-
-success:
-    arr = JS_NewArrayObject(cx, n, sd.results);
-    if (!arr)
-        goto fail;
-    *vp = OBJECT_TO_JSVAL(arr);
-    ok = JS_TRUE;
-
-out:
-    if (sd.threads) {
-        JSContext *acx;
-
-        for (i = 0; i < n; i++) {
-            JS_RemoveValueRoot(cx, &sd.threads[i].fn);
-            acx = sd.threads[i].cx;
-            if (acx) {
-                JS_SetContextThread(acx);
-                DestroyContext(acx, true);
-            }
-        }
-        free(sd.threads);
-    }
-    if (sd.results) {
-        for (i = 0; i < n; i++)
-            JS_RemoveValueRoot(cx, &sd.results[i]);
-        free(sd.results);
-    }
-    if (sd.cvar)
-        PR_DestroyCondVar(sd.cvar);
-    if (sd.lock)
-        PR_DestroyLock(sd.lock);
-
-    return ok;
-
-fail:
-    ok = JS_FALSE;
-    goto out;
-}
-
 static bool
 InitWatchdog(JSRuntime *rt)
 {
     JS_ASSERT(!gWatchdogThread);
     gWatchdogLock = PR_NewLock();
     if (gWatchdogLock) {
         gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
         if (gWatchdogWakeup) {
@@ -3651,17 +3412,17 @@ ScheduleWatchdog(JSRuntime *rt, jsdouble
 static void
 CancelExecution(JSRuntime *rt)
 {
     gCanceled = true;
     if (gExitCode == 0)
         gExitCode = EXITCODE_TIMEOUT;
 #ifdef JS_THREADSAFE
     if (gWorkerThreadPool)
-        js::workers::terminateAll(rt, gWorkerThreadPool);
+        js::workers::terminateAll(gWorkerThreadPool);
 #endif
     JS_TriggerAllOperationCallbacks(rt);
 
     static const char msg[] = "Script runs for too long, terminating.\n";
 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
     /* It is not safe to call fputs from signals. */
     /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
     ssize_t dummy = write(2, msg, sizeof(msg) - 1);
@@ -4192,17 +3953,16 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("startTraceVis",  StartTraceVisNative, 1,0),
     JS_FN("stopTraceVis",   StopTraceVisNative,  0,0),
 #endif
 #ifdef DEBUG
     JS_FN("arrayInfo",      js_ArrayInfo,   1,0),
 #endif
 #ifdef JS_THREADSAFE
     JS_FN("sleep",          Sleep_fn,       1,0),
-    JS_FN("scatter",        Scatter,        1,0),
 #endif
     JS_FN("snarf",          Snarf,          0,0),
     JS_FN("read",           Snarf,          0,0),
     JS_FN("compile",        Compile,        1,0),
     JS_FN("parse",          Parse,          1,0),
     JS_FN("timeout",        Timeout,        1,0),
     JS_FN("elapsed",        Elapsed,        0,0),
     JS_FN("parent",         Parent,         1,0),
@@ -4332,17 +4092,16 @@ static const char *const shell_help_mess
 "startTraceVis(filename)  Start TraceVis recording (stops any current recording)",
 "stopTraceVis()           Stop TraceVis recording",
 #endif
 #ifdef DEBUG
 "arrayInfo(a1, a2, ...)   Report statistics about arrays",
 #endif
 #ifdef JS_THREADSAFE
 "sleep(dt)                Sleep for dt seconds",
-"scatter(fns)             Call functions concurrently (ignoring errors)",
 #endif
 "snarf(filename)          Read filename into returned string",
 "read(filename)           Synonym for snarf",
 "compile(code)            Compiles a string to bytecode, potentially throwing",
 "parse(code)              Parses a string, potentially throwing",
 "timeout([seconds])\n"
 "  Get/Set the limit in seconds for the execution time for the current context.\n"
 "  A negative value (default) means that the execution time is unlimited.",
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -165,16 +165,17 @@ class WorkerParent {
     // does not bother with locking.
     void removeChild(Worker *w) {
         ChildSet::Ptr p = children.lookup(w);
         JS_ASSERT(p);
         children.remove(p);
     }
 
     void disposeChildren();
+    void notifyTerminating();
 };
 
 template <class T>
 class ThreadSafeQueue
 {
   protected:
     Queue<T, SystemAllocPolicy> queue;
     JSLock *lock;
@@ -523,21 +524,22 @@ class ThreadPool
                 shutdown(cx);
                 ok = false;
                 break;
             }
         }
         return ok;
     }
 
-    void terminateAll(JSRuntime *rt) {
+    void terminateAll() {
         // See comment about JS_ATOMIC_SET in the implementation of
         // JS_TriggerOperationCallback.
         JS_ATOMIC_SET(&terminating, 1);
-        JS_TriggerAllOperationCallbacks(rt);
+        if (mq)
+            mq->notifyTerminating();
     }
 
     /* This context is used only to free memory. */
     void shutdown(JSContext *cx) {
         wq->close();
         for (int i = 0; i < threadCount; i++) {
             if (threads[i]) {
                 PR_JoinThread(threads[i]);
@@ -588,46 +590,58 @@ class ThreadPool
  * asynchronously to tell the Worker to terminate.
  */
 class Worker : public WorkerParent
 {
   private:
     ThreadPool *threadPool;
     WorkerParent *parent;
     JSObject *object;  // Worker object exposed to parent
+    JSRuntime *runtime;
     JSContext *context;
     JSLock *lock;
     Queue<Event *, SystemAllocPolicy> events;  // owning pointers to pending events
     Event *current;
     bool terminated;
     int32_t terminateFlag;
 
     static JSClass jsWorkerClass;
 
     Worker()
-        : threadPool(NULL), parent(NULL), object(NULL),
+        : threadPool(NULL), parent(NULL), object(NULL), runtime(NULL),
           context(NULL), lock(NULL), current(NULL), terminated(false), terminateFlag(0) {}
 
     bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) {
         JS_ASSERT(!threadPool && !this->parent && !object && !lock);
 
         if (!initWorkerParent() || !parent->addChild(this))
             return false;
         threadPool = parent->getThreadPool();
         this->parent = parent;
         this->object = obj;
         lock = JS_NEW_LOCK();
         return lock &&
+               createRuntime(parentcx) &&
                createContext(parentcx, parent) &&
                JS_SetPrivate(parentcx, obj, this);
     }
 
+    bool createRuntime(JSContext *parentcx) {
+        runtime = JS_NewRuntime(1L * 1024L * 1024L);
+        if (!runtime) {
+            JS_ReportOutOfMemory(parentcx);
+            return false;
+        }
+        JS_ClearRuntimeThread(runtime);
+        return true;
+    }
+
     bool createContext(JSContext *parentcx, WorkerParent *parent) {
-        JSRuntime *rt = JS_GetRuntime(parentcx);
-        context = JS_NewContext(rt, 8192);
+        JSAutoSetRuntimeThread guard(runtime);
+        context = JS_NewContext(runtime, 8192);
         if (!context)
             return false;
 
         // The Worker has a strong reference to the global; see jsTraceWorker.
         // JSOPTION_UNROOTED_GLOBAL ensures that when the worker becomes
         // unreachable, it and its global object can be collected. Otherwise
         // the cx->globalObject root would keep them both alive forever.
         JS_SetOptions(context, JS_GetOptions(parentcx) | JSOPTION_UNROOTED_GLOBAL |
@@ -749,21 +763,27 @@ class Worker : public WorkerParent
     void dispose() {
         JS_ASSERT(!current);
         while (!events.empty())
             events.pop()->destroy(context);
         if (lock) {
             JS_DESTROY_LOCK(lock);
             lock = NULL;
         }
+        if (runtime)
+            JS_SetRuntimeThread(runtime);
         if (context) {
             JS_SetContextThread(context);
             JS_DestroyContextNoGC(context);
             context = NULL;
         }
+        if (runtime) {
+            JS_DestroyRuntime(runtime);
+            runtime = NULL;
+        }
         object = NULL;
 
         // Do not call parent->removeChild(). This is called either from
         // ~Worker, which calls it for us; or from parent->disposeChildren or
         // Worker::create, which require that it not be called.
         parent = NULL;
         disposeChildren();
     }
@@ -792,16 +812,21 @@ class Worker : public WorkerParent
 
     void setTerminateFlag() {
         AutoLock hold(lock);
         terminateFlag = true;
         if (current)
             JS_TriggerOperationCallback(context);
     }
 
+    void notifyTerminating() {
+        setTerminateFlag();
+        WorkerParent::notifyTerminating();
+    }
+
     void processOneEvent();
 
     /* Trace method to be called from C++. */
     void trace(JSTracer *trc) {
         // Just mark the JSObject. If we haven't already been marked,
         // jsTraceWorker will be called, at which point we'll trace referents.
         JS_CALL_OBJECT_TRACER(trc, object, "queued Worker");
     }
@@ -972,16 +997,24 @@ void
 WorkerParent::disposeChildren()
 {
     for (ChildSet::Enum e(children); !e.empty(); e.popFront()) {
         e.front()->dispose();
         e.removeFront();
     }
 }
 
+void
+WorkerParent::notifyTerminating()
+{
+    AutoLock hold(getLock());
+    for (ChildSet::Range r = children.all(); !r.empty(); r.popFront())
+        r.front()->notifyTerminating();
+}
+
 bool
 MainQueue::shouldStop()
 {
     // Note: This deliberately nests WorkerQueue::lock in MainQueue::lock.
     // Releasing MainQueue::lock would risk a race -- isIdle() could return
     // false, but the workers could become idle before we reacquire
     // MainQueue::lock and go to sleep, and we would wait on the condvar
     // forever.
@@ -1105,33 +1138,35 @@ Worker::processOneEvent()
         event = current = events.pop();
     }
 
     JS_SetContextThread(context);
     JS_SetNativeStackQuota(context, gMaxStackSize);
 
     Event::Result result;
     {
-        JSAutoRequest req(context);
+        JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
+        JSAutoRequest ar(context);
         result = event->process(context);
     }
 
     // Note: we have to leave the above request before calling parent->post or
     // checkTermination, both of which acquire locks.
     if (result == Event::forwardToParent) {
         event->setChildAndRecipient(this, parent);
         if (parent->post(event)) {
             event = NULL;  // to prevent it from being deleted below
         } else {
             JS_ReportOutOfMemory(context);
             result = Event::fail;
         }
     }
     if (result == Event::fail && !checkTermination()) {
-        JSAutoRequest req(context);
+        JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
+        JSAutoRequest ar(context);
         Event *err = ErrorEvent::create(context, this);
         if (err && !parent->post(err)) {
             JS_ReportOutOfMemory(context);
             err->destroy(context);
             err = NULL;
         }
         if (!err) {
             // FIXME - out of memory, probably should panic
@@ -1255,19 +1290,19 @@ JSFunctionSpec Worker::jsMethods[3] = {
 
 ThreadPool *
 js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp)
 {
     return Worker::initWorkers(cx, hooks, global, rootp);
 }
 
 void
-js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp)
+js::workers::terminateAll(ThreadPool *tp)
 {
-    tp->terminateAll(rt);
+    tp->terminateAll();
 }
 
 void
 js::workers::finish(JSContext *cx, ThreadPool *tp)
 {
     if (MainQueue *mq = tp->getMainQueue()) {
         JS_ALWAYS_TRUE(mq->mainThreadWork(cx, true));
         tp->shutdown(cx);
--- a/js/src/shell/jsworkers.h
+++ b/js/src/shell/jsworkers.h
@@ -56,31 +56,31 @@ namespace js {
 
         class WorkerHooks {
         public:
             virtual JSObject *newGlobalObject(JSContext *cx) = 0;
             virtual ~WorkerHooks() {}
         };
 
         /*
-	 * Initialize workers. This defines the Worker constructor on global.
-	 * Requires request. rootp must point to a GC root.
-	 *
-	 * On success, *rootp receives a pointer to an object, and init returns
+         * Initialize workers. This defines the Worker constructor on global.
+         * Requires request. rootp must point to a GC root.
+         *
+         * On success, *rootp receives a pointer to an object, and init returns
          * a non-null value. The caller must keep the object rooted and must
          * pass it to js::workers::finish later.
-	 */
+         */
         ThreadPool *init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp);
 
         /* Asynchronously signal for all workers to terminate.
          *
          * Call this before calling finish() to shut down without waiting for
          * all messages to be proceesed.
          */
-        void terminateAll(JSRuntime *rt, ThreadPool *tp);
+        void terminateAll(ThreadPool *tp);
 
 	/*
 	 * Finish running any workers, shut down the thread pool, and free all
 	 * resources associated with workers. The application must call this
 	 * before shutting down the runtime, and not during GC.
 	 *
 	 * Requires request.
 	 */
--- a/js/src/tests/js1_8_5/extensions/worker-fib.js
+++ b/js/src/tests/js1_8_5/extensions/worker-fib.js
@@ -4,15 +4,15 @@
  * http://creativecommons.org/licenses/publicdomain/
  * Contributor: Jason Orendorff <jorendorff@mozilla.com>
  */
 
 if (typeof Worker != 'undefined') {
     JSTest.waitForExplicitFinish();
     var w = Worker(workerDir + "worker-fib-child.js");
     w.onmessage = function (event) {
-        reportCompare("55", event.data, "worker-fib");
+        reportCompare("21", event.data, "worker-fib");
         JSTest.testFinished();
     };
-    w.postMessage("10\t" + workerDir); // 0 1 1 2 3 5 8 13 21 34 55
+    w.postMessage("8\t" + workerDir); // 0 1 1 2 3 5 8 13 21
 } else {
     reportCompare(0, 0, "Test skipped. Shell workers required.");
 }
--- a/js/src/tests/tests.py
+++ b/js/src/tests/tests.py
@@ -12,17 +12,17 @@ def do_run_cmd(cmd):
     th_run_cmd(cmd, l)
     return l[1]
 
 def set_limits():
     # resource module not supported on all platforms
     try:
         import resource
         GB = 2**30
-        resource.setrlimit(resource.RLIMIT_AS, (1*GB, 1*GB))
+        resource.setrlimit(resource.RLIMIT_AS, (2*GB, 2*GB))
     except:
         return
 
 def th_run_cmd(cmd, l):
     t0 = datetime.datetime.now()
 
     # close_fds and preexec_fn are not supported on Windows and will
     # cause a ValueError.
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -757,16 +757,18 @@ mozJSComponentLoader::GlobalForLocation(
                                         char **aLocation,
                                         jsval *exception)
 {
     nsresult rv;
 
     JSPrincipals* jsPrincipals = nsnull;
     JSCLContextHelper cx(this);
 
+    JS_AbortIfWrongThread(JS_GetRuntime(cx));
+
     // preserve caller's compartment
     js::PreserveCompartment pc(cx);
     
     rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSPrincipalsHolder princHolder(mContext, jsPrincipals);
 
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -527,16 +527,44 @@ nsXPConnect::BeginCycleCollection(nsCycl
     NS_ASSERTION(!explainLiveExpectedGarbage, "Didn't call nsXPConnect::Collect()?");
 #endif
 
     GetRuntime()->AddXPConnectRoots(mCycleCollectionContext->GetJSContext(), cb);
 
     return NS_OK;
 }
 
+void
+nsXPConnect::NotifyLeaveMainThread()
+{
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
+    JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
+}
+
+void
+nsXPConnect::NotifyEnterCycleCollectionThread()
+{
+    NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
+    JS_SetRuntimeThread(mRuntime->GetJSRuntime());
+}
+
+void
+nsXPConnect::NotifyLeaveCycleCollectionThread()
+{
+    NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
+    JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
+}
+
+void
+nsXPConnect::NotifyEnterMainThread()
+{
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
+    JS_SetRuntimeThread(mRuntime->GetJSRuntime());
+}
+
 nsresult 
 nsXPConnect::FinishTraverse()
 {
     if (mCycleCollectionContext) {
         JSContext *cx = mCycleCollectionContext->GetJSContext();
         mCycleCollectionContext = nsnull;
         JS_ClearContextThread(cx);
     }
@@ -2439,17 +2467,19 @@ nsXPConnect::DefineDOMQuickStubs(JSConte
 
 /* attribute JSRuntime runtime; */
 NS_IMETHODIMP
 nsXPConnect::GetRuntime(JSRuntime **runtime)
 {
     if(!runtime)
         return NS_ERROR_NULL_POINTER;
 
-    *runtime = GetRuntime()->GetJSRuntime();
+    JSRuntime *rt = GetRuntime()->GetJSRuntime();
+    JS_AbortIfWrongThread(rt);
+    *runtime = rt;
     return NS_OK;
 }
 
 /* attribute nsIXPCScriptable backstagePass; */
 NS_IMETHODIMP
 nsXPConnect::GetBackstagePass(nsIXPCScriptable **bsp)
 {
     if(!mBackstagePass) {
--- a/js/src/xpconnect/src/xpccallcontext.cpp
+++ b/js/src/xpconnect/src/xpccallcontext.cpp
@@ -497,16 +497,17 @@ XPCCallContext::GetCalleeClassInfo(nsICl
     *aCalleeClassInfo = temp;
     return NS_OK;
 }
 
 /* readonly attribute JSContextPtr JSContext; */
 NS_IMETHODIMP
 XPCCallContext::GetJSContext(JSContext * *aJSContext)
 {
+    JS_AbortIfWrongThread(JS_GetRuntime(mJSContext));
     *aJSContext = mJSContext;
     return NS_OK;
 }
 
 /* readonly attribute PRUint32 Argc; */
 NS_IMETHODIMP
 XPCCallContext::GetArgc(PRUint32 *aArgc)
 {
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -3539,16 +3539,18 @@ nsXPCComponents_Utils::EvalInSandbox(con
     return rv;
 }
 
 nsresult
 xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
                   const char *filename, PRInt32 lineNo,
                   JSVersion jsVersion, PRBool returnStringOnly, jsval *rval)
 {
+    JS_AbortIfWrongThread(JS_GetRuntime(cx));
+
 #ifdef DEBUG
     // NB: The "unsafe" unwrap here is OK because we must be called from chrome.
     {
         nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
         if (ssm) {
             JSStackFrame *fp;
             nsIPrincipal *subjectPrincipal =
                 ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -541,16 +541,20 @@ public:
     // nsCycleCollectionParticipant
     NS_IMETHOD Root(void *p);
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
     NS_IMETHOD Traverse(void *p,
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime
+    virtual void NotifyLeaveMainThread();
+    virtual void NotifyEnterCycleCollectionThread();
+    virtual void NotifyLeaveCycleCollectionThread();
+    virtual void NotifyEnterMainThread();
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                           bool explainExpectedLiveGarbage);
     virtual nsresult FinishTraverse();
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
     virtual bool NeedCollect();
     virtual void Collect();
 #ifdef DEBUG_CC
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3443,16 +3443,22 @@ class nsCycleCollectorRunner : public ns
     nsICycleCollectorListener *mListener;
     Mutex mLock;
     CondVar mRequest;
     CondVar mReply;
     PRBool mRunning;
     PRBool mShutdown;
     PRBool mCollected;
 
+    nsCycleCollectionJSRuntime *GetJSRuntime()
+    {
+        return static_cast<nsCycleCollectionJSRuntime*>
+                 (mCollector->mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]);
+    }
+
 public:
     NS_IMETHOD Run()
     {
 #ifdef XP_WIN
         TlsSetValue(gTLSThreadIDIndex,
                     (void*) mozilla::threads::CycleCollector);
 #elif defined(NS_TLS)
         gTLSThreadID = mozilla::threads::CycleCollector;
@@ -3473,17 +3479,19 @@ public:
         while (1) {
             mRequest.Wait();
 
             if (!mRunning) {
                 mReply.Notify();
                 return NS_OK;
             }
 
+            GetJSRuntime()->NotifyEnterCycleCollectionThread();
             mCollected = mCollector->BeginCollection(mListener);
+            GetJSRuntime()->NotifyLeaveCycleCollectionThread();
 
             mReply.Notify();
         }
 
         return NS_OK;
     }
 
     nsCycleCollectorRunner(nsCycleCollector *collector)
@@ -3512,18 +3520,20 @@ public:
 
         nsAutoTArray<PtrInfo*, 4000> whiteNodes;
         if (!mCollector->PrepareForCollection(&whiteNodes))
             return 0;
 
         NS_ASSERTION(!mListener, "Should have cleared this already!");
         mListener = aListener;
 
+        GetJSRuntime()->NotifyLeaveMainThread();
         mRequest.Notify();
         mReply.Wait();
+        GetJSRuntime()->NotifyEnterMainThread();
 
         mListener = nsnull;
 
         if (mCollected) {
             mCollected = mCollector->FinishCollection();
 
             mCollector->CleanupAfterCollection();
 
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -72,16 +72,24 @@ void nsCycleCollector_shutdown();
 // The JS runtime is special, it needs to call cycle collection during its GC.
 // If the JS runtime is registered nsCycleCollector_collect will call
 // nsCycleCollectionJSRuntime::Collect which will call
 // nsCycleCollector_doCollect, else nsCycleCollector_collect will call
 // nsCycleCollector_doCollect directly.
 struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
 {
     /**
+     * Called before/after transitioning to/from the main thread.
+     */
+    virtual void NotifyLeaveMainThread() = 0;
+    virtual void NotifyEnterCycleCollectionThread() = 0;
+    virtual void NotifyLeaveCycleCollectionThread() = 0;
+    virtual void NotifyEnterMainThread() = 0;
+
+    /**
      * Should we force a JavaScript GC before a CC?
      */
     virtual bool NeedCollect() = 0;
 
     /**
      * Runs the JavaScript GC.
      */
     virtual void Collect() = 0;