Backed out changeset 0cf822d12c64 (and dependent be91fb29d950 and 53dd7a930bae) due to intermittent crashes
authorLuke Wagner <luke@mozilla.com>
Mon, 15 Aug 2011 17:21:23 -0700
changeset 75423 5f0596a0b81e10538e29f084a5525825b8dd4229
parent 75422 170e2522e530285e049d9f8e6a0cb17547ae1ba0
child 75424 c5e84e1e62daf3e196e1aadcba60c89ea75f3c3a
push id21023
push usermak77@bonardo.net
push dateThu, 18 Aug 2011 09:39:20 +0000
treeherdermozilla-central@f69a10f23bf3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone9.0a1
backs out0cf822d12c64a1bc22782330a961c72fd34219d0
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
Backed out changeset 0cf822d12c64 (and dependent be91fb29d950 and 53dd7a930bae) due to intermittent crashes
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
security/manager/ssl/src/nsNSSIOLayer.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
xpcom/proxy/src/nsProxyObjectManager.cpp
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -5342,18 +5342,16 @@ 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
@@ -78,16 +78,18 @@ 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 \
   $(NULL)
 
 # Disabled: an entirely unrelated test seems to cause this to fail.  Moreover,
 # given the test's dependence on interactions between the compiler, the GC, and
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testThreadGC.cpp
@@ -0,0 +1,195 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testThreads.cpp
@@ -0,0 +1,174 @@
+/* -*- 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,33 +591,30 @@ 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);
@@ -645,20 +642,16 @@ JSRuntime::JSRuntime()
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&debuggerList);
 }
 
 bool
 JSRuntime::init(uint32 maxbytes)
 {
-#ifdef JS_THREADSAFE
-    ownerThread_ = js_CurrentThreadId();
-#endif
-
 #ifdef JS_METHODJIT_SPEW
     JMCheckLogging();
 #endif
 
 #ifdef JS_TRACER
     InitJIT();
 #endif
 
@@ -738,38 +731,16 @@ JSRuntime::~JSRuntime()
         JS_DESTROY_CONDVAR(requestDone);
     if (rtLock)
         JS_DESTROY_LOCK(rtLock);
     if (stateChange)
         JS_DESTROY_CONDVAR(stateChange);
 #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
@@ -2016,24 +1987,22 @@ 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);
 }
@@ -5359,26 +5328,22 @@ 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)
@@ -5554,36 +5519,32 @@ 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);
 }
 
@@ -6148,18 +6109,16 @@ 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)) {
@@ -6167,46 +6126,19 @@ 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
@@ -3727,61 +3727,16 @@ 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,18 +311,16 @@ 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.
@@ -424,24 +422,23 @@ js_NewContext(JSRuntime *rt, size_t stac
     }
 
     return cx;
 }
 
 void
 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
 {
-    JSRuntime *rt = cx->runtime;
-    JS_AbortIfWrongThread(rt);
-
+    JSRuntime *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,44 +322,29 @@ 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
@@ -1367,21 +1352,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
@@ -1925,17 +1925,16 @@ 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;
@@ -2713,17 +2712,16 @@ 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)
@@ -2900,18 +2898,16 @@ 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(gWorkerThreadPool);
+        js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool);
 #endif
     return JS_FALSE;
 }
 
 static const char *
 ToSource(JSContext *cx, jsval *vp, JSAutoByteString *bytes)
 {
     JSString *str = JS_ValueToSource(cx, *vp);
@@ -3252,16 +3252,255 @@ 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) {
@@ -3436,17 +3675,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(gWorkerThreadPool);
+        js::workers::terminateAll(rt, 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);
@@ -3964,16 +4203,17 @@ 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),
@@ -4090,16 +4330,17 @@ 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,17 +165,16 @@ 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;
@@ -524,22 +523,21 @@ class ThreadPool
                 shutdown(cx);
                 ok = false;
                 break;
             }
         }
         return ok;
     }
 
-    void terminateAll() {
+    void terminateAll(JSRuntime *rt) {
         // See comment about JS_ATOMIC_SET in the implementation of
         // JS_TriggerOperationCallback.
         JS_ATOMIC_SET(&terminating, 1);
-        if (mq)
-            mq->notifyTerminating();
+        JS_TriggerAllOperationCallbacks(rt);
     }
 
     /* 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]);
@@ -590,58 +588,46 @@ 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), runtime(NULL),
+        : threadPool(NULL), parent(NULL), object(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) {
-        JSAutoSetRuntimeThread guard(runtime);
-        context = JS_NewContext(runtime, 8192);
+        JSRuntime *rt = JS_GetRuntime(parentcx);
+        context = JS_NewContext(rt, 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 |
@@ -763,27 +749,21 @@ 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();
     }
@@ -812,21 +792,16 @@ 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");
     }
@@ -997,24 +972,16 @@ 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.
@@ -1138,35 +1105,33 @@ Worker::processOneEvent()
         event = current = events.pop();
     }
 
     JS_SetContextThread(context);
     JS_SetNativeStackQuota(context, gMaxStackSize);
 
     Event::Result result;
     {
-        JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
-        JSAutoRequest ar(context);
+        JSAutoRequest req(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()) {
-        JSAutoSetRuntimeThread asrt(JS_GetRuntime(context));
-        JSAutoRequest ar(context);
+        JSAutoRequest req(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
@@ -1290,19 +1255,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(ThreadPool *tp)
+js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp)
 {
-    tp->terminateAll();
+    tp->terminateAll(rt);
 }
 
 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(ThreadPool *tp);
+        void terminateAll(JSRuntime *rt, 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("21", event.data, "worker-fib");
+        reportCompare("55", event.data, "worker-fib");
         JSTest.testFinished();
     };
-    w.postMessage("8\t" + workerDir); // 0 1 1 2 3 5 8 13 21
+    w.postMessage("10\t" + workerDir); // 0 1 1 2 3 5 8 13 21 34 55
 } 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, (2*GB, 2*GB))
+        resource.setrlimit(resource.RLIMIT_AS, (1*GB, 1*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
@@ -746,18 +746,16 @@ 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,44 +527,16 @@ 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);
     }
@@ -2470,19 +2442,17 @@ nsXPConnect::DefineDOMQuickStubs(JSConte
 
 /* attribute JSRuntime runtime; */
 NS_IMETHODIMP
 nsXPConnect::GetRuntime(JSRuntime **runtime)
 {
     if(!runtime)
         return NS_ERROR_NULL_POINTER;
 
-    JSRuntime *rt = GetRuntime()->GetJSRuntime();
-    JS_AbortIfWrongThread(rt);
-    *runtime = rt;
+    *runtime = GetRuntime()->GetJSRuntime();
     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,17 +497,16 @@ 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,18 +3539,16 @@ 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,20 +541,16 @@ 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/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -3332,83 +3332,16 @@ done:
 
 static SECStatus
 cancel_and_failure(nsNSSSocketInfo* infoObject)
 {
   infoObject->SetCanceled(PR_TRUE);
   return SECFailure;
 }
 
-class nsIsStsHostRunnable : public nsIRunnable
-{
- public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  nsIsStsHostRunnable(const nsCOMPtr<nsIStrictTransportSecurityService> &stss)
-    : stss(stss), stsEnabled(PR_FALSE), nsrv(NS_ERROR_UNEXPECTED)
-  {}
-
-  nsXPIDLCString hostName;
-
-  nsresult GetResult(PRBool &b) const { b = stsEnabled; return nsrv; }
-
- private:
-  nsCOMPtr<nsIStrictTransportSecurityService> stss;
-  PRBool stsEnabled;
-  nsresult nsrv;
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsIsStsHostRunnable,
-                              nsIRunnable)
-
-NS_IMETHODIMP nsIsStsHostRunnable::Run()
-{
-  nsrv = stss->IsStsHost(hostName, &stsEnabled);
-  return NS_OK;
-}
-
-class nsNotifyCertProblemRunnable : public nsIRunnable
-{
- public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  nsNotifyCertProblemRunnable(nsIInterfaceRequestor *cb,
-                              nsIInterfaceRequestor *csi,
-                              nsSSLStatus* status,
-                              const nsCString &hostWithPortString)
-  : cb(cb),
-    csi(csi),
-    status(status),
-    hostWithPortString(hostWithPortString),
-    suppressMessage(PR_FALSE)
-  {}
-
-  PRBool GetSuppressMessage() { return suppressMessage; }
-
- private:
-  nsIInterfaceRequestor* cb;
-  nsIInterfaceRequestor* csi;
-  nsSSLStatus* status;
-  const nsCString& hostWithPortString;
-  PRBool suppressMessage;
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsNotifyCertProblemRunnable,
-                              nsIRunnable)
-
-NS_IMETHODIMP nsNotifyCertProblemRunnable::Run()
-{
-  nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
-  if (bcl)
-    bcl->NotifyCertProblem(csi, status, hostWithPortString, &suppressMessage);
-  return NS_OK;
-}
-
 static SECStatus
 nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
 {
   // cert was revoked, don't do anything else
   // Calling cancel_and_failure is not necessary, and would be wrong,
   // [for errors other than the ones explicitly handled below,] 
   // because it suppresses error reporting.
   if (PR_GetError() == SEC_ERROR_REVOKED_CERTIFICATE)
@@ -3582,35 +3515,31 @@ nsNSSBadCertHandler(void *arg, PRFileDes
   remaining_display_errors = collected_errors;
 
   // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
   // connections must be dropped when there are any certificate errors
   // (STS Spec section 7.3).
 
   nsCOMPtr<nsIStrictTransportSecurityService> stss
     = do_GetService(NS_STSSERVICE_CONTRACTID);
-
-  nsCOMPtr<nsIsStsHostRunnable> runnable(new nsIsStsHostRunnable(stss));
-  if (!runnable)
-    return SECFailure;
+  nsCOMPtr<nsIStrictTransportSecurityService> proxied_stss;
+
+  nsrv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                              NS_GET_IID(nsIStrictTransportSecurityService),
+                              stss, NS_PROXY_SYNC,
+                              getter_AddRefs(proxied_stss));
+  NS_ENSURE_SUCCESS(nsrv, SECFailure);
 
   // now grab the host name to pass to the STS Service
-  nsrv = infoObject->GetHostName(getter_Copies(runnable->hostName));
-  NS_ENSURE_SUCCESS(nsrv, SECFailure);
-
-  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-  if (!mainThread)
-    return SECFailure;
-
-  // Dispatch SYNC since the result is used below
-  nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
+  nsXPIDLCString hostName;
+  nsrv = infoObject->GetHostName(getter_Copies(hostName));
   NS_ENSURE_SUCCESS(nsrv, SECFailure);
 
   PRBool strictTransportSecurityEnabled;
-  nsrv = runnable->GetResult(strictTransportSecurityEnabled);
+  nsrv = proxied_stss->IsStsHost(hostName, &strictTransportSecurityEnabled);
   NS_ENSURE_SUCCESS(nsrv, SECFailure);
 
   if (!strictTransportSecurityEnabled) {
     nsCOMPtr<nsICertOverrideService> overrideService =
       do_GetService(NS_CERTOVERRIDE_CONTRACTID);
     // it is fine to continue without the nsICertOverrideService
 
     PRUint32 overrideBits = 0;
@@ -3640,33 +3569,43 @@ nsNSSBadCertHandler(void *arg, PRFileDes
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Strict-Transport-Security is violated: untrusted transport layer\n"));
   }
 
   // Ok, this is a full stop.
   // First, deliver the technical details of the broken SSL status,
   // giving the caller a chance to suppress the error messages.
 
   PRBool suppressMessage = PR_FALSE;
+  nsresult rv;
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
   nsCOMPtr<nsIInterfaceRequestor> cb;
   infoObject->GetNotificationCallbacks(getter_AddRefs(cb));
   if (cb) {
-    nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
-
-    nsCOMPtr<nsNotifyCertProblemRunnable> runnable(
-        new nsNotifyCertProblemRunnable(cb, csi, status, hostWithPortString));
-    if (!runnable)
-      return SECFailure;
-
-    // Dispatch SYNC since the result is used below
-    nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
-    NS_ENSURE_SUCCESS(nsrv, SECFailure);
-
-    suppressMessage = runnable->GetSuppressMessage();
+    nsCOMPtr<nsIInterfaceRequestor> callbacks;
+    NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                         NS_GET_IID(nsIInterfaceRequestor),
+                         cb,
+                         NS_PROXY_SYNC,
+                         getter_AddRefs(callbacks));
+
+    nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
+    if (bcl) {
+      nsCOMPtr<nsIBadCertListener2> proxy_bcl;
+      NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                           NS_GET_IID(nsIBadCertListener2),
+                           bcl,
+                           NS_PROXY_SYNC,
+                           getter_AddRefs(proxy_bcl));
+      if (proxy_bcl) {
+        nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
+        rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString, 
+                                          &suppressMessage);
+      }
+    }
   }
 
   nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService = 
     do_GetService(NS_RECENTBADCERTS_CONTRACTID);
 
   if (recentBadCertsService) {
     recentBadCertsService->AddBadCert(hostWithPortStringUTF16, status);
   }
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3443,22 +3443,16 @@ 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;
@@ -3479,19 +3473,17 @@ 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)
@@ -3520,20 +3512,18 @@ 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,24 +72,16 @@ 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;
--- a/xpcom/proxy/src/nsProxyObjectManager.cpp
+++ b/xpcom/proxy/src/nsProxyObjectManager.cpp
@@ -47,17 +47,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsProxyEventPrivate.h"
 
 #include "nsIComponentManager.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
 #include "nsIThread.h"
-#include "nsIXPConnect.h"
 
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "xptiprivate.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
@@ -204,24 +203,16 @@ nsProxyObjectManager::GetProxyForObject(
     nsCOMPtr<nsIThread> thread;
     if (aTarget == NS_PROXY_TO_CURRENT_THREAD) {
       aTarget = NS_GetCurrentThread();
     } else if (aTarget == NS_PROXY_TO_MAIN_THREAD) {
       thread = do_GetMainThread();
       aTarget = thread.get();
     }
 
-    if (nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aObj)) {
-      // Only proxy wrapped JS from the main thread to the main thread
-      if (!NS_IsMainThread() || aTarget != NS_GetCurrentThread()) {
-        NS_ABORT_IF_FALSE(false, "GetProxyForObject on wrapped JS not allowed");
-        return NS_ERROR_FAILURE;
-      }
-    }
-
     // check to see if the target is on our thread.  If so, just return the
     // real object.
     
     if (!(proxyType & NS_PROXY_ASYNC) && !(proxyType & NS_PROXY_ALWAYS))
     {
         PRBool result;
         aTarget->IsOnCurrentThread(&result);