Bug 675078 - rm JSThreadData and JSThread (JSRuntime is now officially single-threaded) (r=igor,rs=mccr8)
authorLuke Wagner <luke@mozilla.com>
Mon, 18 Jul 2011 14:54:48 -0700
changeset 85297 e517d4c43143
parent 85296 bbb9dbfe659b
child 85298 3c47bcef2ce4
push id5271
push userlwagner@mozilla.com
push date2012-01-25 03:16 +0000
Treeherderresults
reviewersigor, mccr8
bugs675078
milestone12.0a1
Bug 675078 - rm JSThreadData and JSThread (JSRuntime is now officially single-threaded) (r=igor,rs=mccr8)
js/public/MemoryMetrics.h
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinterp.cpp
js/src/jslock.cpp
js/src/jslock.h
js/src/jslocko.asm
js/src/jsnum.cpp
js/src/jsobj.h
js/src/jsopcode.cpp
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jsscript.cpp
js/src/lock_sparcv8plus.il
js/src/lock_sparcv9.il
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.h
js/src/vm/Stack.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCThreadContext.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/tests/chrome/Makefile.in
js/xpconnect/tests/chrome/test_ccbeginfail.xul
js/xpconnect/tests/chrome/test_ccdump.xul
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -117,20 +117,20 @@ struct CompartmentStats
 
 struct IterateData
 {
     IterateData(JSMallocSizeOfFun mallocSizeOf, GetNameCallback getNameCb,
                 DestroyNameCallback destroyNameCb)
       : runtimeObject(0)
       , runtimeAtomsTable(0)
       , runtimeContexts(0)
-      , runtimeThreadsNormal(0)
-      , runtimeThreadsTemporary(0)
-      , runtimeThreadsRegexpCode(0)
-      , runtimeThreadsStackCommitted(0)
+      , runtimeNormal(0)
+      , runtimeTemporary(0)
+      , runtimeRegexpCode(0)
+      , runtimeStackCommitted(0)
       , gcHeapChunkTotal(0)
       , gcHeapChunkCleanUnused(0)
       , gcHeapChunkDirtyUnused(0)
       , gcHeapChunkCleanDecommitted(0)
       , gcHeapChunkDirtyDecommitted(0)
       , gcHeapArenaUnused(0)
       , gcHeapChunkAdmin(0)
       , gcHeapUnusedPercentage(0)
@@ -148,20 +148,20 @@ struct IterateData
       , mallocSizeOf(mallocSizeOf)
       , getNameCb(getNameCb)
       , destroyNameCb(destroyNameCb)
     {}
 
     int64_t runtimeObject;
     int64_t runtimeAtomsTable;
     int64_t runtimeContexts;
-    int64_t runtimeThreadsNormal;
-    int64_t runtimeThreadsTemporary;
-    int64_t runtimeThreadsRegexpCode;
-    int64_t runtimeThreadsStackCommitted;
+    int64_t runtimeNormal;
+    int64_t runtimeTemporary;
+    int64_t runtimeRegexpCode;
+    int64_t runtimeStackCommitted;
     int64_t gcHeapChunkTotal;
     int64_t gcHeapChunkCleanUnused;
     int64_t gcHeapChunkDirtyUnused;
     int64_t gcHeapChunkCleanDecommitted;
     int64_t gcHeapChunkDirtyDecommitted;
     int64_t gcHeapArenaUnused;
     int64_t gcHeapChunkAdmin;
     int64_t gcHeapUnusedPercentage;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -125,17 +125,16 @@ CPPSRCS		= \
 		jsgcmark.cpp \
 		jsgcchunk.cpp \
 		jsgcstats.cpp \
 		jscrashreport.cpp \
 		jshash.cpp \
 		jsinfer.cpp \
 		jsinterp.cpp \
 		jsiter.cpp \
-		jslock.cpp \
 		jslog2.cpp \
 		jsmath.cpp \
 		jsnativestack.cpp \
 		jsnum.cpp \
 		jsobj.cpp \
 		json.cpp \
 		jsonparser.cpp \
 		jsopcode.cpp \
@@ -614,17 +613,17 @@ check-malloc-function-usage: $(filter-ou
 		"in Makefile.in" "cx->calloc_ or rt->calloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \
 		"in Makefile.in" "cx->realloc_ or rt->realloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \
 		"in Makefile.in" "cx->free_" $^
 
 	# We desire these numbers to go down, not up. See "User guide to memory
 	# management within SpiderMonkey" in jsutil.h.
-	$(srcdir)/config/check_source_count.py OffTheBooks:: 59 \
+	$(srcdir)/config/check_source_count.py OffTheBooks:: 58 \
 		"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
 	# This should go to zero, if possible.
 	$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
 		"in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
 
 ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too.
 #check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time.
 endif
@@ -695,20 +694,16 @@ ifdef JS_THREADSAFE
 DEFINES		+= -DJS_THREADSAFE
 endif
 
 ifdef JS_HAS_CTYPES
 DEFINES		+= -DJS_HAS_CTYPES
 DEFINES         += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\"
 endif
 
-ifdef JS_NO_THIN_LOCKS
-DEFINES		+= -DJS_USE_ONLY_NSPR_LOCKS
-endif
-
 ifdef JS_VERSION
 DEFINES		+= -DJS_VERSION=$(JS_VERSION)
 endif
 
 # We do not want to have obsolete NSPR functionality in threadsafe builds.
 DEFINES   += -DNO_NSPR_10_SUPPORT
 
 ifneq ($(findstring -L,$(NSPR_LIBS)),)
@@ -776,30 +771,16 @@ endif # GNU_CC
 endif
 ifeq ($(OS_RELEASE),4.1)
 EXTRA_LIBS	+= -ldl -lnsl
 else
 EXTRA_LIBS	+= -lposix4 -ldl -lnsl -lsocket
 endif
 endif
 
-ifdef SOLARIS_SUNPRO_CXX
-ifeq ($(TARGET_CPU),sparc)
-# Sun Studio SPARC doesn't work well with gcc inline asm, use lock_SunOS_sparc*.il
-jslock.o: jslock.cpp Makefile.in lock_sparcv8plus.il lock_sparcv9.il
-	$(REPORT_BUILD)
-	@$(MAKE_DEPS_AUTO_CXX)
-ifeq (sparcv9,$(findstring sparcv9,$(OS_TEST)))
-	$(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv9.il $<
-else
-	$(CXX) -o $@ -c $(COMPILE_CFLAGS) $(srcdir)/lock_sparcv8plus.il $<
-endif # sparcv9
-endif # sparc
-endif # SOLARIS_SUNPRO_CXX
-
 # An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result.
 # This suppresses optimization for this single compilation unit.
 ifeq ($(OS_ARCH),AIX)
 jsatom.o: jsatom.cpp Makefile.in
 	$(REPORT_BUILD)
 	@$(MAKE_DEPS_AUTO_CXX)
 	$(CXX) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $<
 jsdtoa.o: jsdtoa.cpp Makefile.in
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -214,47 +214,36 @@ CollectCompartmentStatsForRuntime(JSRunt
             gc::ChunkSize;
 
         IterateCompartmentsArenasCells(cx, data, CompartmentMemoryCallback,
                                        ArenaCallback, CellCallback);
         IterateChunks(cx, data, ChunkCallback);
 
         data->runtimeObject = data->mallocSizeOf(rt, sizeof(JSRuntime));
 
+        size_t normal, temporary, regexpCode, stackCommitted;
+        rt->sizeOfExcludingThis(data->mallocSizeOf,
+                                &normal,
+                                &temporary,
+                                &regexpCode,
+                                &stackCommitted);
+
+        data->runtimeNormal = normal;
+        data->runtimeTemporary = temporary;
+        data->runtimeRegexpCode = regexpCode;
+        data->runtimeStackCommitted = stackCommitted;
+
         // Nb: we use sizeOfExcludingThis() because atomState.atoms is within
         // JSRuntime, and so counted when JSRuntime is counted.
         data->runtimeAtomsTable =
             rt->atomState.atoms.sizeOfExcludingThis(data->mallocSizeOf);
 
-        {
-            // Need the GC lock to call JS_ContextIteratorUnlocked() and to
-            // access rt->threads.
-            AutoLockGC lock(rt);
-
-            JSContext *acx, *iter = NULL;
-            while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
-                data->runtimeContexts +=
-                    acx->sizeOfIncludingThis(data->mallocSizeOf);
-            }
-
-            for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
-                JSThread *thread = r.front().value;
-                size_t normal, temporary, regexpCode, stackCommitted;
-                thread->sizeOfIncludingThis(data->mallocSizeOf,
-                                            &normal,
-                                            &temporary,
-                                            &regexpCode,
-                                            &stackCommitted);
-
-                data->runtimeThreadsNormal         += normal;
-                data->runtimeThreadsTemporary      += temporary;
-                data->runtimeThreadsRegexpCode     += regexpCode;
-                data->runtimeThreadsStackCommitted += stackCommitted;
-            }
-        }
+        JSContext *acx, *iter = NULL;
+        while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL)
+            data->runtimeContexts += acx->sizeOfIncludingThis(data->mallocSizeOf);
     }
 
     JS_DestroyContextNoGC(cx);
 
     // This is initialized to all bytes stored in used chunks, and then we
     // subtract used space from it each time around the loop.
     data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
                                    data->gcHeapChunkCleanUnused -
@@ -341,36 +330,27 @@ GetExplicitNonHeapForRuntime(JSRuntime *
     {
         JSAutoRequest ar(cx);
 
         // explicit/<compartment>/mjit-code
         size_t n = 0;
         IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
         *amount += n;
 
-        {
-            // Need the GC lock to call JS_ContextIteratorUnlocked() and to
-            // access rt->threads.
-            AutoLockGC lock(rt);
+        // explicit/runtime/regexp-code
+        // explicit/runtime/stack-committed
+        size_t regexpCode, stackCommitted;
+        rt->sizeOfExcludingThis(mallocSizeOf,
+                                NULL,
+                                NULL,
+                                &regexpCode,
+                                &stackCommitted);
 
-            // explicit/runtime/threads/regexp-code
-            // explicit/runtime/threads/stack-committed
-            for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
-                JSThread *thread = r.front().value;
-                size_t regexpCode, stackCommitted;
-                thread->sizeOfIncludingThis(mallocSizeOf,
-                                            NULL,
-                                            NULL,
-                                            &regexpCode,
-                                            &stackCommitted);
-
-                *amount += regexpCode;
-                *amount += stackCommitted;
-            }
-        }
+        *amount += regexpCode;
+        *amount += stackCommitted;
     }
 
     JS_DestroyContextNoGC(cx);
 
     return true;
 }
 
 } // namespace JS
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -57,29 +57,16 @@
 #endif
 
 using namespace std;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
-** Helper classes
-*******************************************************************************/
-
-class ScopedContextThread
-{
-public:
-  ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); }
-  ~ScopedContextThread() { JS_ClearContextThread(mCx); }
-private:
-  JSContext* mCx;
-};
-
-/*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
 static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
 
 namespace CType {
   static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
   static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
@@ -2786,17 +2773,16 @@ CType::FinalizeProtoClass(JSContext* cx,
   // for use with FunctionType closures. And if we're here, in this finalizer,
   // we're guaranteed to not need it anymore. Note that this slot will only
   // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
   jsval slot;
   if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot))
     return;
 
   JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
-  JS_SetContextThread(closureCx);
   JS_DestroyContextNoGC(closureCx);
 }
 
 void
 CType::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
   jsval slot = obj->getSlot(SLOT_TYPECODE);
@@ -5378,24 +5364,17 @@ CClosure::Create(JSContext* cx,
       return NULL;
     }
 
     if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX,
            PRIVATE_TO_JSVAL(cinfo->cx))) {
       JS_DestroyContextNoGC(cinfo->cx);
       return NULL;
     }
-
-    JS_ClearContextThread(cinfo->cx);
-  }
-
-#ifdef DEBUG
-  // We want *this* context's thread here so use cx instead of cinfo->cx.
-  cinfo->cxThread = JS_GetContextThread(cx);
-#endif
+  }
 
   // Prepare the error sentinel value. It's important to do this now, because
   // we might be unable to convert the value to the proper type. If so, we want
   // the caller to know about it _now_, rather than some uncertain time in the
   // future when the error sentinel is actually needed.
   if (!JSVAL_IS_VOID(errVal)) {
 
     // Make sure the callback returns something.
@@ -5500,21 +5479,16 @@ CClosure::ClosureStub(ffi_cif* cif, void
 
   // Retrieve the essentials from our closure object.
   ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
   JSContext* cx = cinfo->cx;
   JSObject* typeObj = cinfo->typeObj;
   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;
 
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -326,19 +326,16 @@ struct ClosureInfo
   JSRuntime* rt;         // Used in the destructor, where cx might have already
                          // been GCed.
   JSObject* closureObj;  // CClosure object
   JSObject* typeObj;     // FunctionType describing the C function
   JSObject* thisObj;     // 'this' object to use for the JS function call
   JSObject* jsfnObj;     // JS function
   void* errResult;       // Result that will be returned if the closure throws
   ffi_closure* closure;  // The C closure itself
-#ifdef DEBUG
-  intptr_t cxThread;     // The thread on which the context may be used
-#endif
 
   // Anything conditionally freed in the destructor should be initialized to
   // NULL here.
   ClosureInfo(JSRuntime* runtime)
     : rt(runtime)
     , errResult(NULL)
     , closure(NULL)
   {}
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -55,24 +55,26 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsclone.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdate.h"
+#include "jsdtoa.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
+#include "jsnativestack.h"
 #include "jsnum.h"
 #include "json.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jsscript.h"
@@ -83,17 +85,18 @@
 #include "jstypedarray.h"
 
 #include "ds/LifoAlloc.h"
 #include "builtin/MapObject.h"
 #include "builtin/RegExp.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/BytecodeEmitter.h"
 #include "js/MemoryMetrics.h"
-#include "mozilla/Util.h" // DebugOnly
+#include "mozilla/Util.h"
+#include "yarr/BumpPointerAllocator.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/RegExpObject-inl.h"
@@ -687,21 +690,37 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
 /*
  * Has a new runtime ever been created?  This flag is used to detect unsafe
  * changes to js_CStringsAreUTF8 after a runtime has been created, and to
  * control things that should happen only once across all runtimes.
  */
 static JSBool js_NewRuntimeWasCalled = JS_FALSE;
 
 JSRuntime::JSRuntime()
-  : atomsCompartment(NULL),
+  : interrupt(0),
+    atomsCompartment(NULL),
+#ifdef JS_THREADSAFE
+    ownerThread_(NULL),
+#endif
+    tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    execAlloc_(NULL),
+    bumpAlloc_(NULL),
+    repCache_(NULL),
+    interpreterFrames(NULL),
     cxCallback(NULL),
     compartmentCallback(NULL),
     activityCallback(NULL),
     activityCallbackArg(NULL),
+#ifdef JS_THREADSAFE
+    suspendCount(0),
+    requestDepth(0),
+# ifdef DEBUG
+    checkRequestDepth(0),
+# endif
+#endif
     gcSystemAvailableChunkListHead(NULL),
     gcUserAvailableChunkListHead(NULL),
     gcKeepAtoms(0),
     gcBytes(0),
     gcTriggerBytes(0),
     gcLastBytes(0),
     gcMaxBytes(0),
     gcMaxMallocBytes(0),
@@ -741,60 +760,54 @@ JSRuntime::JSRuntime()
     positiveInfinityValue(UndefinedValue()),
     emptyString(NULL),
     debugMode(false),
     profilingScripts(false),
     hadOutOfMemory(false),
     data(NULL),
 #ifdef JS_THREADSAFE
     gcLock(NULL),
-    gcDone(NULL),
-    requestDone(NULL),
     requestCount(0),
-    gcThread(NULL),
     gcHelperThread(thisFromCtor()),
 #endif
     debuggerMutations(0),
     securityCallbacks(NULL),
     structuredCloneCallbacks(NULL),
     telemetryCallback(NULL),
     propertyRemovals(0),
-    scriptFilenameTable(NULL),
-#ifdef JS_THREADSAFE
-    scriptFilenameTableLock(NULL),
-#endif
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
     anynameObject(NULL),
     functionNamespaceObject(NULL),
-#ifdef JS_THREADSAFE
-    interruptCounter(0),
-#else
-    threadData(thisFromCtor()),
-#endif
+    waiveGCQuota(false),
+    dtoaState(NULL),
+    pendingProxyOperation(NULL),
     trustedPrincipals_(NULL),
-    wrapObjectCallback(NULL),
+    wrapObjectCallback(TransparentObjectWrapper),
     preWrapObjectCallback(NULL),
     preserveWrapperCallback(NULL),
+#ifdef DEBUG
+    noGCOrAllocationCheck(0),
+#endif
     inOOMReport(0)
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&debuggerList);
 
     PodZero(&globalDebugHooks);
     PodZero(&atomState);
 }
 
 bool
 JSRuntime::init(uint32_t maxbytes)
 {
 #ifdef JS_THREADSAFE
-    ownerThread_ = js_CurrentThreadId();
+    ownerThread_ = PR_GetCurrentThread();
 #endif
 
 #ifdef JS_METHODJIT_SPEW
     JMCheckLogging();
 #endif
 
     if (!js_InitGC(this, maxbytes))
         return false;
@@ -807,35 +820,38 @@ JSRuntime::init(uint32_t maxbytes)
     }
 
     atomsCompartment->isSystemCompartment = true;
     atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
 
     if (!js_InitAtomState(this))
         return false;
 
-    wrapObjectCallback = js::TransparentObjectWrapper;
-
-#ifdef JS_THREADSAFE
-    /* this is asymmetric with JS_ShutDown: */
-    if (!js_SetupLocks(8, 16))
-        return false;
-#endif
-
-    debugMode = false;
-    if (!js_InitThreads(this))
-        return false;
     if (!InitRuntimeNumberState(this))
         return false;
 
+    dtoaState = js_NewDtoaState();
+    if (!dtoaState)
+        return false;
+
+    if (!stackSpace.init())
+        return false;
+
+    conservativeGC.nativeStackBase = GetNativeStackBase();
     return true;
 }
 
 JSRuntime::~JSRuntime()
 {
+    JS_ASSERT(onOwnerThread());
+
+    delete_<JSC::ExecutableAllocator>(execAlloc_);
+    delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
+    JS_ASSERT(!repCache_);
+
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
     if (!JS_CLIST_IS_EMPTY(&contextList)) {
         JSContext *cx, *iter = NULL;
         uintN cxcount = 0;
         while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) {
             fprintf(stderr,
 "JS API usage error: found live context at %p\n",
@@ -844,51 +860,53 @@ JSRuntime::~JSRuntime()
         }
         fprintf(stderr,
 "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
                 cxcount, (cxcount == 1) ? "" : "s");
     }
 #endif
 
     FinishRuntimeNumberState(this);
-    js_FinishThreads(this);
     js_FinishAtomState(this);
 
+    if (dtoaState)
+        js_DestroyDtoaState(dtoaState);
+
     js_FinishGC(this);
 #ifdef JS_THREADSAFE
     if (gcLock)
-        JS_DESTROY_LOCK(gcLock);
-    if (gcDone)
-        JS_DESTROY_CONDVAR(gcDone);
-    if (requestDone)
-        JS_DESTROY_CONDVAR(requestDone);
+        PR_DestroyLock(gcLock);
 #endif
 }
 
 #ifdef JS_THREADSAFE
 void
 JSRuntime::setOwnerThread()
 {
-    JS_ASSERT(ownerThread_ == (void *)-1);
-    ownerThread_ = js_CurrentThreadId();
+    JS_ASSERT(ownerThread_ == (void *)0xc1ea12);  /* "clear" */
+    JS_ASSERT(requestDepth == 0);
+    ownerThread_ = PR_GetCurrentThread();
+    conservativeGC.nativeStackBase = GetNativeStackBase();
 }
 
 void
 JSRuntime::clearOwnerThread()
 {
     JS_ASSERT(onOwnerThread());
-    ownerThread_ = (void *)-1;
+    JS_ASSERT(requestDepth == 0);
+    ownerThread_ = (void *)0xc1ea12;  /* "clear" */
+    conservativeGC.nativeStackBase = 0;
 }
 
 JS_FRIEND_API(bool)
 JSRuntime::onOwnerThread() const
 {
-    return ownerThread_ == js_CurrentThreadId();
-}
-#endif
+    return ownerThread_ == PR_GetCurrentThread();
+}
+#endif  /* JS_THREADSAFE */
 
 JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32_t maxbytes)
 {
     if (!js_NewRuntimeWasCalled) {
 #ifdef DEBUG
         /*
          * This code asserts that the numbers associated with the error names
@@ -938,20 +956,16 @@ JS_DestroyRuntime(JSRuntime *rt)
     Probes::destroyRuntime(rt);
     Foreground::delete_(rt);
 }
 
 JS_PUBLIC_API(void)
 JS_ShutDown(void)
 {
     Probes::shutdown();
-
-#ifdef JS_THREADSAFE
-    js_CleanupLocks();
-#endif
     PRMJ_NowShutdown();
 }
 
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt)
 {
     return rt->data;
 }
@@ -985,76 +999,53 @@ JS::UserCompartmentCount(const JSRuntime
     }
     return n;
 }
 
 #ifdef JS_THREADSAFE
 static void
 StartRequest(JSContext *cx)
 {
-    JSThread *t = cx->thread();
-    JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
-    if (t->data.requestDepth) {
-        t->data.requestDepth++;
+    JSRuntime *rt = cx->runtime;
+    JS_ASSERT(rt->onOwnerThread());
+
+    if (rt->requestDepth) {
+        rt->requestDepth++;
     } else {
-        JSRuntime *rt = cx->runtime;
         AutoLockGC lock(rt);
 
-        /* Wait until the GC is finished. */
-        if (rt->gcThread != cx->thread()) {
-            while (rt->gcThread)
-                JS_AWAIT_GC_DONE(rt);
-        }
-
         /* Indicate that a request is running. */
         rt->requestCount++;
-        t->data.requestDepth = 1;
-
-        /*
-         * Adjust rt->interruptCounter to reflect any interrupts added while the
-         * thread was suspended.
-         */
-        if (t->data.interruptFlags)
-            JS_ATOMIC_INCREMENT(&rt->interruptCounter);
+        rt->requestDepth = 1;
 
         if (rt->requestCount == 1 && rt->activityCallback)
             rt->activityCallback(rt->activityCallbackArg, true);
     }
 }
 
 static void
 StopRequest(JSContext *cx)
 {
-    JSThread *t = cx->thread();
-    JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-    JS_ASSERT(t->data.requestDepth != 0);
-    if (t->data.requestDepth != 1) {
-        t->data.requestDepth--;
+    JSRuntime *rt = cx->runtime;
+    JS_ASSERT(rt->onOwnerThread());
+    JS_ASSERT(rt->requestDepth != 0);
+    if (rt->requestDepth != 1) {
+        rt->requestDepth--;
     } else {
-        t->data.conservativeGC.updateForRequestEnd(t->suspendCount);
+        rt->conservativeGC.updateForRequestEnd(rt->suspendCount);
 
         /* Lock before clearing to interlock with ClaimScope, in jslock.c. */
-        JSRuntime *rt = cx->runtime;
         AutoLockGC lock(rt);
 
-        t->data.requestDepth = 0;
-
-        /*
-         * Adjust rt->interruptCounter to reflect any interrupts added while the
-         * thread still had active requests.
-         */
-        if (t->data.interruptFlags)
-            JS_ATOMIC_DECREMENT(&rt->interruptCounter);
+        rt->requestDepth = 0;
 
         /* Give the GC a chance to run if this was the last request running. */
         JS_ASSERT(rt->requestCount > 0);
         rt->requestCount--;
         if (rt->requestCount == 0) {
-            JS_NOTIFY_REQUEST_DONE(rt);
             if (rt->activityCallback)
                 rt->activityCallback(rt->activityCallbackArg, false);
         }
     }
 }
 #endif /* JS_THREADSAFE */
 
 JS_PUBLIC_API(void)
@@ -1085,55 +1076,66 @@ JS_YieldRequest(JSContext *cx)
     JS_ResumeRequest(cx, JS_SuspendRequest(cx));
 #endif
 }
 
 JS_PUBLIC_API(jsrefcount)
 JS_SuspendRequest(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
-    JSThread *t = cx->thread();
-    JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
-    jsrefcount saveDepth = t->data.requestDepth;
+    JSRuntime *rt = cx->runtime;
+    JS_ASSERT(rt->onOwnerThread());
+
+    jsrefcount saveDepth = rt->requestDepth;
     if (!saveDepth)
         return 0;
 
-    t->suspendCount++;
-    t->data.requestDepth = 1;
+    rt->suspendCount++;
+    rt->requestDepth = 1;
     StopRequest(cx);
     return saveDepth;
 #else
     return 0;
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
 {
 #ifdef JS_THREADSAFE
-    JSThread *t = cx->thread();
-    JS_ASSERT(CURRENT_THREAD_IS_ME(t));
+    JSRuntime *rt = cx->runtime;
+    JS_ASSERT(rt->onOwnerThread());
     if (saveDepth == 0)
         return;
     JS_ASSERT(saveDepth >= 1);
-    JS_ASSERT(!t->data.requestDepth);
-    JS_ASSERT(t->suspendCount);
+    JS_ASSERT(!rt->requestDepth);
+    JS_ASSERT(rt->suspendCount);
     StartRequest(cx);
-    t->data.requestDepth = saveDepth;
-    t->suspendCount--;
+    rt->requestDepth = saveDepth;
+    rt->suspendCount--;
 #endif
 }
 
 JS_PUBLIC_API(JSBool)
-JS_IsInRequest(JSContext *cx)
+JS_IsInRequest(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
-    JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
-    return JS_THREAD_DATA(cx)->requestDepth != 0;
+    JS_ASSERT(rt->onOwnerThread());
+    return rt->requestDepth != 0;
+#else
+    return false;
+#endif
+}
+
+JS_PUBLIC_API(JSBool)
+JS_IsInSuspendedRequest(JSRuntime *rt)
+{
+#ifdef JS_THREADSAFE
+    JS_ASSERT(rt->onOwnerThread());
+    return rt->suspendCount != 0;
 #else
     return false;
 #endif
 }
 
 JS_PUBLIC_API(JSContextCallback)
 JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback)
 {
@@ -3014,33 +3016,29 @@ JS_SetThreadStackLimit(JSContext *cx, ui
         limitAddr = UINTPTR_MAX;
 #endif
     cx->stackLimit = limitAddr;
 }
 
 JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSContext *cx, size_t stackSize)
 {
-#ifdef JS_THREADSAFE
-    JS_ASSERT(cx->thread());
-#endif
-
 #if JS_STACK_GROWTH_DIRECTION > 0
     if (stackSize == 0) {
         cx->stackLimit = UINTPTR_MAX;
     } else {
-        uintptr_t stackBase = reinterpret_cast<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase);
+        uintptr_t stackBase = cx->runtime->nativeStackBase;
         JS_ASSERT(stackBase <= size_t(-1) - stackSize);
         cx->stackLimit = stackBase + stackSize - 1;
     }
 #else
     if (stackSize == 0) {
         cx->stackLimit = 0;
     } else {
-        uintptr_t stackBase = reinterpret_cast<uintptr_t>(JS_THREAD_DATA(cx)->nativeStackBase);
+        uintptr_t stackBase = uintptr_t(cx->runtime->conservativeGC.nativeStackBase);
         JS_ASSERT(stackBase >= stackSize);
         cx->stackLimit = stackBase - (stackSize - 1);
     }
 #endif
 }
 
 /************************************************************************/
 
@@ -5513,19 +5511,16 @@ JS_New(JSContext *cx, JSObject *ctor, ui
     }
 
     return &args.rval().toObject();
 }
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback)
 {
-#ifdef JS_THREADSAFE
-    JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
-#endif
     JSOperationCallback old = cx->operationCallback;
     cx->operationCallback = callback;
     return old;
 }
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx)
 {
@@ -5533,36 +5528,31 @@ JS_GetOperationCallback(JSContext *cx)
 }
 
 JS_PUBLIC_API(void)
 JS_TriggerOperationCallback(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     AutoLockGC lock(cx->runtime);
 #endif
-    TriggerOperationCallback(cx);
+    cx->runtime->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(void)
-JS_TriggerAllOperationCallbacks(JSRuntime *rt)
+JS_TriggerRuntimeOperationCallback(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
 #endif
-    TriggerAllOperationCallbacks(rt);
+    rt->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx)
 {
-#ifdef JS_THREADSAFE
-    if (!cx->thread())
-        return false;
-#endif
-
     StackFrame *fp = cx->maybefp();
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     return fp != NULL;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SaveFrameChain(JSContext *cx)
@@ -6585,60 +6575,23 @@ JS_ThrowStopIteration(JSContext *cx)
 {
     AssertNoGC(cx);
     return js_ThrowStopIteration(cx);
 }
 
 JS_PUBLIC_API(intptr_t)
 JS_GetCurrentThread()
 {
-    return reinterpret_cast<intptr_t>(js_CurrentThreadId());
-}
-
-/*
- * Get the owning thread id of a context. Returns 0 if the context is not
- * owned by any thread.
- */
-JS_PUBLIC_API(intptr_t)
-JS_GetContextThread(JSContext *cx)
-{
 #ifdef JS_THREADSAFE
-    return cx->thread() ? reinterpret_cast<intptr_t>(cx->thread()->id) : 0;
+    return reinterpret_cast<intptr_t>(PR_GetCurrentThread());
 #else
     return 0;
 #endif
 }
 
-/*
- * 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(intptr_t)
-JS_SetContextThread(JSContext *cx)
-{
-    /* This function can be called by a finalizer. */
-    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<intptr_t>(cx->thread()->id);
-    }
-
-    if (!js_InitContextThreadAndLockGC(cx)) {
-        js_ReportOutOfMemory(cx);
-        return -1;
-    }
-
-    JS_UNLOCK_GC(cx->runtime);
-#endif
-    return 0;
-}
-
 extern JS_PUBLIC_API(void)
 JS_ClearRuntimeThread(JSRuntime *rt)
 {
     AssertNoGC(rt);
 #ifdef JS_THREADSAFE
     rt->clearOwnerThread();
 #endif
 }
@@ -6656,54 +6609,16 @@ extern JS_NEVER_INLINE JS_PUBLIC_API(voi
 JS_AbortIfWrongThread(JSRuntime *rt)
 {
 #ifdef JS_THREADSAFE
     if (!rt->onOwnerThread())
         JS_Assert("rt->onOwnerThread()", __FILE__, __LINE__);
 #endif
 }
 
-JS_PUBLIC_API(intptr_t)
-JS_ClearContextThread(JSContext *cx)
-{
-    JS_AbortIfWrongThread(cx->runtime);
-    AssertNoGC(cx);
-
-#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();
-    if (!t)
-        return 0;
-    JS_ASSERT(CURRENT_THREAD_IS_ME(t));
-
-    /*
-     * We must not race with a GC that accesses cx->thread for all threads,
-     * see bug 476934.
-     */
-    JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
-    js_WaitForGC(rt);
-    js_ClearContextThread(cx);
-    JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
-
-    /*
-     * We can access t->id as long as the GC lock is held and we cannot race
-     * with the GC that may delete t.
-     */
-    return reinterpret_cast<intptr_t>(t->id);
-#else
-    return 0;
-#endif
-}
-
 #ifdef JS_GC_ZEAL
 JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment)
 {
     bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold;
     cx->runtime->gcZeal_ = zeal;
     cx->runtime->gcZealFrequency = frequency;
     cx->runtime->gcNextScheduled = schedule ? frequency : 0;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2318,16 +2318,19 @@ extern JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt);
 
 extern JS_PUBLIC_API(void)
 JS_ShutDown(void);
 
 JS_PUBLIC_API(void *)
 JS_GetRuntimePrivate(JSRuntime *rt);
 
+extern JS_PUBLIC_API(JSRuntime *)
+JS_GetRuntime(JSContext *cx);
+
 JS_PUBLIC_API(void)
 JS_SetRuntimePrivate(JSRuntime *rt, void *data);
 
 extern JS_PUBLIC_API(void)
 JS_BeginRequest(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext *cx);
@@ -2338,17 +2341,20 @@ JS_YieldRequest(JSContext *cx);
 
 extern JS_PUBLIC_API(jsrefcount)
 JS_SuspendRequest(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);
 
 extern JS_PUBLIC_API(JSBool)
-JS_IsInRequest(JSContext *cx);
+JS_IsInRequest(JSRuntime *rt);
+
+extern JS_PUBLIC_API(JSBool)
+JS_IsInSuspendedRequest(JSRuntime *rt);
 
 #ifdef __cplusplus
 JS_END_EXTERN_C
 
 inline bool
 IsPoisonedId(jsid iden)
 {
     if (JSID_IS_STRING(iden))
@@ -2420,24 +2426,24 @@ class JSAutoSuspendRequest {
 #endif
 };
 
 class JSAutoCheckRequest {
   public:
     JSAutoCheckRequest(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM) {
 #if defined JS_THREADSAFE && defined DEBUG
         mContext = cx;
-        JS_ASSERT(JS_IsInRequest(cx));
+        JS_ASSERT(JS_IsInRequest(JS_GetRuntime(cx)));
 #endif
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~JSAutoCheckRequest() {
 #if defined JS_THREADSAFE && defined DEBUG
-        JS_ASSERT(JS_IsInRequest(mContext));
+        JS_ASSERT(JS_IsInRequest(JS_GetRuntime(mContext)));
 #endif
     }
 
 
   private:
 #if defined JS_THREADSAFE && defined DEBUG
     JSContext *mContext;
 #endif
@@ -4484,17 +4490,17 @@ JS_SetOperationCallback(JSContext *cx, J
 
 extern JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_TriggerOperationCallback(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
-JS_TriggerAllOperationCallbacks(JSRuntime *rt);
+JS_TriggerRuntimeOperationCallback(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx);
 
 /*
  * Saving and restoring frame chains.
  *
  * These two functions are used to set aside cx's call stack while that stack
@@ -5257,34 +5263,16 @@ JS_ThrowReportedError(JSContext *cx, con
  */
 extern JS_PUBLIC_API(JSBool)
 JS_ThrowStopIteration(JSContext *cx);
 
 extern JS_PUBLIC_API(intptr_t)
 JS_GetCurrentThread();
 
 /*
- * Associate the current thread with the given context.  This is done
- * implicitly by JS_NewContext.
- *
- * Returns the old thread id for this context, which should be treated as
- * an opaque value.  This value is provided for comparison to 0, which
- * indicates that ClearContextThread has been called on this context
- * since the last SetContextThread, or non-0, which indicates the opposite.
- */
-extern JS_PUBLIC_API(intptr_t)
-JS_GetContextThread(JSContext *cx);
-
-extern JS_PUBLIC_API(intptr_t)
-JS_SetContextThread(JSContext *cx);
-
-extern JS_PUBLIC_API(intptr_t)
-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).
  *
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -328,21 +328,18 @@ JSBool
 js_InitAtomState(JSRuntime *rt)
 {
     JSAtomState *state = &rt->atomState;
 
     JS_ASSERT(!state->atoms.initialized());
     if (!state->atoms.init(JS_STRING_HASH_COUNT))
         return false;
 
-#ifdef JS_THREADSAFE
-    js_InitLock(&state->lock);
-#endif
     JS_ASSERT(state->atoms.initialized());
-    return JS_TRUE;
+    return true;
 }
 
 void
 js_FinishAtomState(JSRuntime *rt)
 {
     JSAtomState *state = &rt->atomState;
 
     if (!state->atoms.initialized()) {
@@ -350,20 +347,16 @@ js_FinishAtomState(JSRuntime *rt)
          * We are called with uninitialized state when JS_NewRuntime fails and
          * calls JS_DestroyRuntime on a partially initialized runtime.
          */
         return;
     }
 
     for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
         r.front().asPtr()->finalize(rt);
-
-#ifdef JS_THREADSAFE
-    js_FinishLock(&state->lock);
-#endif
 }
 
 bool
 js_InitCommonAtoms(JSContext *cx)
 {
     JSAtomState *state = &cx->runtime->atomState;
     JSAtom **atoms = state->commonAtomsStart();
     for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) {
@@ -428,17 +421,16 @@ js_SweepAtomState(JSContext *cx)
 
 bool
 AtomIsInterned(JSContext *cx, JSAtom *atom)
 {
     /* We treat static strings as interned because they're never collected. */
     if (StaticStrings::isStatic(atom))
         return true;
 
-    AutoLockAtomsCompartment lock(cx);
     AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
     if (!p)
         return false;
 
     return p->isTagged();
 }
 
 enum OwnCharsBehavior
@@ -457,18 +449,16 @@ static JSAtom *
 AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
               InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
 {
     const jschar *chars = *pchars;
 
     if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
         return s;
 
-    AutoLockAtomsCompartment lock(cx);
-
     AtomSet &atoms = cx->runtime->atomState.atoms;
     AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
 
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         return atom;
     }
@@ -517,19 +507,16 @@ JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
         if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
             return &atom;
 
-        /* Here we have to check whether the atom is already interned. */
-        AutoLockAtomsCompartment lock(cx);
-
         AtomSet &atoms = cx->runtime->atomState.atoms;
         AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
         JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
         JS_ASSERT(p->asPtr() == &atom);
         JS_ASSERT(ib == InternAtom);
         p->setTagged(bool(ib));
         return &atom;
     }
@@ -599,19 +586,19 @@ js_AtomizeChars(JSContext *cx, const jsc
     return AtomizeInline(cx, &chars, length, ib);
 }
 
 JSAtom *
 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
 {
     if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
         return atom;
-    AutoLockAtomsCompartment lock(cx);
-    AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length));
-    return p ? p->asPtr() : NULL;
+    if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)))
+        return p->asPtr();
+    return NULL;
 }
 
 #ifdef DEBUG
 JS_FRIEND_API(void)
 js_DumpAtoms(JSContext *cx, FILE *fp)
 {
     JSAtomState *state = &cx->runtime->atomState;
 
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -263,20 +263,16 @@ enum FlationCoding
 };
 
 }  /* namespace js */
 
 struct JSAtomState
 {
     js::AtomSet         atoms;
 
-#ifdef JS_THREADSAFE
-    JSThinLock          lock;
-#endif
-
     /*
      * From this point until the end of struct definition the struct must
      * contain only js::PropertyName fields. We use this to access the storage
      * occupied by the common atoms in js_FinishCommonAtoms.
      *
      * js_common_atom_names defined in jsatom.cpp contains C strings for atoms
      * in the order of atom fields here. Therefore you must update that array
      * if you change member order here.
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -59,25 +59,23 @@
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
-#include "jsdtoa.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcmark.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
-#include "jsnativestack.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
@@ -91,343 +89,100 @@
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jscompartment.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
-namespace js {
-
-ThreadData::ThreadData(JSRuntime *rt)
-  : rt(rt),
-    interruptFlags(0),
-#ifdef JS_THREADSAFE
-    requestDepth(0),
-#endif
-    tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    execAlloc(NULL),
-    bumpAlloc(NULL),
-    repCache(NULL),
-    dtoaState(NULL),
-    nativeStackBase(GetNativeStackBase()),
-    pendingProxyOperation(NULL),
-    interpreterFrames(NULL)
+void
+JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                               size_t *regexpCode, size_t *stackCommitted)
 {
-#ifdef DEBUG
-    noGCOrAllocationCheck = 0;
-#endif
-}
-
-ThreadData::~ThreadData()
-{
-    JS_ASSERT(!repCache);
-
-    rt->delete_<JSC::ExecutableAllocator>(execAlloc);
-    rt->delete_<WTF::BumpPointerAllocator>(bumpAlloc);
-
-    if (dtoaState)
-        js_DestroyDtoaState(dtoaState);
-}
-
-bool
-ThreadData::init()
-{
-    JS_ASSERT(!repCache);
-    return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
-}
-
-#ifdef JS_THREADSAFE
-void
-ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
-                                size_t *regexpCode, size_t *stackCommitted)
-{
-    /*
-     * There are other ThreadData members that could be measured; the ones
-     * below have been seen by DMD to be worth measuring.  More stuff may be
-     * added later.
-     */
-
     /*
      * The computedSize is 0 because sizeof(DtoaState) isn't available here and
      * it's not worth making it available.
      */
     if (normal)
         *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0);
 
     if (temporary)
         *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
     if (regexpCode) {
         size_t method = 0, regexp = 0, unused = 0;
-        if (execAlloc)
-            execAlloc->sizeOfCode(&method, &regexp, &unused);
+        if (execAlloc_)
+            execAlloc_->sizeOfCode(&method, &regexp, &unused);
         JS_ASSERT(method == 0);     /* this execAlloc is only used for regexp code */
         *regexpCode = regexp + unused;
     }
 
     if (stackCommitted)
         *stackCommitted = stackSpace.sizeOfCommitted();
 }
-#endif
 
-void
-ThreadData::triggerOperationCallback(JSRuntime *rt)
+JS_FRIEND_API(void)
+JSRuntime::triggerOperationCallback()
 {
-    JS_ASSERT(rt == this->rt);
-
     /*
-     * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
-     * the write will become immediately visible to other processors polling
-     * the flag.  Note that we only care about visibility here, not read/write
-     * ordering: this field can only be written with the GC lock held.
+     * Use JS_ATOMIC_SET in the hope that it ensures the write will become
+     * immediately visible to other processors polling the flag.
      */
-    if (interruptFlags)
-        return;
-    JS_ATOMIC_SET(&interruptFlags, 1);
-
-#ifdef JS_THREADSAFE
-    /* rt->interruptCounter does not reflect suspended threads. */
-    if (requestDepth != 0)
-        JS_ATOMIC_INCREMENT(&rt->interruptCounter);
-#endif
+    JS_ATOMIC_SET(&interrupt, 1);
 }
 
 JSC::ExecutableAllocator *
-ThreadData::createExecutableAllocator(JSContext *cx)
+JSRuntime::createExecutableAllocator(JSContext *cx)
 {
-    JS_ASSERT(!execAlloc);
-    JS_ASSERT(cx->runtime == rt);
+    JS_ASSERT(!execAlloc_);
+    JS_ASSERT(cx->runtime == this);
 
-    execAlloc = rt->new_<JSC::ExecutableAllocator>();
-    if (!execAlloc)
+    execAlloc_ = new_<JSC::ExecutableAllocator>();
+    if (!execAlloc_)
         js_ReportOutOfMemory(cx);
-    return execAlloc;
+    return execAlloc_;
 }
 
 WTF::BumpPointerAllocator *
-ThreadData::createBumpPointerAllocator(JSContext *cx)
+JSRuntime::createBumpPointerAllocator(JSContext *cx)
 {
-    JS_ASSERT(!bumpAlloc);
-    JS_ASSERT(cx->runtime == rt);
+    JS_ASSERT(!bumpAlloc_);
+    JS_ASSERT(cx->runtime == this);
 
-    bumpAlloc = rt->new_<WTF::BumpPointerAllocator>();
-    if (!bumpAlloc)
+    bumpAlloc_ = new_<WTF::BumpPointerAllocator>();
+    if (!bumpAlloc_)
         js_ReportOutOfMemory(cx);
-    return bumpAlloc;
+    return bumpAlloc_;
 }
 
 RegExpPrivateCache *
-ThreadData::createRegExpPrivateCache(JSContext *cx)
+JSRuntime::createRegExpPrivateCache(JSContext *cx)
 {
-    JS_ASSERT(!repCache);
-    JS_ASSERT(cx->runtime == rt);
+    JS_ASSERT(!repCache_);
+    JS_ASSERT(cx->runtime == this);
 
-    RegExpPrivateCache *newCache = rt->new_<RegExpPrivateCache>(rt);
+    RegExpPrivateCache *newCache = new_<RegExpPrivateCache>(this);
 
     if (!newCache || !newCache->init()) {
         js_ReportOutOfMemory(cx);
-        rt->delete_<RegExpPrivateCache>(newCache);
+        delete_<RegExpPrivateCache>(newCache);
         return NULL;
     }
 
-    repCache = newCache;
-    return repCache;
+    repCache_ = newCache;
+    return repCache_;
 }
 
-void
-ThreadData::purgeRegExpPrivateCache()
-{
-    rt->delete_<RegExpPrivateCache>(repCache);
-    repCache = NULL;
-}
-
-} /* namespace js */
-
 JSScript *
 js_GetCurrentScript(JSContext *cx)
 {
     return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
 }
 
-
-#ifdef JS_THREADSAFE
-
-void
-JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
-                              size_t *regexpCode, size_t *stackCommitted)
-{
-    data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted);
-    if (normal)
-        *normal += mallocSizeOf(this, sizeof(JSThread));
-}
-
-JSThread *
-js_CurrentThreadAndLockGC(JSRuntime *rt)
-{
-    void *id = js_CurrentThreadId();
-    JS_LOCK_GC(rt);
-
-    /*
-     * We must not race with a GC that accesses cx->thread for JSContext
-     * instances on all threads, see bug 476934.
-     */
-    js_WaitForGC(rt);
-
-    JSThread *thread;
-    JSThread::Map::AddPtr p = rt->threads.lookupForAdd(id);
-    if (p) {
-        thread = p->value;
-
-        /*
-         * If thread has no contexts, it might be left over from a previous
-         * thread with the same id but a different stack address.
-         */
-        if (JS_CLIST_IS_EMPTY(&thread->contextList))
-            thread->data.nativeStackBase = GetNativeStackBase();
-    } else {
-        JS_UNLOCK_GC(rt);
-
-        thread = OffTheBooks::new_<JSThread>(rt, id);
-        if (!thread || !thread->init()) {
-            Foreground::delete_(thread);
-            return NULL;
-        }
-        JS_LOCK_GC(rt);
-        js_WaitForGC(rt);
-        if (!rt->threads.relookupOrAdd(p, id, thread)) {
-            JS_UNLOCK_GC(rt);
-            Foreground::delete_(thread);
-            return NULL;
-        }
-
-        /* Another thread cannot add an entry for the current thread id. */
-        JS_ASSERT(p->value == thread);
-    }
-    JS_ASSERT(thread->id == id);
-
-    /*
-     * We skip the assert under glibc due to an apparent bug there, see
-     * bug 608526.
-     */
-#ifndef __GLIBC__
-    JS_ASSERT(GetNativeStackBase() == thread->data.nativeStackBase);
-#endif
-
-    return thread;
-}
-
-JSBool
-js_InitContextThreadAndLockGC(JSContext *cx)
-{
-    JSThread *thread = js_CurrentThreadAndLockGC(cx->runtime);
-    if (!thread)
-        return false;
-
-    JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
-    cx->setThread(thread);
-    return true;
-}
-
-void
-JSContext::setThread(JSThread *thread)
-{
-    thread_ = thread;
-    stack.threadReset();
-}
-
-void
-js_ClearContextThread(JSContext *cx)
-{
-    JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread()));
-    JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
-    cx->setThread(NULL);
-}
-
-#endif /* JS_THREADSAFE */
-
-ThreadData *
-js_CurrentThreadData(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
-    JSThread *thread = js_CurrentThreadAndLockGC(rt);
-    if (!thread)
-        return NULL;
-
-    return &thread->data;
-#else
-    return &rt->threadData;
-#endif
-}
-
-JSBool
-js_InitThreads(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
-    return rt->threads.init(4);
-#else
-    return rt->threadData.init();
-#endif
-}
-
-void
-js_FinishThreads(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
-    if (!rt->threads.initialized())
-        return;
-    for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
-        JSThread *thread = r.front().value;
-        Foreground::delete_(thread);
-    }
-    rt->threads.clear();
-#endif
-}
-
-void
-js_PurgeThreads(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    for (JSThread::Map::Enum e(cx->runtime->threads);
-         !e.empty();
-         e.popFront()) {
-        JSThread *thread = e.front().value;
-
-        if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
-            JS_ASSERT(cx->thread() != thread);
-            Foreground::delete_(thread);
-            e.removeFront();
-        } else {
-            thread->data.purge(cx);
-        }
-    }
-#else
-    cx->runtime->threadData.purge(cx);
-#endif
-}
-
-void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    for (JSThread::Map::Enum e(cx->runtime->threads);
-         !e.empty();
-         e.popFront())
-    {
-        JSThread *thread = e.front().value;
-
-        JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList));
-        thread->data.purgeRegExpPrivateCache();
-    }
-#else
-    cx->runtime->threadData.purgeRegExpPrivateCache();
-#endif
-}
-
 JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JS_AbortIfWrongThread(rt);
 
     /*
      * 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
@@ -439,30 +194,22 @@ js_NewContext(JSRuntime *rt, size_t stac
 
     JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
 
     if (!cx->busyArrays.init()) {
         Foreground::delete_(cx);
         return NULL;
     }
 
-#ifdef JS_THREADSAFE
-    if (!js_InitContextThreadAndLockGC(cx)) {
-        Foreground::delete_(cx);
-        return NULL;
-    }
-#endif
-
     /*
      * Here the GC lock is still held after js_InitContextThreadAndLockGC took it and
      * the GC is not running on another thread.
      */
     bool first = JS_CLIST_IS_EMPTY(&rt->contextList);
     JS_APPEND_LINK(&cx->link, &rt->contextList);
-    JS_UNLOCK_GC(rt);
 
     js_InitRandom(cx);
 
     /*
      * If cx is the first context on this runtime, initialize well-known atoms,
      * keywords, numbers, and strings.  If one of these steps should fail, the
      * runtime will be left in a partially initialized state, with zeroes and
      * nulls stored in the default-initialized remainder of the struct.  We'll
@@ -496,60 +243,40 @@ js_NewContext(JSRuntime *rt, size_t stac
 }
 
 void
 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
-    JSContextCallback cxCallback;
-
     JS_ASSERT(!cx->enumerators);
 
 #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())
-        JS_SetContextThread(cx);
-
-    /*
      * For API compatibility we support destroying contexts with non-zero
      * cx->outstandingRequests but we assume that all JS_BeginRequest calls
      * on this cx contributes to cx->thread->data.requestDepth and there is no
      * JS_SuspendRequest calls that set aside the counter.
      */
-    JS_ASSERT(cx->outstandingRequests <= cx->thread()->data.requestDepth);
+    JS_ASSERT(cx->outstandingRequests <= cx->runtime->requestDepth);
 #endif
 
     if (mode != JSDCM_NEW_FAILED) {
-        cxCallback = rt->cxCallback;
-        if (cxCallback) {
+        if (JSContextCallback cxCallback = rt->cxCallback) {
             /*
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
             DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
             JS_ASSERT(callbackStatus);
         }
     }
 
     JS_LOCK_GC(rt);
-#ifdef JS_THREADSAFE
-    /*
-     * Typically we are called outside a request, so ensure that the GC is not
-     * running before removing the context from rt->contextList, see bug 477021.
-     */
-    if (cx->thread()->data.requestDepth == 0)
-        js_WaitForGC(rt);
-#endif
     JS_REMOVE_LINK(&cx->link);
     bool last = !rt->hasContexts();
     if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
 #ifdef JS_THREADSAFE
         || cx->outstandingRequests != 0
 #endif
         ) {
         JS_ASSERT(!rt->gcRunning);
@@ -561,17 +288,17 @@ js_DestroyContext(JSContext *cx, JSDestr
 
         if (last) {
 #ifdef JS_THREADSAFE
             /*
              * If this thread is not in a request already, begin one now so
              * that we wait for any racing GC started on a not-last context to
              * finish, before we plow ahead and unpin atoms.
              */
-            if (cx->thread()->data.requestDepth == 0)
+            if (cx->runtime->requestDepth == 0)
                 JS_BeginRequest(cx);
 #endif
 
             /*
              * Dump remaining type inference results first. This printing
              * depends on atoms still existing.
              */
             {
@@ -602,27 +329,19 @@ js_DestroyContext(JSContext *cx, JSDestr
             JS_LOCK_GC(rt);
         } else {
             if (mode == JSDCM_FORCE_GC)
                 js_GC(cx, NULL, GC_NORMAL, gcstats::DESTROYCONTEXT);
             else if (mode == JSDCM_MAYBE_GC)
                 JS_MaybeGC(cx);
 
             JS_LOCK_GC(rt);
-            js_WaitForGC(rt);
         }
     }
 #ifdef JS_THREADSAFE
-#ifdef DEBUG
-    JSThread *t = cx->thread();
-#endif
-    js_ClearContextThread(cx);
-    JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
-#endif
-#ifdef JS_THREADSAFE
     rt->gcHelperThread.waitBackgroundSweepEnd();
 #endif
     JS_UNLOCK_GC(rt);
     Foreground::delete_(cx);
 }
 
 JSContext *
 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
@@ -640,17 +359,17 @@ js_ContextIterator(JSRuntime *rt, JSBool
 }
 
 JS_FRIEND_API(JSContext *)
 js_NextActiveContext(JSRuntime *rt, JSContext *cx)
 {
     JSContext *iter = cx;
 #ifdef JS_THREADSAFE
     while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
-        if (cx->outstandingRequests && cx->thread()->data.requestDepth)
+        if (cx->outstandingRequests && cx->runtime->requestDepth)
             break;
     }
     return cx;
 #else
     return js_ContextIterator(rt, JS_FALSE, &iter);
 #endif
 }
 
@@ -733,19 +452,16 @@ PopulateReportBlame(JSContext *cx, JSErr
  * Instead we just invoke the errorReporter with an "Out Of Memory"
  * type message, and then hope the process ends swiftly.
  */
 void
 js_ReportOutOfMemory(JSContext *cx)
 {
     cx->runtime->hadOutOfMemory = true;
 
-    /* AtomizeInline can call this indirectly when it creates the string. */
-    AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx);
-
     JSErrorReport report;
     JSErrorReporter onError = cx->errorReporter;
 
     /* Get the message for this error, but we won't expand any arguments. */
     const JSErrorFormatString *efs =
         js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
     const char *msg = efs ? efs->format : "Out of memory";
 
@@ -1201,33 +917,27 @@ js_GetErrorMessage(void *userRef, const 
     if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
         return &js_ErrorFormatString[errorNumber];
     return NULL;
 }
 
 JSBool
 js_InvokeOperationCallback(JSContext *cx)
 {
+    JS_ASSERT_REQUEST_DEPTH(cx);
+
     JSRuntime *rt = cx->runtime;
-    ThreadData *td = JS_THREAD_DATA(cx);
-
-    JS_ASSERT_REQUEST_DEPTH(cx);
-    JS_ASSERT(td->interruptFlags != 0);
+    JS_ASSERT(rt->interrupt != 0);
 
     /*
      * Reset the callback counter first, then run GC and yield. If another
      * thread is racing us here we will accumulate another callback request
      * which will be serviced at the next opportunity.
      */
-    JS_LOCK_GC(rt);
-    td->interruptFlags = 0;
-#ifdef JS_THREADSAFE
-    JS_ATOMIC_DECREMENT(&rt->interruptCounter);
-#endif
-    JS_UNLOCK_GC(rt);
+    JS_ATOMIC_SET(&rt->interrupt, 0);
 
     if (rt->gcIsNeeded)
         js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason);
 
 #ifdef JS_THREADSAFE
     /*
      * We automatically yield the current context every time the operation
      * callback is hit since we might be called as a result of an impending
@@ -1251,53 +961,21 @@ js_InvokeOperationCallback(JSContext *cx
 
     return !cb || cb(cx);
 }
 
 JSBool
 js_HandleExecutionInterrupt(JSContext *cx)
 {
     JSBool result = JS_TRUE;
-    if (JS_THREAD_DATA(cx)->interruptFlags)
+    if (cx->runtime->interrupt)
         result = js_InvokeOperationCallback(cx) && result;
     return result;
 }
 
-namespace js {
-
-void
-TriggerOperationCallback(JSContext *cx)
-{
-    /*
-     * We allow for cx to come from another thread. Thus we must deal with
-     * possible JS_ClearContextThread calls when accessing cx->thread. But we
-     * assume that the calling thread is in a request so JSThread cannot be
-     * GC-ed.
-     */
-    ThreadData *td;
-#ifdef JS_THREADSAFE
-    JSThread *thread = cx->thread();
-    if (!thread)
-        return;
-    td = &thread->data;
-#else
-    td = JS_THREAD_DATA(cx);
-#endif
-    td->triggerOperationCallback(cx->runtime);
-}
-
-void
-TriggerAllOperationCallbacks(JSRuntime *rt)
-{
-    for (ThreadDataIter i(rt); !i.empty(); i.popFront())
-        i.threadData()->triggerOperationCallback(rt);
-}
-
-} /* namespace js */
-
 StackFrame *
 js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
 {
     if (!fp)
         fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     JS_ASSERT_IF(fp, fp->isScriptFrame());
@@ -1349,30 +1027,26 @@ JSContext::JSContext(JSRuntime *rt)
     generatingError(false),
 #if JS_STACK_GROWTH_DIRECTION > 0
     stackLimit(UINTPTR_MAX),
 #else
     stackLimit(0),
 #endif
     runtime(rt),
     compartment(NULL),
-#ifdef JS_THREADSAFE
-    thread_(NULL),
-#endif
     stack(thisDuringConstruction()),  /* depends on cx->thread_ */
     parseMapPool_(NULL),
     globalObject(NULL),
     argumentFormatMap(NULL),
     lastMessage(NULL),
     errorReporter(NULL),
     operationCallback(NULL),
     data(NULL),
     data2(NULL),
 #ifdef JS_THREADSAFE
-    atomsCompartmentIsLocked(false),
     outstandingRequests(0),
 #endif
     autoGCRooters(NULL),
     debugHooks(&rt->globalDebugHooks),
     securityCallbacks(NULL),
     resolveFlags(0),
     rngSeed(0),
     iterValue(MagicValue(JS_NO_ITER_VALUE)),
@@ -1402,20 +1076,16 @@ JSContext::JSContext(JSRuntime *rt)
 #ifdef DEBUG
     checkGCRooters = NULL;
 #endif
 #endif
 }
 
 JSContext::~JSContext()
 {
-#ifdef JS_THREADSAFE
-    JS_ASSERT(!thread_);
-#endif
-
     /* Free the stuff hanging off of cx. */
     if (parseMapPool_)
         Foreground::delete_<ParseMapPool>(parseMapPool_);
 
     if (lastMessage)
         Foreground::free_(lastMessage);
 
     /* Remove any argument formatters. */
@@ -1504,25 +1174,16 @@ bool
 JSContext::runningWithTrustedPrincipals() const
 {
     return !compartment || compartment->principals == runtime->trustedPrincipals();
 }
 
 JS_FRIEND_API(void)
 JSRuntime::onTooMuchMalloc()
 {
-#ifdef JS_THREADSAFE
-    AutoLockGC lock(this);
-
-    /*
-     * We can be called outside a request and can race against a GC that
-     * mutates the JSThread set during the sweeping phase.
-     */
-    js_WaitForGC(this);
-#endif
     TriggerGC(this, gcstats::TOOMUCHMALLOC);
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
     /*
      * Retry when we are done with the background sweeping and have stopped
@@ -1544,16 +1205,29 @@ JSRuntime::onOutOfMemory(void *p, size_t
     if (p)
         return p;
     if (cx)
         js_ReportOutOfMemory(cx);
     return NULL;
 }
 
 void
+JSRuntime::purge(JSContext *cx)
+{
+    tempLifoAlloc.freeUnused();
+    gsnCache.purge();
+
+    /* FIXME: bug 506341 */
+    propertyCache.purge(cx);
+
+    delete_<RegExpPrivateCache>(repCache_);
+    repCache_ = NULL;
+}
+
+void
 JSContext::purge()
 {
     if (!activeCompilations) {
         Foreground::delete_<ParseMapPool>(parseMapPool_);
         parseMapPool_ = NULL;
     }
 }
 
@@ -1652,23 +1326,22 @@ JSContext::sizeOfIncludingThis(JSMallocS
 
 namespace JS {
 
 #if defined JS_THREADSAFE && defined DEBUG
 
 AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
     : cx(cx)
 {
-    JS_ASSERT(cx->thread());
-    JS_ASSERT(cx->thread()->data.requestDepth || cx->thread() == cx->runtime->gcThread);
+    JS_ASSERT(cx->runtime->requestDepth || cx->runtime->gcRunning);
     JS_ASSERT(cx->runtime->onOwnerThread());
-    cx->thread()->checkRequestDepth++;
+    cx->runtime->checkRequestDepth++;
 }
 
 AutoCheckRequestDepth::~AutoCheckRequestDepth()
 {
-    JS_ASSERT(cx->thread()->checkRequestDepth != 0);
-    cx->thread()->checkRequestDepth--;
+    JS_ASSERT(cx->runtime->checkRequestDepth != 0);
+    cx->runtime->checkRequestDepth--;
 }
 
 #endif
 
 } // namespace JS
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -118,260 +118,163 @@ struct GSNCache {
 inline GSNCache *
 GetGSNCache(JSContext *cx);
 
 struct PendingProxyOperation {
     PendingProxyOperation   *next;
     JSObject                *object;
 };
 
-struct ThreadData {
-    JSRuntime           *rt;
+typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
 
-    /*
-     * If non-zero, we were been asked to call the operation callback as soon
-     * as possible.  If the thread has an active request, this contributes
-     * towards rt->interruptCounter.
-     */
-    volatile int32_t    interruptFlags;
-
-#ifdef JS_THREADSAFE
-    /* The request depth for this thread. */
-    unsigned            requestDepth;
-#endif
-
-    /* Keeper of the contiguous stack used by all contexts in this thread. */
-    StackSpace          stackSpace;
-
-    /* Temporary arena pool used while compiling and decompiling. */
-    static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
-    LifoAlloc           tempLifoAlloc;
-
-  private:
-    /*
-     * Both of these allocators are used for regular expression code which is shared at the
-     * thread-data level.
-     */
-    JSC::ExecutableAllocator    *execAlloc;
-    WTF::BumpPointerAllocator   *bumpAlloc;
-    js::RegExpPrivateCache      *repCache;
-
-    JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
-    WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
-    js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
-
-  public:
-    JSC::ExecutableAllocator *getOrCreateExecutableAllocator(JSContext *cx) {
-        if (execAlloc)
-            return execAlloc;
-
-        return createExecutableAllocator(cx);
-    }
-
-    WTF::BumpPointerAllocator *getOrCreateBumpPointerAllocator(JSContext *cx) {
-        if (bumpAlloc)
-            return bumpAlloc;
-
-        return createBumpPointerAllocator(cx);
-    }
-
-    js::RegExpPrivateCache *getRegExpPrivateCache() {
-        return repCache;
-    }
-    js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSContext *cx) {
-        if (repCache)
-            return repCache;
-
-        return createRegExpPrivateCache(cx);
-    }
-
-    /* Called at the end of the global GC sweep phase to deallocate repCache memory. */
-    void purgeRegExpPrivateCache();
-
-    /*
-     * The GSN cache is per thread since even multi-cx-per-thread embeddings
-     * do not interleave js_GetSrcNote calls.
-     */
-    GSNCache            gsnCache;
-
-    /* Property cache for faster call/get/set invocation. */
-    PropertyCache       propertyCache;
-
-    /* State used by jsdtoa.cpp. */
-    DtoaState           *dtoaState;
-
+struct ConservativeGCData
+{
     /* Base address of the native stack for the current thread. */
     uintptr_t           *nativeStackBase;
 
-    /* List of currently pending operations on proxies. */
-    PendingProxyOperation *pendingProxyOperation;
+    /*
+     * The GC scans conservatively between ThreadData::nativeStackBase and
+     * nativeStackTop unless the latter is NULL.
+     */
+    uintptr_t           *nativeStackTop;
 
-    ConservativeGCThreadData conservativeGC;
+    union {
+        jmp_buf         jmpbuf;
+        uintptr_t       words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
+    } registerSnapshot;
 
-#ifdef DEBUG
-    size_t              noGCOrAllocationCheck;
+    /*
+     * Cycle collector uses this to communicate that the native stack of the
+     * GC thread should be scanned only if the thread have more than the given
+     * threshold of requests.
+     */
+    unsigned requestThreshold;
+
+    ConservativeGCData()
+      : nativeStackBase(NULL), nativeStackTop(NULL), requestThreshold(0)
+    {}
+
+    ~ConservativeGCData() {
+#ifdef JS_THREADSAFE
+        /*
+         * The conservative GC scanner should be disabled when the thread leaves
+         * the last request.
+         */
+        JS_ASSERT(!hasStackToScan());
+#endif
+    }
+
+    JS_NEVER_INLINE void recordStackTop();
+
+#ifdef JS_THREADSAFE
+    void updateForRequestEnd(unsigned suspendCount) {
+        if (suspendCount)
+            recordStackTop();
+        else
+            nativeStackTop = NULL;
+    }
 #endif
 
-    ThreadData(JSRuntime *rt);
-    ~ThreadData();
-
-    bool init();
-
-    void mark(JSTracer *trc) {
-        stackSpace.mark(trc);
+    bool hasStackToScan() const {
+        return !!nativeStackTop;
     }
-
-    void purge(JSContext *cx) {
-        tempLifoAlloc.freeUnused();
-        gsnCache.purge();
-
-        /* FIXME: bug 506341. */
-        propertyCache.purge(cx);
-    }
-
-#ifdef JS_THREADSAFE
-    void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
-                             size_t *regexpCode, size_t *stackCommitted);
-#endif
-
-    /* This must be called with the GC lock held. */
-    void triggerOperationCallback(JSRuntime *rt);
-
-    /*
-     * Frames currently running in js::Interpret. See InterpreterFrames for
-     * details.
-     */
-    InterpreterFrames *interpreterFrames;
 };
 
 } /* namespace js */
 
-#ifdef JS_THREADSAFE
-
-/*
- * Structure uniquely representing a thread.  It holds thread-private data
- * that can be accessed without a global lock.
- */
-struct JSThread {
-    typedef js::HashMap<void *,
-                        JSThread *,
-                        js::DefaultHasher<void *>,
-                        js::SystemAllocPolicy> Map;
-
-    /* Linked list of all contexts in use on this thread. */
-    JSCList             contextList;
-
-    /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */
-    void                *id;
-
-    /* Number of JS_SuspendRequest calls without JS_ResumeRequest. */
-    unsigned            suspendCount;
-
-# ifdef DEBUG
-    unsigned            checkRequestDepth;
-# endif
-
-    /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
-    js::ThreadData      data;
-
-    JSThread(JSRuntime *rt, void *id)
-      : id(id),
-        suspendCount(0),
-# ifdef DEBUG
-        checkRequestDepth(0),
-# endif
-        data(rt)
-    {
-        JS_INIT_CLIST(&contextList);
-    }
-
-    ~JSThread() {
-        /* The thread must have zero contexts. */
-        JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList));
-    }
-
-    bool init() {
-        return data.init();
-    }
-
-    JS_FRIEND_API(void) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal,
-                                            size_t *temporary, size_t *regexpCode,
-                                            size_t *stackCommitted);
-};
-
-#define JS_THREAD_DATA(cx)      (&(cx)->thread()->data)
-
-extern JSThread *
-js_CurrentThreadAndLockGC(JSRuntime *rt);
-
-/*
- * The function takes the GC lock and does not release in successful return.
- * On error (out of memory) the function releases the lock but delegates
- * the error reporting to the caller.
- */
-extern JSBool
-js_InitContextThreadAndLockGC(JSContext *cx);
-
-/*
- * On entrance the GC lock must be held and it will be held on exit.
- */
-extern void
-js_ClearContextThread(JSContext *cx);
-
-#endif /* JS_THREADSAFE */
-
-typedef enum JSDestroyContextMode {
-    JSDCM_NO_GC,
-    JSDCM_MAYBE_GC,
-    JSDCM_FORCE_GC,
-    JSDCM_NEW_FAILED
-} JSDestroyContextMode;
-
-typedef struct JSPropertyTreeEntry {
-    JSDHashEntryHdr     hdr;
-    js::Shape           *child;
-} JSPropertyTreeEntry;
-
-namespace js {
-
-typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
-
-}
-
 struct JSRuntime
 {
+    /*
+     * If non-zero, we were been asked to call the operation callback as soon
+     * as possible.
+     */
+    volatile int32_t    interrupt;
+
     /* Default compartment. */
     JSCompartment       *atomsCompartment;
 
     /* List of compartments (protected by the GC lock). */
     js::CompartmentVector compartments;
 
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
 #ifdef JS_THREADSAFE
   public:
+    void *ownerThread() const { return ownerThread_; }
     void clearOwnerThread();
     void setOwnerThread();
     JS_FRIEND_API(bool) onOwnerThread() const;
   private:
     void                *ownerThread_;
   public:
 #else
   public:
     bool onOwnerThread() const { return true; }
 #endif
 
+    /* Keeper of the contiguous stack used by all contexts in this thread. */
+    js::StackSpace stackSpace;
+
+    /* Temporary arena pool used while compiling and decompiling. */
+    static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
+    js::LifoAlloc tempLifoAlloc;
+
+  private:
+    /*
+     * Both of these allocators are used for regular expression code which is shared at the
+     * thread-data level.
+     */
+    JSC::ExecutableAllocator *execAlloc_;
+    WTF::BumpPointerAllocator *bumpAlloc_;
+    js::RegExpPrivateCache *repCache_;
+
+    JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
+    WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
+    js::RegExpPrivateCache *createRegExpPrivateCache(JSContext *cx);
+
+  public:
+    JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
+        return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx);
+    }
+    WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
+        return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
+    }
+    js::RegExpPrivateCache *maybeRegExpPrivateCache() {
+        return repCache_;
+    }
+    js::RegExpPrivateCache *getRegExpPrivateCache(JSContext *cx) {
+        return repCache_ ? repCache_ : createRegExpPrivateCache(cx);
+    }
+
+    /*
+     * Frames currently running in js::Interpret. See InterpreterFrames for
+     * details.
+     */
+    js::InterpreterFrames *interpreterFrames;
+
     /* Context create/destroy callback. */
     JSContextCallback   cxCallback;
 
     /* Compartment create/destroy callback. */
     JSCompartmentCallback compartmentCallback;
 
     js::ActivityCallback  activityCallback;
     void                 *activityCallbackArg;
 
+#ifdef JS_THREADSAFE
+    /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */
+    unsigned            suspendCount;
+
+    /* The request depth for this thread. */
+    unsigned            requestDepth;
+
+# ifdef DEBUG
+    unsigned            checkRequestDepth;
+# endif
+#endif
+
     /* Garbage collector state, used by jsgc.c. */
 
     /*
      * Set of all GC chunks with at least one allocated thing. The
      * conservative GC uses it to quickly check if a possible GC thing points
      * into an allocated chunk.
      */
     js::GCChunkSet      gcChunkSet;
@@ -514,17 +417,17 @@ struct JSRuntime
 
     /* Well-known numbers held for use by this runtime's contexts. */
     js::Value           NaNValue;
     js::Value           negativeInfinityValue;
     js::Value           positiveInfinityValue;
 
     JSAtom              *emptyString;
 
-    /* List of active contexts sharing this runtime; protected by gcLock. */
+    /* List of active contexts sharing this runtime. */
     JSCList             contextList;
 
     bool hasContexts() const {
         return !JS_CLIST_IS_EMPTY(&contextList);
     }
     
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
@@ -545,30 +448,19 @@ struct JSRuntime
     JSCList             debuggerList;
 
     /* Client opaque pointers */
     void                *data;
 
 #ifdef JS_THREADSAFE
     /* These combine to interlock the GC and new requests. */
     PRLock              *gcLock;
-    PRCondVar           *gcDone;
-    PRCondVar           *requestDone;
     uint32_t            requestCount;
-    JSThread            *gcThread;
 
     js::GCHelperThread  gcHelperThread;
-
-    /*
-     * Mapping from NSPR thread identifiers to JSThreads.
-     *
-     * This map can be accessed by the GC thread; or by the thread that holds
-     * gcLock, if GC is not running.
-     */
-    JSThread::Map       threads;
 #endif /* JS_THREADSAFE */
 
     uint32_t            debuggerMutations;
 
     /*
      * Security callbacks set on the runtime are used by each context unless
      * an override is set on the context.
      */
@@ -582,45 +474,53 @@ struct JSRuntime
 
     /*
      * The propertyRemovals counter is incremented for every JSObject::clear,
      * and for each JSObject::remove method call that frees a slot in the given
      * object. See js_NativeGet and js_NativeSet in jsobj.cpp.
      */
     int32_t             propertyRemovals;
 
-    /* Script filename table. */
-    struct JSHashTable  *scriptFilenameTable;
-#ifdef JS_THREADSAFE
-    PRLock              *scriptFilenameTableLock;
-#endif
-
     /* Number localization, used by jsnum.c */
     const char          *thousandsSeparator;
     const char          *decimalSeparator;
     const char          *numGrouping;
 
     /*
      * Weak references to lazily-created, well-known XML singletons.
      *
      * NB: Singleton objects must be carefully disconnected from the rest of
      * the object graph usually associated with a JSContext's global object,
      * including the set of standard class objects.  See jsxml.c for details.
      */
     JSObject            *anynameObject;
     JSObject            *functionNamespaceObject;
 
-#ifdef JS_THREADSAFE
-    /* Number of threads with active requests and unhandled interrupts. */
-    volatile int32_t    interruptCounter;
-#else
-    js::ThreadData      threadData;
+    /*
+     * Flag indicating that we are waiving any soft limits on the GC heap
+     * because we want allocations to be infallible (except when we hit OOM).
+     */
+    bool                waiveGCQuota;
 
-#define JS_THREAD_DATA(cx)      (&(cx)->runtime->threadData)
-#endif
+    /*
+     * The GSN cache is per thread since even multi-cx-per-thread embeddings
+     * do not interleave js_GetSrcNote calls.
+     */
+    js::GSNCache        gsnCache;
+
+    /* Property cache for faster call/get/set invocation. */
+    js::PropertyCache   propertyCache;
+
+    /* State used by jsdtoa.cpp. */
+    DtoaState           *dtoaState;
+
+    /* List of currently pending operations on proxies. */
+    js::PendingProxyOperation *pendingProxyOperation;
+
+    js::ConservativeGCData conservativeGC;
 
   private:
     JSPrincipals        *trustedPrincipals_;
   public:
     void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
     JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
 
     /* Literal table maintained by jsatom.c functions. */
@@ -628,16 +528,20 @@ struct JSRuntime
 
     /* Tables of strings that are pre-allocated in the atomsCompartment. */
     js::StaticStrings   staticStrings;
 
     JSWrapObjectCallback wrapObjectCallback;
     JSPreWrapCallback    preWrapObjectCallback;
     js::PreserveWrapperCallback preserveWrapperCallback;
 
+#ifdef DEBUG
+    size_t              noGCOrAllocationCheck;
+#endif
+
     /*
      * To ensure that cx->malloc does not cause a GC, we set this flag during
      * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
      * reporting the OOM, we ignore it.
      */
     int32_t             inOOMReport;
 
     JSRuntime();
@@ -734,23 +638,30 @@ struct JSRuntime
      * This should be called after system malloc/realloc returns NULL to try
      * to recove some memory or to report an error. Failures in malloc and
      * calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
+
+    JS_FRIEND_API(void) triggerOperationCallback();
+
+    void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                             size_t *regexpCode, size_t *stackCommitted);
+
+    void purge(JSContext *cx);
 };
 
-/* Common macros to access thread-local caches in JSThread or JSRuntime. */
-#define JS_PROPERTY_CACHE(cx)   (JS_THREAD_DATA(cx)->propertyCache)
+/* Common macros to access thread-local caches in JSRuntime. */
+#define JS_PROPERTY_CACHE(cx)   (cx->runtime->propertyCache)
 
-#define JS_KEEP_ATOMS(rt)   JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms);
-#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms);
+#define JS_KEEP_ATOMS(rt)   (rt)->gcKeepAtoms++;
+#define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--;
 
 #ifdef JS_ARGUMENT_FORMATTER_DEFINED
 /*
  * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to
  * formatter functions.  Elements are sorted in non-increasing format string
  * length order.
  */
 struct JSArgumentFormatMap {
@@ -894,34 +805,24 @@ struct JSContext
      * True if generating an error, to prevent runaway recursion.
      * NB: generatingError packs with throwing below.
      */
     JSPackedBool        generatingError;
 
     /* Limit pointer for checking native stack consumption during recursion. */
     uintptr_t           stackLimit;
 
-    /* Data shared by threads in an address space. */
+    /* Data shared by contexts and compartments in an address space. */
     JSRuntime *const    runtime;
 
     /* GC heap compartment. */
     JSCompartment       *compartment;
 
     inline void setCompartment(JSCompartment *compartment);
 
-#ifdef JS_THREADSAFE
-  private:
-    JSThread            *thread_;
-  public:
-    JSThread *thread() const { return thread_; }
-
-    void setThread(JSThread *thread);
-    static const size_t threadOffset() { return offsetof(JSContext, thread_); }
-#endif
-
     /* Current execution stack. */
     js::ContextStack    stack;
 
     /* ContextStack convenience functions */
     inline bool hasfp() const               { return stack.hasfp(); }
     inline js::StackFrame* fp() const       { return stack.fp(); }
     inline js::StackFrame* maybefp() const  { return stack.maybefp(); }
     inline js::FrameRegs& regs() const      { return stack.regs(); }
@@ -1039,27 +940,20 @@ struct JSContext
         return !!(runOptions & ropt);
     }
 
     bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
     bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
     bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
     bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
 
-    js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; }
+    js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; }
     inline js::LifoAlloc &typeLifoAlloc();
 
 #ifdef JS_THREADSAFE
-    /*
-     * AtomizeInline uses this flag to tell RunLastDitchGC and
-     * js_ReportOutOfMemory that they should temporarily unlock the atoms
-     * compartment.
-     */
-    bool                atomsCompartmentIsLocked;
-
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
     JSCList             threadLinks;        /* JSThread contextList linkage */
 #endif
 
     /* Stack of thread-stack-allocated GC roots. */
     js::AutoGCRooter   *autoGCRooters;
@@ -1160,18 +1054,16 @@ struct JSContext
 #ifdef JS_THREADSAFE
     /*
      * When non-null JSContext::free_ delegates the job to the background
      * thread.
      */
     js::GCHelperThread *gcBackgroundFree;
 #endif
 
-    js::ThreadData *threadData() { return JS_THREAD_DATA(this); }
-
     inline void* malloc_(size_t bytes) {
         return runtime->malloc_(bytes, this);
     }
 
     inline void* mallocNoReport(size_t bytes) {
         JS_ASSERT(bytes != 0);
         return runtime->malloc_(bytes, NULL);
     }
@@ -1246,23 +1138,16 @@ struct JSContext
 
     JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const;
 
     static inline JSContext *fromLinkField(JSCList *link) {
         JS_ASSERT(link);
         return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, link));
     }
 
-#ifdef JS_THREADSAFE
-    static inline JSContext *fromThreadLinks(JSCList *link) {
-        JS_ASSERT(link);
-        return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, threadLinks));
-    }
-#endif
-
   private:
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void *p);
@@ -1320,89 +1205,40 @@ class AutoXMLRooter : private AutoGCRoot
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JSXML * const xml;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 #endif /* JS_HAS_XML_SUPPORT */
 
+#ifdef JS_THREADSAFE
+# define JS_LOCK_GC(rt)    PR_Lock((rt)->gcLock)
+# define JS_UNLOCK_GC(rt)  PR_Unlock((rt)->gcLock)
+#else
+# define JS_LOCK_GC(rt)
+# define JS_UNLOCK_GC(rt)
+#endif
+
 class AutoUnlockGC {
   private:
     JSRuntime *rt;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoUnlockGC(JSRuntime *rt
                           JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : rt(rt)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_UNLOCK_GC(rt);
     }
     ~AutoUnlockGC() { JS_LOCK_GC(rt); }
 };
 
-class AutoLockAtomsCompartment {
-  private:
-    JSContext *cx;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  public:
-    AutoLockAtomsCompartment(JSContext *cx
-                             JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : cx(cx)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-#ifdef JS_THREADSAFE
-        JS_ASSERT(!cx->atomsCompartmentIsLocked);
-        JS_LOCK(cx, &cx->runtime->atomState.lock);
-        cx->atomsCompartmentIsLocked = true;
-#endif
-    }
-
-    ~AutoLockAtomsCompartment() {
-#ifdef JS_THREADSAFE
-        JS_ASSERT(cx->atomsCompartmentIsLocked);
-        cx->atomsCompartmentIsLocked = false;
-        JS_UNLOCK(cx, &cx->runtime->atomState.lock);
-#endif
-    }
-};
-
-class AutoUnlockAtomsCompartmentWhenLocked {
-    JSContext *cx;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  public:
-    AutoUnlockAtomsCompartmentWhenLocked(JSContext *cx
-                                         JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : cx(NULL)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
- #ifdef JS_THREADSAFE
-        if (cx->atomsCompartmentIsLocked) {
-            this->cx = cx;
-            cx->atomsCompartmentIsLocked = false;
-            JS_UNLOCK(cx, &cx->runtime->atomState.lock);
-        }
-#endif
-    }
-
-    ~AutoUnlockAtomsCompartmentWhenLocked() {
-#ifdef JS_THREADSAFE
-        if (cx) {
-            JS_ASSERT(!cx->atomsCompartmentIsLocked);
-            JS_LOCK(cx, &cx->runtime->atomState.lock);
-            cx->atomsCompartmentIsLocked = true;
-        }
-#endif
-    }
-};
-
 class AutoKeepAtoms {
     JSRuntime *rt;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoKeepAtoms(JSRuntime *rt
                            JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : rt(rt)
@@ -1473,111 +1309,56 @@ class JSAutoResolveFlags
     ~JSAutoResolveFlags() { mContext->resolveFlags = mSaved; }
 
   private:
     JSContext *mContext;
     uintN mSaved;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-extern js::ThreadData *
-js_CurrentThreadData(JSRuntime *rt);
-
-extern JSBool
-js_InitThreads(JSRuntime *rt);
-
-extern void
-js_FinishThreads(JSRuntime *rt);
-
-extern void
-js_PurgeThreads(JSContext *cx);
-
-extern void
-js_PurgeThreads_PostGlobalSweep(JSContext *cx);
-
 namespace js {
 
-#ifdef JS_THREADSAFE
-
-/* Iterator over ThreadData from all JSThread instances. */
-class ThreadDataIter : public JSThread::Map::Range
-{
-  public:
-    ThreadDataIter(JSRuntime *rt) : JSThread::Map::Range(rt->threads.all()) {}
-
-    ThreadData *threadData() const {
-        return &front().value->data;
-    }
-};
-
-#else /* !JS_THREADSAFE */
-
-class ThreadDataIter
-{
-    JSRuntime *runtime;
-    bool done;
-  public:
-    ThreadDataIter(JSRuntime *rt) : runtime(rt), done(false) {}
-
-    bool empty() const {
-        return done;
-    }
-
-    void popFront() {
-        JS_ASSERT(!done);
-        done = true;
-    }
-
-    ThreadData *threadData() const {
-        JS_ASSERT(!done);
-        return &runtime->threadData;
-    }
-};
-
-#endif  /* !JS_THREADSAFE */
-
 /*
  * Enumerate all contexts in a runtime that are in the same thread as a given
  * context.
  */
 class ThreadContextRange {
     JSCList *begin;
     JSCList *end;
 
 public:
     explicit ThreadContextRange(JSContext *cx) {
-#ifdef JS_THREADSAFE
-        end = &cx->thread()->contextList;
-#else
         end = &cx->runtime->contextList;
-#endif
         begin = end->next;
     }
 
     bool empty() const { return begin == end; }
     void popFront() { JS_ASSERT(!empty()); begin = begin->next; }
 
     JSContext *front() const {
-#ifdef JS_THREADSAFE
-        return JSContext::fromThreadLinks(begin);
-#else
         return JSContext::fromLinkField(begin);
-#endif
     }
 };
 
 } /* namespace js */
 
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
 extern JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize);
 
+typedef enum JSDestroyContextMode {
+    JSDCM_NO_GC,
+    JSDCM_MAYBE_GC,
+    JSDCM_FORCE_GC,
+    JSDCM_NEW_FAILED
+} JSDestroyContextMode;
+
 extern void
 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode);
 
 /*
  * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise
  * the caller must be holding rt->gcLock.
  */
 extern JSContext *
@@ -1653,53 +1434,40 @@ js_ReportValueErrorFlags(JSContext *cx, 
 
 #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2)     \
     ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,          \
                                     spindex, v, fallback, arg1, arg2))
 
 extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
 
 #ifdef JS_THREADSAFE
-# define JS_ASSERT_REQUEST_DEPTH(cx)  (JS_ASSERT((cx)->thread()),             \
-                                       JS_ASSERT((cx)->thread()->data.requestDepth >= 1))
+# define JS_ASSERT_REQUEST_DEPTH(cx)  JS_ASSERT((cx)->runtime->requestDepth >= 1)
 #else
 # define JS_ASSERT_REQUEST_DEPTH(cx)  ((void) 0)
 #endif
 
 /*
  * If the operation callback flag was set, call the operation callback.
  * This macro can run the full GC. Return true if it is OK to continue and
  * false otherwise.
  */
 #define JS_CHECK_OPERATION_LIMIT(cx)                                          \
     (JS_ASSERT_REQUEST_DEPTH(cx),                                             \
-     (!JS_THREAD_DATA(cx)->interruptFlags || js_InvokeOperationCallback(cx)))
+     (!cx->runtime->interrupt || js_InvokeOperationCallback(cx)))
 
 /*
  * Invoke the operation callback and return false if the current execution
  * is to be terminated.
  */
 extern JSBool
 js_InvokeOperationCallback(JSContext *cx);
 
 extern JSBool
 js_HandleExecutionInterrupt(JSContext *cx);
 
-namespace js {
-
-/* These must be called with GC lock taken. */
-
-void
-TriggerOperationCallback(JSContext *cx);
-
-void
-TriggerAllOperationCallbacks(JSRuntime *rt);
-
-} /* namespace js */
-
 /*
  * Get the topmost scripted frame in a context. Note: if the topmost frame is
  * in the middle of an inline call, that call will be expanded. To avoid this,
  * use cx->stack.currentScript or cx->stack.currentScriptedScopeChain.
  */
 extern js::StackFrame *
 js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp);
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -82,17 +82,17 @@ GetGlobalForScopeChain(JSContext *cx)
     if (!scope)
         return NULL;
     return &scope->asGlobal();
 }
 
 inline GSNCache *
 GetGSNCache(JSContext *cx)
 {
-    return &JS_THREAD_DATA(cx)->gsnCache;
+    return &cx->runtime->gsnCache;
 }
 
 class AutoNamespaceArray : protected AutoGCRooter {
   public:
     AutoNamespaceArray(JSContext *cx) : AutoGCRooter(cx, NAMESPACES) {
         array.init();
     }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -556,20 +556,20 @@ CallContextDebugHandler(JSContext *cx, J
     if (!CanCallContextDebugHandler(cx))
         return JSTRAP_RETURN;
 
     return cx->debugHooks->debuggerHandler(cx, script, bc, rval,
                                            cx->debugHooks->debuggerHandlerData);
 }
 
 #ifdef JS_THREADSAFE
-JSThread *
-GetContextThread(const JSContext *cx)
+void *
+GetOwnerThread(const JSContext *cx)
 {
-    return cx->thread();
+    return cx->runtime->ownerThread();
 }
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx)
 {
     return cx->outstandingRequests;
 }
 
@@ -580,28 +580,28 @@ GetRuntimeGCLock(const JSRuntime *rt)
 }
 
 AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : context(cx)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-    ThreadData &threadData = context->thread()->data;
-    JS_ASSERT(threadData.requestDepth >= 1);
-    JS_ASSERT(!threadData.conservativeGC.requestThreshold);
-    if (threadData.requestDepth == 1)
-        threadData.conservativeGC.requestThreshold = 1;
+    JSRuntime *rt = context->runtime;
+    JS_ASSERT(rt->requestDepth >= 1);
+    JS_ASSERT(!rt->conservativeGC.requestThreshold);
+    if (rt->requestDepth == 1)
+        rt->conservativeGC.requestThreshold = 1;
 }
 
 AutoSkipConservativeScan::~AutoSkipConservativeScan()
 {
-    ThreadData &threadData = context->thread()->data;
-    if (threadData.requestDepth == 1)
-        threadData.conservativeGC.requestThreshold = 0;
+    JSRuntime *rt = context->runtime;
+    if (rt->requestDepth == 1)
+        rt->conservativeGC.requestThreshold = 0;
 }
 #endif
 
 JS_FRIEND_API(JSCompartment *)
 GetContextCompartment(const JSContext *cx)
 {
     return cx->compartment;
 }
@@ -621,22 +621,19 @@ SetActivityCallback(JSRuntime *rt, Activ
 
 JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx)
 {
     return !cx->stack.empty();
 }
 
 JS_FRIEND_API(void)
-TriggerOperationCallbacksForActiveContexts(JSRuntime *rt)
+TriggerOperationCallback(JSRuntime *rt)
 {
-    JSContext* cx = NULL;
-    while ((cx = js_NextActiveContext(rt, cx))) {
-        TriggerOperationCallback(cx);
-    }
+    rt->triggerOperationCallback();
 }
 
 JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt)
 {
     return rt->compartments;
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -470,18 +470,18 @@ GetPCCountScriptCount(JSContext *cx);
 
 JS_FRIEND_API(JSString *)
 GetPCCountScriptSummary(JSContext *cx, size_t script);
 
 JS_FRIEND_API(JSString *)
 GetPCCountScriptContents(JSContext *cx, size_t script);
 
 #ifdef JS_THREADSAFE
-JS_FRIEND_API(JSThread *)
-GetContextThread(const JSContext *cx);
+JS_FRIEND_API(void *)
+GetOwnerThread(const JSContext *cx);
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx);
 
 JS_FRIEND_API(PRLock *)
 GetRuntimeGCLock(const JSRuntime *rt);
 
 class JS_FRIEND_API(AutoSkipConservativeScan)
@@ -557,17 +557,17 @@ CanCallContextDebugHandler(JSContext *cx
 extern JS_FRIEND_API(JSTrapStatus)
 CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval);
 
 extern JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx);
 
 /* Must be called with GC lock taken. */
 extern JS_FRIEND_API(void)
-TriggerOperationCallbacksForActiveContexts(JSRuntime *rt);
+TriggerOperationCallback(JSRuntime *rt);
 
 class SystemAllocPolicy;
 typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
 extern JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt);
 
 extern JS_FRIEND_API(size_t)
 SizeOfJSContext();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -101,16 +101,22 @@
 
 #ifdef MOZ_VALGRIND
 # define JS_VALGRIND
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
+#ifdef XP_WIN
+# include "jswin.h"
+#else
+# include <unistd.h>
+#endif
+
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 
 namespace js {
 namespace gc {
 
 #ifdef JS_GC_ZEAL
@@ -870,25 +876,19 @@ js_InitGC(JSRuntime *rt, uint32_t maxbyt
 
     if (!rt->gcRootsHash.init(256))
         return false;
 
     if (!rt->gcLocksHash.init(256))
         return false;
 
 #ifdef JS_THREADSAFE
-    rt->gcLock = JS_NEW_LOCK();
+    rt->gcLock = PR_NewLock();
     if (!rt->gcLock)
         return false;
-    rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
-    if (!rt->gcDone)
-        return false;
-    rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
-    if (!rt->requestDone)
-        return false;
     if (!rt->gcHelperThread.init())
         return false;
 #endif
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
@@ -1074,33 +1074,41 @@ MarkWordConservatively(JSTracer *trc, ui
 static void
 MarkRangeConservatively(JSTracer *trc, const uintptr_t *begin, const uintptr_t *end)
 {
     JS_ASSERT(begin <= end);
     for (const uintptr_t *i = begin; i < end; ++i)
         MarkWordConservatively(trc, *i);
 }
 
-static void
-MarkThreadDataConservatively(JSTracer *trc, ThreadData *td)
+static JS_NEVER_INLINE void
+MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt)
 {
-    ConservativeGCThreadData *ctd = &td->conservativeGC;
-    JS_ASSERT(ctd->hasStackToScan());
+    ConservativeGCData *cgcd = &rt->conservativeGC;
+    if (!cgcd->hasStackToScan()) {
+#ifdef JS_THREADSAFE
+        JS_ASSERT(!rt->suspendCount);
+        JS_ASSERT(rt->requestDepth <= cgcd->requestThreshold);
+#endif
+        return;
+    }
+
     uintptr_t *stackMin, *stackEnd;
 #if JS_STACK_GROWTH_DIRECTION > 0
-    stackMin = td->nativeStackBase;
-    stackEnd = ctd->nativeStackTop;
+    stackMin = rt->conservativeGC.nativeStackBase;
+    stackEnd = cgcd->nativeStackTop;
 #else
-    stackMin = ctd->nativeStackTop + 1;
-    stackEnd = td->nativeStackBase;
+    stackMin = cgcd->nativeStackTop + 1;
+    stackEnd = rt->conservativeGC.nativeStackBase;
 #endif
+
     JS_ASSERT(stackMin <= stackEnd);
     MarkRangeConservatively(trc, stackMin, stackEnd);
-    MarkRangeConservatively(trc, ctd->registerSnapshot.words,
-                            ArrayEnd(ctd->registerSnapshot.words));
+    MarkRangeConservatively(trc, cgcd->registerSnapshot.words,
+                            ArrayEnd(cgcd->registerSnapshot.words));
 }
 
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *beginv, Value *endv)
 {
     /*
      * Normally, the drainMarkStack phase of marking will never trace outside
      * of the compartment currently being collected. However, conservative
@@ -1129,69 +1137,49 @@ MarkStackRangeConservatively(JSTracer *t
     JS_ASSERT(begin <= end);
     for (const uintptr_t *i = begin; i < end; i += sizeof(Value) / sizeof(uintptr_t))
         MarkWordConservatively(trc, *i);
 #else
     MarkRangeConservatively(trc, begin, end);
 #endif
 }
 
-void
-MarkConservativeStackRoots(JSTracer *trc)
-{
-#ifdef JS_THREADSAFE
-    for (JSThread::Map::Range r = trc->runtime->threads.all(); !r.empty(); r.popFront()) {
-        JSThread *thread = r.front().value;
-        ConservativeGCThreadData *ctd = &thread->data.conservativeGC;
-        if (ctd->hasStackToScan()) {
-            JS_ASSERT_IF(!thread->data.requestDepth, thread->suspendCount);
-            MarkThreadDataConservatively(trc, &thread->data);
-        } else {
-            JS_ASSERT(!thread->suspendCount);
-            JS_ASSERT(thread->data.requestDepth <= ctd->requestThreshold);
-        }
-    }
-#else
-    MarkThreadDataConservatively(trc, &trc->runtime->threadData);
-#endif
-}
-
 JS_NEVER_INLINE void
-ConservativeGCThreadData::recordStackTop()
+ConservativeGCData::recordStackTop()
 {
     /* Update the native stack pointer if it points to a bigger stack. */
     uintptr_t dummy;
     nativeStackTop = &dummy;
 
     /*
-     * To record and update the register snapshot for the conservative
-     * scanning with the latest values we use setjmp.
+     * To record and update the register snapshot for the conservative scanning
+     * with the latest values we use setjmp.
      */
 #if defined(_MSC_VER)
 # pragma warning(push)
 # pragma warning(disable: 4611)
 #endif
     (void) setjmp(registerSnapshot.jmpbuf);
 #if defined(_MSC_VER)
 # pragma warning(pop)
 #endif
 }
 
-static inline void
+void
 RecordNativeStackTopForGC(JSContext *cx)
 {
-    ConservativeGCThreadData *ctd = &JS_THREAD_DATA(cx)->conservativeGC;
+    ConservativeGCData *cgcd = &cx->runtime->conservativeGC;
 
 #ifdef JS_THREADSAFE
     /* Record the stack top here only if we are called from a request. */
-    JS_ASSERT(cx->thread()->data.requestDepth >= ctd->requestThreshold);
-    if (cx->thread()->data.requestDepth == ctd->requestThreshold)
+    JS_ASSERT(cx->runtime->requestDepth >= cgcd->requestThreshold);
+    if (cx->runtime->requestDepth == cgcd->requestThreshold)
         return;
 #endif
-    ctd->recordStackTop();
+    cgcd->recordStackTop();
 }
 
 } /* namespace js */
 
 bool
 js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, gc::AllocKind *thingKind, void **thing)
 {
     return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID;
@@ -1259,48 +1247,45 @@ js_AddRootRT(JSRuntime *rt, jsval *vp, c
     /*
      * Due to the long-standing, but now removed, use of rt->gcLock across the
      * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
      * properly with a racing GC, without calling JS_AddRoot from a request.
      * We have to preserve API compatibility here, now that we avoid holding
      * rt->gcLock across the mark phase (including the root hashtable mark).
      */
     AutoLockGC lock(rt);
-    js_WaitForGC(rt);
 
     return !!rt->gcRootsHash.put((void *)vp,
                                  RootInfo(name, JS_GC_ROOT_VALUE_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
 {
     /*
      * Due to the long-standing, but now removed, use of rt->gcLock across the
      * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking
      * properly with a racing GC, without calling JS_AddRoot from a request.
      * We have to preserve API compatibility here, now that we avoid holding
      * rt->gcLock across the mark phase (including the root hashtable mark).
      */
     AutoLockGC lock(rt);
-    js_WaitForGC(rt);
 
     return !!rt->gcRootsHash.put((void *)rp,
                                  RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_RemoveRoot(JSRuntime *rt, void *rp)
 {
     /*
      * Due to the JS_RemoveRootRT API, we may be called outside of a request.
      * Same synchronization drill as above in js_AddRoot.
      */
     AutoLockGC lock(rt);
-    js_WaitForGC(rt);
     rt->gcRootsHash.remove(rp);
     rt->gcPoke = JS_TRUE;
     return JS_TRUE;
 }
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
@@ -1655,19 +1640,16 @@ ArenaLists::finalizeScripts(JSContext *c
     finalizeNow(cx, FINALIZE_SCRIPT);
 }
 
 static void
 RunLastDitchGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
 
-    /* The atoms are locked when we create a string in AtomizeInline. */
-    AutoUnlockAtomsCompartmentWhenLocked unlockAtomsCompartment(cx);
-
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
     js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH);
 }
 
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
@@ -2095,17 +2077,17 @@ MarkWeakReferences(GCMarker *gcmarker)
 }
 
 static void
 MarkRuntime(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime;
 
     if (rt->hasContexts())
-        MarkConservativeStackRoots(trc);
+        MarkConservativeStackRoots(trc, rt);
 
     for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
         gc_root_traversal(trc, r.front());
 
     for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
         gc_lock_traversal(r.front(), trc);
 
     if (rt->scriptPCCounters) {
@@ -2136,44 +2118,42 @@ MarkRuntime(JSTracer *trc)
             for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->pcCounters)
                     MarkRoot(trc, script, "profilingScripts");
             }
         }
     }
 
-    for (ThreadDataIter i(rt); !i.empty(); i.popFront())
-        i.threadData()->mark(trc);
+    rt->stackSpace.mark(trc);
 
     /* The embedding can register additional roots here. */
     if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
         (*op)(trc, rt->gcBlackRootsData);
 
     if (!IS_GC_MARKING_TRACER(trc)) {
         /* We don't want to miss these when called from TraceRuntime. */
         if (JSTraceDataOp op = rt->gcGrayRootsTraceOp)
             (*op)(trc, rt->gcGrayRootsData);
     }
 }
 
 void
 TriggerGC(JSRuntime *rt, gcstats::Reason reason)
 {
+    JS_ASSERT(rt->onOwnerThread());
+
     if (rt->gcRunning || rt->gcIsNeeded)
         return;
 
-    /*
-     * Trigger the GC when it is safe to call an operation callback on any
-     * thread.
-     */
+    /* Trigger the GC when it is safe to call an operation callback. */
     rt->gcIsNeeded = true;
     rt->gcTriggerCompartment = NULL;
     rt->gcTriggerReason = reason;
-    TriggerAllOperationCallbacks(rt);
+    rt->triggerOperationCallback();
 }
 
 void
 TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason)
 {
     JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->gcRunning);
 
@@ -2203,17 +2183,17 @@ TriggerCompartmentGC(JSCompartment *comp
 
     /*
      * Trigger the GC when it is safe to call an operation callback on any
      * thread.
      */
     rt->gcIsNeeded = true;
     rt->gcTriggerCompartment = comp;
     rt->gcTriggerReason = reason;
-    TriggerAllOperationCallbacks(comp->rt);
+    comp->rt->triggerOperationCallback();
 }
 
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
@@ -2376,30 +2356,47 @@ ExpireChunksAndArenas(JSRuntime *rt, boo
     }
 
     if (shouldShrink)
         DecommitArenas(rt);
 }
 
 #ifdef JS_THREADSAFE
 
+static unsigned
+GetCPUCount()
+{
+    static unsigned ncpus = 0;
+    if (ncpus == 0) {
+# ifdef XP_WIN
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ncpus = unsigned(sysinfo.dwNumberOfProcessors);
+# else
+        long n = sysconf(_SC_NPROCESSORS_ONLN);
+        ncpus = (n > 0) ? unsigned(n) : 1;
+# endif
+    }
+    return ncpus;
+}
+
 bool
 GCHelperThread::init()
 {
     if (!(wakeup = PR_NewCondVar(rt->gcLock)))
         return false;
     if (!(done = PR_NewCondVar(rt->gcLock)))
         return false;
 
     thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL,
                              PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
     if (!thread)
         return false;
 
-    backgroundAllocation = (js_GetCPUCount() >= 2);
+    backgroundAllocation = (GetCPUCount() >= 2);
     return true;
 }
 
 void
 GCHelperThread::finish()
 {
     PRThread *join = NULL;
     {
@@ -2675,17 +2672,17 @@ SweepCompartments(JSContext *cx, JSGCInv
 static void
 BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next())
         c->purge(cx);
 
-    js_PurgeThreads(cx);
+    rt->purge(cx);
 
     {
         JSContext *iter = NULL;
         while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
             acx->purge();
     }
 
     /*
@@ -2839,23 +2836,17 @@ SweepPhase(JSContext *cx, GCMarker *gcma
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT);
         if (rt->gcCallback)
             (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
     }
 }
 
-/*
- * Perform mark-and-sweep GC.
- *
- * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
- * other thread must be either outside all requests or blocked waiting for GC
- * to finish. The caller must hold rt->gcLock.
- */
+/* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */
 static void
 MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
     rt->gcNumber++;
 
     /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
     rt->gcIsNeeded = false;
@@ -2875,210 +2866,65 @@ MarkAndSweep(JSContext *cx, JSGCInvocati
     rt->gcIncrementalTracer = &gcmarker;
 
     BeginMarkPhase(cx, &gcmarker, gckind);
     gcmarker.drainMarkStack();
     EndMarkPhase(cx, &gcmarker, gckind);
     SweepPhase(cx, &gcmarker, gckind);
 }
 
-#ifdef JS_THREADSAFE
-
-/*
- * If the GC is running and we're called on another thread, wait for this GC
- * activation to finish. We can safely wait here without fear of deadlock (in
- * the case where we are called within a request on another thread's context)
- * because the GC doesn't set rt->gcRunning until after it has waited for all
- * active requests to end.
- *
- * We call here js_CurrentThreadId() after checking for rt->gcState to avoid
- * an expensive call when the GC is not running.
- */
-void
-js_WaitForGC(JSRuntime *rt)
-{
-    if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
-        do {
-            JS_AWAIT_GC_DONE(rt);
-        } while (rt->gcRunning);
-    }
-}
-
-/*
- * GC is running on another thread. Temporarily suspend all requests running
- * on the current thread and wait until the GC is done.
- */
-static void
-LetOtherGCFinish(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime;
-    JS_ASSERT(rt->gcThread);
-    JS_ASSERT(cx->thread() != rt->gcThread);
-
-    size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0;
-    JS_ASSERT(requestDebit <= rt->requestCount);
-    if (requestDebit != 0) {
-        rt->requestCount -= requestDebit;
-        if (rt->requestCount == 0)
-            JS_NOTIFY_REQUEST_DONE(rt);
-
-        /*
-         * Update the native stack before we wait so the GC thread see the
-         * correct stack bounds.
-         */
-        RecordNativeStackTopForGC(cx);
-    }
-
-    /*
-     * Check that we did not release the GC lock above and let the GC to
-     * finish before we wait.
-     */
-    JS_ASSERT(rt->gcThread);
-
-    /*
-     * Wait for GC to finish on the other thread, even if requestDebit is 0
-     * and even if GC has not started yet because the gcThread is waiting in
-     * AutoGCSession. This ensures that js_GC never returns without a full GC
-     * cycle happening.
-     */
-    do {
-        JS_AWAIT_GC_DONE(rt);
-    } while (rt->gcThread);
-
-    rt->requestCount += requestDebit;
-}
-
-#endif
-
 class AutoGCSession {
   public:
     explicit AutoGCSession(JSContext *cx);
     ~AutoGCSession();
 
   private:
     JSContext   *context;
 
     AutoGCSession(const AutoGCSession&) MOZ_DELETE;
     void operator=(const AutoGCSession&) MOZ_DELETE;
 };
 
-/*
- * Start a new GC session. Together with LetOtherGCFinish this function
- * contains the rendezvous algorithm by which we stop the world for GC.
- *
- * This thread becomes the GC thread. Wait for all other threads to quiesce.
- * Then set rt->gcRunning and return.
- */
+/* Start a new GC session. */
 AutoGCSession::AutoGCSession(JSContext *cx)
   : context(cx)
 {
-    JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+    JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
     JSRuntime *rt = cx->runtime;
-
-#ifdef JS_THREADSAFE
-    if (rt->gcThread && rt->gcThread != cx->thread())
-        LetOtherGCFinish(cx);
-#endif
-
     JS_ASSERT(!rt->gcRunning);
-
-#ifdef JS_THREADSAFE
-    /* No other thread is in GC, so indicate that we're now in GC. */
-    JS_ASSERT(!rt->gcThread);
-    rt->gcThread = cx->thread();
-
-    /*
-     * Notify operation callbacks on other threads, which will give them a
-     * chance to yield their requests. Threads without requests perform their
-     * callback at some later point, which then will be unnecessary, but
-     * harmless.
-     */
-    for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
-        JSThread *thread = r.front().value;
-        if (thread != cx->thread())
-            thread->data.triggerOperationCallback(rt);
-    }
-
-    /*
-     * Discount the request on the current thread from contributing to
-     * rt->requestCount before we wait for all other requests to finish.
-     * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on
-     * rt->requestCount transitions to 0.
-     */
-    size_t requestDebit = cx->thread()->data.requestDepth ? 1 : 0;
-    JS_ASSERT(requestDebit <= rt->requestCount);
-    if (requestDebit != rt->requestCount) {
-        rt->requestCount -= requestDebit;
-
-        do {
-            JS_AWAIT_REQUEST_DONE(rt);
-        } while (rt->requestCount > 0);
-        rt->requestCount += requestDebit;
-    }
-
-#endif /* JS_THREADSAFE */
-
-    /*
-     * Set rt->gcRunning here within the GC lock, and after waiting for any
-     * active requests to end. This way js_WaitForGC called outside a request
-     * would not block on the GC that is waiting for other requests to finish
-     * with rt->gcThread set while JS_BeginRequest would do such wait.
-     */
     rt->gcRunning = true;
 }
 
-/* End the current GC session and allow other threads to proceed. */
 AutoGCSession::~AutoGCSession()
 {
     JSRuntime *rt = context->runtime;
     rt->gcRunning = false;
-#ifdef JS_THREADSAFE
-    JS_ASSERT(rt->gcThread == context->thread());
-    rt->gcThread = NULL;
-    JS_NOTIFY_GC_DONE(rt);
-#endif
 }
 
 /*
  * GC, repeatedly if necessary, until we think we have not created any new
- * garbage and no other threads are demanding more GC. We disable inlining
- * to ensure that the bottom of the stack with possible GC roots recorded in
- * js_GC excludes any pointers we use during the marking implementation.
+ * garbage. We disable inlining to ensure that the bottom of the stack with
+ * possible GC roots recorded in js_GC excludes any pointers we use during the
+ * marking implementation.
  */
 static JS_NEVER_INLINE void
 GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
 
     JS_ASSERT_IF(comp, comp != rt->atomsCompartment);
     JS_ASSERT_IF(comp, rt->gcMode == JSGC_MODE_COMPARTMENT);
 
-    /*
-     * Recursive GC is no-op and a call from another thread waits the started
-     * GC cycle to finish.
-     */
-    if (rt->gcMarkAndSweep) {
-#ifdef JS_THREADSAFE
-        JS_ASSERT(rt->gcThread);
-        if (rt->gcThread != cx->thread()) {
-            /* We do not return until another GC finishes. */
-            LetOtherGCFinish(cx);
-        }
-#endif
+    /* Recursive GC is no-op. */
+    if (rt->gcMarkAndSweep)
         return;
-    }
 
     AutoGCSession gcsession(cx);
 
-    /*
-     * Don't GC if any thread is reporting an OOM. We check the flag after we
-     * have set up the GC session and know that the thread that reported OOM
-     * is either the current thread or waits for the GC to complete on this
-     * thread.
-     */
+    /* Don't GC if we are reporting an OOM. */
     if (rt->inOOMReport)
         return;
 
     /*
      * We should not be depending on cx->compartment in the GC, so set it to
      * NULL to look for violations.
      */
     SwitchToCompartment sc(cx, (JSCompartment *)NULL);
@@ -3098,19 +2944,16 @@ GCCycle(JSContext *cx, JSCompartment *co
     JS_ASSERT(!cx->gcBackgroundFree);
     rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
     if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep())
         cx->gcBackgroundFree = &rt->gcHelperThread;
 #endif
 
     MarkAndSweep(cx, gckind);
 
-    if (!comp)
-        js_PurgeThreads_PostGlobalSweep(cx);
-
 #ifdef JS_THREADSAFE
     if (cx->gcBackgroundFree) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
         cx->gcBackgroundFree = NULL;
         rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
     }
 #endif
 
@@ -3211,17 +3054,17 @@ void
 TraceRuntime(JSTracer *trc)
 {
     JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
 
 #ifdef JS_THREADSAFE
     {
         JSContext *cx = trc->context;
         JSRuntime *rt = cx->runtime;
-        if (rt->gcThread != cx->thread()) {
+        if (!rt->gcRunning) {
             AutoLockGC lock(rt);
             AutoGCSession gcsession(cx);
 
             rt->gcHelperThread.waitBackgroundSweepEnd();
             AutoUnlockGC unlock(rt);
 
             AutoCopyFreeListToArenas copy(rt);
             RecordNativeStackTopForGC(trc->context);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1315,21 +1315,16 @@ struct WrapperHasher
 
     static bool match(const Value &l, const Value &k) { return l == k; }
 };
 
 typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap;
 
 } /* namespace js */
 
-#ifdef DEBUG
-extern bool
-CheckAllocation(JSContext *cx);
-#endif
-
 extern JS_FRIEND_API(JSGCTraceKind)
 js_GetGCThingTraceKind(void *thing);
 
 extern JSBool
 js_InitGC(JSRuntime *rt, uint32_t maxbytes);
 
 extern void
 js_FinishGC(JSRuntime *rt);
@@ -1411,32 +1406,16 @@ typedef enum JSGCInvocationKind {
     /* Minimize GC triggers and release empty GC chunks right away. */
     GC_SHRINK             = 1
 } JSGCInvocationKind;
 
 /* Pass NULL for |comp| to get a full GC. */
 extern void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r);
 
-#ifdef JS_THREADSAFE
-/*
- * This is a helper for code at can potentially run outside JS request to
- * ensure that the GC is not running when the function returns.
- *
- * This function must be called with the GC lock held.
- */
-extern void
-js_WaitForGC(JSRuntime *rt);
-
-#else /* !JS_THREADSAFE */
-
-# define js_WaitForGC(rt)    ((void) 0)
-
-#endif
-
 namespace js {
 
 #ifdef JS_THREADSAFE
 
 class GCHelperThread {
     enum State {
         IDLE,
         SWEEPING,
@@ -1580,67 +1559,16 @@ struct GCChunkHasher {
         JS_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
         JS_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
         return k == l;
     }
 };
 
 typedef HashSet<js::gc::Chunk *, GCChunkHasher, SystemAllocPolicy> GCChunkSet;
 
-struct ConservativeGCThreadData {
-
-    /*
-     * The GC scans conservatively between ThreadData::nativeStackBase and
-     * nativeStackTop unless the latter is NULL.
-     */
-    uintptr_t           *nativeStackTop;
-
-    union {
-        jmp_buf         jmpbuf;
-        uintptr_t       words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
-    } registerSnapshot;
-
-    /*
-     * Cycle collector uses this to communicate that the native stack of the
-     * GC thread should be scanned only if the thread have more than the given
-     * threshold of requests.
-     */
-    unsigned requestThreshold;
-
-    ConservativeGCThreadData()
-      : nativeStackTop(NULL), requestThreshold(0)
-    {
-    }
-
-    ~ConservativeGCThreadData() {
-#ifdef JS_THREADSAFE
-        /*
-         * The conservative GC scanner should be disabled when the thread leaves
-         * the last request.
-         */
-        JS_ASSERT(!hasStackToScan());
-#endif
-    }
-
-    JS_NEVER_INLINE void recordStackTop();
-
-#ifdef JS_THREADSAFE
-    void updateForRequestEnd(unsigned suspendCount) {
-        if (suspendCount)
-            recordStackTop();
-        else
-            nativeStackTop = NULL;
-    }
-#endif
-
-    bool hasStackToScan() const {
-        return !!nativeStackTop;
-    }
-};
-
 template<class T>
 struct MarkStack {
     T *stack;
     T *tos;
     T *limit;
 
     bool push(T item) {
         if (tos == limit)
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -341,17 +341,17 @@ class CellIter: public CellIterImpl
 #endif
         if (lists->isSynchronizedFreeList(kind)) {
             lists = NULL;
         } else {
             JS_ASSERT(!comp->rt->gcRunning);
             lists->copyFreeListToArena(kind);
         }
 #ifdef DEBUG
-        counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck;
+        counter = &cx->runtime->noGCOrAllocationCheck;
         ++*counter;
 #endif
         init(comp, kind);
     }
 
     ~CellIter() {
 #ifdef DEBUG
         JS_ASSERT(*counter > 0);
@@ -379,17 +379,17 @@ inline T *
 NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
 #ifdef JS_THREADSAFE
     JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
                  kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
 #endif
     JS_ASSERT(!cx->runtime->gcRunning);
-    JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+    JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
     js::gc::MaybeCheckStackRoots(cx);
 
@@ -406,17 +406,17 @@ inline T *
 TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
 #ifdef JS_THREADSAFE
     JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
                  kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING);
 #endif
     JS_ASSERT(!cx->runtime->gcRunning);
-    JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
+    JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         return NULL;
 #endif
 
     void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
     return static_cast<T *>(t);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1253,23 +1253,23 @@ class GenericInterruptEnabler : public I
     T *variable;
     T value;
 };
 
 inline InterpreterFrames::InterpreterFrames(JSContext *cx, FrameRegs *regs, 
                                             const InterruptEnablerBase &enabler)
   : context(cx), regs(regs), enabler(enabler)
 {
-    older = JS_THREAD_DATA(cx)->interpreterFrames;
-    JS_THREAD_DATA(cx)->interpreterFrames = this;
+    older = cx->runtime->interpreterFrames;
+    cx->runtime->interpreterFrames = this;
 }
  
 inline InterpreterFrames::~InterpreterFrames()
 {
-    JS_THREAD_DATA(context)->interpreterFrames = older;
+    context->runtime->interpreterFrames = older;
 }
 
 #if defined(DEBUG) && !defined(JS_THREADSAFE)
 void
 js::AssertValidPropertyCacheHit(JSContext *cx,
                                 JSObject *start, JSObject *found,
                                 PropertyCacheEntry *entry)
 {
@@ -1549,17 +1549,17 @@ js::Interpret(JSContext *cx, StackFrame 
     JS_END_MACRO
 
     /*
      * Prepare to call a user-supplied branch handler, and abort the script
      * if it returns false.
      */
 #define CHECK_BRANCH()                                                        \
     JS_BEGIN_MACRO                                                            \
-        if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
+        if (cx->runtime->interrupt && !js_HandleExecutionInterrupt(cx))       \
             goto error;                                                       \
     JS_END_MACRO
 
 #define BRANCH(n)                                                             \
     JS_BEGIN_MACRO                                                            \
         regs.pc += (n);                                                       \
         op = (JSOp) *regs.pc;                                                 \
         if ((n) <= 0)                                                         \
deleted file mode 100644
--- a/js/src/jslock.cpp
+++ /dev/null
@@ -1,743 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifdef JS_THREADSAFE
-
-/*
- * JS locking stubs.
- */
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef XP_WIN
-# include "jswin.h"
-#else
-# include <unistd.h>
-#endif
-
-#include "jspubtd.h"
-#include "jstypes.h"
-#include "jsutil.h"
-#include "jsstdint.h"
-#include "jscntxt.h"
-#include "jsgc.h"
-#include "jslock.h"
-#include "jsscope.h"
-#include "jsstr.h"
-
-#include "jsscopeinlines.h"
-
-using namespace js;
-
-#define ReadWord(W) (W)
-
-#if !defined(__GNUC__)
-# define __asm__ asm
-# define __volatile__ volatile
-#endif
-
-/* Implement NativeCompareAndSwap. */
-
-#if defined(_MSC_VER) && defined(_M_IX86)
-// TODO: Bug 716204 - undo this pragma.
-#pragma warning( disable : 4035 )
-JS_BEGIN_EXTERN_C
-extern long __cdecl
-_InterlockedCompareExchange(long *volatile dest, long exchange, long comp);
-JS_END_EXTERN_C
-#pragma intrinsic(_InterlockedCompareExchange)
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwapHelper(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    _InterlockedCompareExchange((long*) w, nv, ov);
-    __asm {
-        sete al
-    }
-}
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    return (NativeCompareAndSwapHelper(w, ov, nv) & 1);
-}
-
-#elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64))
-/*
- * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC
- * declares _InterlockedCompareExchange64 through <windows.h>.
- */
-#pragma intrinsic(_InterlockedCompareExchange64)
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long long));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    return _InterlockedCompareExchange64((long long *volatile)w, nv, ov) == ov;
-}
-
-#elif defined(XP_MACOSX) || defined(DARWIN)
-
-#include <libkern/OSAtomic.h>
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    /* Details on these functions available in the manpage for atomic */
-    return OSAtomicCompareAndSwapPtrBarrier(reinterpret_cast<void *>(ov),
-                                            reinterpret_cast<void *>(nv),
-                                            reinterpret_cast<void * volatile *>(w));
-}
-
-#elif defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))
-
-/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    unsigned int res;
-
-    __asm__ __volatile__ (
-                          "lock\n"
-                          "cmpxchgl %2, (%1)\n"
-                          "sete %%al\n"
-                          "andl $1, %%eax\n"
-                          : "=a" (res)
-#ifdef __SUNPRO_CC
-/* Different code for Sun Studio because of a bug of SS12U1 */
-                          : "c" (w), "d" (nv), "a" (ov)
-#else
-                          : "r" (w), "r" (nv), "a" (ov)
-#endif
-                          : "cc", "memory");
-    return (int)res;
-}
-#elif defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    unsigned int res;
-
-    __asm__ __volatile__ (
-                          "lock\n"
-                          "cmpxchgq %2, (%1)\n"
-                          "sete %%al\n"
-                          "movzbl %%al, %%eax\n"
-                          : "=a" (res)
-                          : "r" (w), "r" (nv), "a" (ov)
-                          : "cc", "memory");
-    return (int)res;
-}
-
-#elif defined(__sparc)
-#if defined(__GNUC__)
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    unsigned int res;
-
-    __asm__ __volatile__ (
-                  "membar #StoreLoad | #LoadLoad\n"
-#if JS_BITS_PER_WORD == 32
-                  "cas [%1],%2,%3\n"
-#else
-                  "casx [%1],%2,%3\n"
-#endif
-                  "membar #StoreLoad | #LoadLoad\n"
-                  "cmp %2,%3\n"
-                  "be,a 1f\n"
-                  "mov 1,%0\n"
-                  "mov 0,%0\n"
-                  "1:"
-                  : "=r" (res)
-                  : "r" (w), "r" (ov), "r" (nv));
-    return (int)res;
-}
-
-#elif defined(__SUNPRO_CC)
-
-/* Implementation in lock_sparc*.il */
-extern "C" int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv);
-
-#endif
-
-#elif defined(AIX)
-
-#include <sys/atomic_op.h>
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    int res;
-    JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(long));
-
-    res = compare_and_swaplp((atomic_l)w, &ov, nv);
-    if (res)
-        __asm__("isync");
-    return res;
-}
-
-#elif defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
-
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-  return __sync_bool_compare_and_swap(w, ov, nv);
-}
-
-#elif defined(USE_ARM_KUSER)
-
-/* See https://bugzilla.mozilla.org/show_bug.cgi?id=429387 for a
- * description of this ABI; this is a function provided at a fixed
- * location by the kernel in the memory space of each process.
- */
-typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
-#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)
-
-JS_STATIC_ASSERT(sizeof(intptr_t) == sizeof(int));
-
-static JS_ALWAYS_INLINE int
-NativeCompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    volatile int *vp = (volatile int *) w;
-    PRInt32 failed = 1;
-
-    /* Loop until a __kernel_cmpxchg succeeds. See bug 446169 */
-    do {
-        failed = __kernel_cmpxchg(ov, nv, vp);
-    } while (failed && *vp == ov);
-    return !failed;
-}
-
-#elif JS_HAS_NATIVE_COMPARE_AND_SWAP
-
-#error "JS_HAS_NATIVE_COMPARE_AND_SWAP should be 0 if your platform lacks a compare-and-swap instruction."
-
-#endif /* arch-tests */
-
-#if JS_HAS_NATIVE_COMPARE_AND_SWAP
-
-JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    return !!NativeCompareAndSwap(w, ov, nv);
-}
-
-#elif defined(NSPR_LOCK)
-
-# ifdef __GNUC__
-# warning "js_CompareAndSwap is implemented using NSPR lock"
-# endif
-
-JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    int result;
-    static PRLock *CompareAndSwapLock = JS_NEW_LOCK();
-
-    JS_ACQUIRE_LOCK(CompareAndSwapLock);
-    result = (*w == ov);
-    if (result)
-        *w = nv;
-    JS_RELEASE_LOCK(CompareAndSwapLock);
-    return result;
-}
-
-#else /* !defined(NSPR_LOCK) */
-
-#error "NSPR_LOCK should be on when the platform lacks native compare-and-swap."
-
-#endif
-
-void
-js_AtomicSetMask(volatile intptr_t *w, intptr_t mask)
-{
-    intptr_t ov, nv;
-
-    do {
-        ov = *w;
-        nv = ov | mask;
-    } while (!js_CompareAndSwap(w, ov, nv));
-}
-
-void
-js_AtomicClearMask(volatile intptr_t *w, intptr_t mask)
-{
-    intptr_t ov, nv;
-
-    do {
-        ov = *w;
-        nv = ov & ~mask;
-    } while (!js_CompareAndSwap(w, ov, nv));
-}
-
-unsigned
-js_GetCPUCount()
-{
-    static unsigned ncpus = 0;
-    if (ncpus == 0) {
-# ifdef XP_WIN
-        SYSTEM_INFO sysinfo;
-        GetSystemInfo(&sysinfo);
-        ncpus = unsigned(sysinfo.dwNumberOfProcessors);
-# else
-        long n = sysconf(_SC_NPROCESSORS_ONLN);
-        ncpus = (n > 0) ? unsigned(n) : 1;
-# endif
-    }
-    return ncpus;
-}
-
-#ifndef NSPR_LOCK
-
-struct JSFatLock {
-    int         susp;
-    PRLock      *slock;
-    PRCondVar   *svar;
-    JSFatLock   *next;
-    JSFatLock   **prevp;
-};
-
-typedef struct JSFatLockTable {
-    JSFatLock   *free_;
-    JSFatLock   *taken;
-} JSFatLockTable;
-
-#define GLOBAL_LOCK_INDEX(id)   (((uint32_t)(uintptr_t)(id)>>2) & global_locks_mask)
-
-static void
-js_Dequeue(JSThinLock *);
-
-static PRLock **global_locks;
-static uint32_t global_lock_count = 1;
-static uint32_t global_locks_log2 = 0;
-static uint32_t global_locks_mask = 0;
-
-static void
-js_LockGlobal(void *id)
-{
-    uint32_t i = GLOBAL_LOCK_INDEX(id);
-    PR_Lock(global_locks[i]);
-}
-
-static void
-js_UnlockGlobal(void *id)
-{
-    uint32_t i = GLOBAL_LOCK_INDEX(id);
-    PR_Unlock(global_locks[i]);
-}
-
-#endif /* !NSPR_LOCK */
-
-void
-js_InitLock(JSThinLock *tl)
-{
-#ifdef NSPR_LOCK
-    tl->owner = 0;
-    tl->fat = (JSFatLock*)JS_NEW_LOCK();
-#else
-    PodZero(tl);
-#endif
-}
-
-void
-js_FinishLock(JSThinLock *tl)
-{
-#ifdef NSPR_LOCK
-    tl->owner = 0xdeadbeef;
-    if (tl->fat)
-        JS_DESTROY_LOCK(((JSLock*)tl->fat));
-#else
-    JS_ASSERT(tl->owner == 0);
-    JS_ASSERT(tl->fat == NULL);
-#endif
-}
-
-#ifndef NSPR_LOCK
-
-static JSFatLock *
-NewFatlock()
-{
-    JSFatLock *fl = (JSFatLock *) OffTheBooks::malloc_(sizeof(JSFatLock)); /* for now */
-    if (!fl) return NULL;
-    fl->susp = 0;
-    fl->next = NULL;
-    fl->prevp = NULL;
-    fl->slock = PR_NewLock();
-    fl->svar = PR_NewCondVar(fl->slock);
-    return fl;
-}
-
-static void
-DestroyFatlock(JSFatLock *fl)
-{
-    PR_DestroyLock(fl->slock);
-    PR_DestroyCondVar(fl->svar);
-    UnwantedForeground::free_(fl);
-}
-
-static JSFatLock *
-ListOfFatlocks(int listc)
-{
-    JSFatLock *m;
-    JSFatLock *m0;
-    int i;
-
-    JS_ASSERT(listc>0);
-    m0 = m = NewFatlock();
-    for (i=1; i<listc; i++) {
-        m->next = NewFatlock();
-        m = m->next;
-    }
-    return m0;
-}
-
-static void
-DeleteListOfFatlocks(JSFatLock *m)
-{
-    JSFatLock *m0;
-    for (; m; m=m0) {
-        m0 = m->next;
-        DestroyFatlock(m);
-    }
-}
-
-static JSFatLockTable *fl_list_table = NULL;
-static uint32_t        fl_list_table_len = 0;
-static uint32_t        fl_list_chunk_len = 0;
-
-static JSFatLock *
-GetFatlock(void *id)
-{
-    JSFatLock *m;
-
-    uint32_t i = GLOBAL_LOCK_INDEX(id);
-    if (fl_list_table[i].free_ == NULL) {
-#ifdef DEBUG
-        if (fl_list_table[i].taken)
-            printf("Ran out of fat locks!\n");
-#endif
-        fl_list_table[i].free_ = ListOfFatlocks(fl_list_chunk_len);
-    }
-    m = fl_list_table[i].free_;
-    fl_list_table[i].free_ = m->next;
-    m->susp = 0;
-    m->next = fl_list_table[i].taken;
-    m->prevp = &fl_list_table[i].taken;
-    if (fl_list_table[i].taken)
-        fl_list_table[i].taken->prevp = &m->next;
-    fl_list_table[i].taken = m;
-    return m;
-}
-
-static void
-PutFatlock(JSFatLock *m, void *id)
-{
-    uint32_t i;
-    if (m == NULL)
-        return;
-
-    /* Unlink m from fl_list_table[i].taken. */
-    *m->prevp = m->next;
-    if (m->next)
-        m->next->prevp = m->prevp;
-
-    /* Insert m in fl_list_table[i].free. */
-    i = GLOBAL_LOCK_INDEX(id);
-    m->next = fl_list_table[i].free_;
-    fl_list_table[i].free_ = m;
-}
-
-#endif /* !NSPR_LOCK */
-
-JSBool
-js_SetupLocks(int listc, int globc)
-{
-#ifndef NSPR_LOCK
-    uint32_t i;
-
-    if (global_locks)
-        return JS_TRUE;
-#ifdef DEBUG
-    if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */
-        printf("Bad number %d in js_SetupLocks()!\n", listc);
-    if (globc > 100 || globc < 0)   /* globc == number of global locks */
-        printf("Bad number %d in js_SetupLocks()!\n", listc);
-#endif
-    global_locks_log2 = JS_CEILING_LOG2W(globc);
-    global_locks_mask = JS_BITMASK(global_locks_log2);
-    global_lock_count = JS_BIT(global_locks_log2);
-    global_locks = (PRLock **) OffTheBooks::malloc_(global_lock_count * sizeof(PRLock*));
-    if (!global_locks)
-        return JS_FALSE;
-    for (i = 0; i < global_lock_count; i++) {
-        global_locks[i] = PR_NewLock();
-        if (!global_locks[i]) {
-            global_lock_count = i;
-            js_CleanupLocks();
-            return JS_FALSE;
-        }
-    }
-    fl_list_table = (JSFatLockTable *) OffTheBooks::malloc_(i * sizeof(JSFatLockTable));
-    if (!fl_list_table) {
-        js_CleanupLocks();
-        return JS_FALSE;
-    }
-    fl_list_table_len = global_lock_count;
-    for (i = 0; i < global_lock_count; i++)
-        fl_list_table[i].free_ = fl_list_table[i].taken = NULL;
-    fl_list_chunk_len = listc;
-#endif /* !NSPR_LOCK */
-    return JS_TRUE;
-}
-
-void
-js_CleanupLocks()
-{
-#ifndef NSPR_LOCK
-    uint32_t i;
-
-    if (global_locks) {
-        for (i = 0; i < global_lock_count; i++)
-            PR_DestroyLock(global_locks[i]);
-        UnwantedForeground::free_(global_locks);
-        global_locks = NULL;
-        global_lock_count = 1;
-        global_locks_log2 = 0;
-        global_locks_mask = 0;
-    }
-    if (fl_list_table) {
-        for (i = 0; i < fl_list_table_len; i++) {
-            DeleteListOfFatlocks(fl_list_table[i].free_);
-            fl_list_table[i].free_ = NULL;
-            DeleteListOfFatlocks(fl_list_table[i].taken);
-            fl_list_table[i].taken = NULL;
-        }
-        UnwantedForeground::free_(fl_list_table);
-        fl_list_table = NULL;
-        fl_list_table_len = 0;
-    }
-#endif /* !NSPR_LOCK */
-}
-
-#ifdef NSPR_LOCK
-
-static JS_ALWAYS_INLINE void
-ThinLock(JSThinLock *tl, intptr_t me)
-{
-    JS_ACQUIRE_LOCK((JSLock *) tl->fat);
-    tl->owner = me;
-}
-
-static JS_ALWAYS_INLINE void
-ThinUnlock(JSThinLock *tl, intptr_t /*me*/)
-{
-    tl->owner = 0;
-    JS_RELEASE_LOCK((JSLock *) tl->fat);
-}
-
-#else
-
-/*
- * Fast locking and unlocking is implemented by delaying the allocation of a
- * system lock (fat lock) until contention.  As long as a locking thread A
- * runs uncontended, the lock is represented solely by storing A's identity in
- * the object being locked.
- *
- * If another thread B tries to lock the object currently locked by A, B is
- * enqueued into a fat lock structure (which might have to be allocated and
- * pointed to by the object), and suspended using NSPR conditional variables
- * (wait).  A wait bit (Bacon bit) is set in the lock word of the object,
- * signalling to A that when releasing the lock, B must be dequeued and
- * notified.
- *
- * The basic operation of the locking primitives (js_Lock, js_Unlock,
- * js_Enqueue, and js_Dequeue) is compare-and-swap.  Hence, when locking into
- * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p
- * is unlocked.  Similarly, when unlocking p, if compare-and-swap(p, A, 0)
- * succeeds this implies that p is uncontended (no one is waiting because the
- * wait bit is not set).
- *
- * When dequeueing, the lock is released, and one of the threads suspended on
- * the lock is notified.  If other threads still are waiting, the wait bit is
- * kept (in js_Enqueue), and if not, the fat lock is deallocated.
- *
- * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread
- * are serialized using a global lock.  For scalability, a hashtable of global
- * locks is used, which is indexed modulo the thin lock pointer.
- */
-
-/*
- * Invariants:
- * (i)  global lock is held
- * (ii) fl->susp >= 0
- */
-static int
-js_SuspendThread(JSThinLock *tl)
-{
-    JSFatLock *fl;
-    if (tl->fat == NULL)
-        fl = tl->fat = GetFatlock(tl);
-    else
-        fl = tl->fat;
-    JS_ASSERT(fl->susp >= 0);
-    fl->susp++;
-    PR_Lock(fl->slock);
-    js_UnlockGlobal(tl);
-    DebugOnly<PRStatus> stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT);
-    JS_ASSERT(stat != PR_FAILURE);
-    PR_Unlock(fl->slock);
-    js_LockGlobal(tl);
-    fl->susp--;
-    if (fl->susp == 0) {
-        PutFatlock(fl, tl);
-        tl->fat = NULL;
-    }
-    return tl->fat == NULL;
-}
-
-/*
- * (i)  global lock is held
- * (ii) fl->susp > 0
- */
-static void
-js_ResumeThread(JSThinLock *tl)
-{
-    JSFatLock *fl = tl->fat;
-    JS_ASSERT(fl != NULL);
-    JS_ASSERT(fl->susp > 0);
-    PR_Lock(fl->slock);
-    js_UnlockGlobal(tl);
-    DebugOnly<PRStatus> stat = PR_NotifyCondVar(fl->svar);
-    JS_ASSERT(stat != PR_FAILURE);
-    PR_Unlock(fl->slock);
-}
-
-static void
-js_Enqueue(JSThinLock *tl, intptr_t me)
-{
-    intptr_t o, n;
-
-    js_LockGlobal(tl);
-    for (;;) {
-        o = ReadWord(tl->owner);
-        n = Thin_SetWait(o);
-        if (o != 0 && NativeCompareAndSwap(&tl->owner, o, n)) {
-            if (js_SuspendThread(tl))
-                me = Thin_RemoveWait(me);
-            else
-                me = Thin_SetWait(me);
-        }
-        else if (NativeCompareAndSwap(&tl->owner, 0, me)) {
-            js_UnlockGlobal(tl);
-            return;
-        }
-    }
-}
-
-static void
-js_Dequeue(JSThinLock *tl)
-{
-    intptr_t o;
-
-    js_LockGlobal(tl);
-    o = ReadWord(tl->owner);
-    JS_ASSERT(Thin_GetWait(o) != 0);
-    JS_ASSERT(tl->fat != NULL);
-    if (!NativeCompareAndSwap(&tl->owner, o, 0)) /* release it */
-        JS_ASSERT(0);
-    js_ResumeThread(tl);
-}
-
-static JS_ALWAYS_INLINE void
-ThinLock(JSThinLock *tl, intptr_t me)
-{
-    JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-    if (NativeCompareAndSwap(&tl->owner, 0, me))
-        return;
-    if (Thin_RemoveWait(ReadWord(tl->owner)) != me)
-        js_Enqueue(tl, me);
-#ifdef DEBUG
-    else
-        JS_ASSERT(0);
-#endif
-}
-
-static JS_ALWAYS_INLINE void
-ThinUnlock(JSThinLock *tl, intptr_t me)
-{
-    JS_ASSERT(CURRENT_THREAD_IS_ME(me));
-
-    /*
-     * Since we can race with the NativeCompareAndSwap in js_Enqueue, we need
-     * to use a C_A_S here as well -- Arjan van de Ven 30/1/08
-     */
-    if (NativeCompareAndSwap(&tl->owner, me, 0))
-        return;
-
-    JS_ASSERT(Thin_GetWait(tl->owner));
-    if (Thin_RemoveWait(ReadWord(tl->owner)) == me)
-        js_Dequeue(tl);
-#ifdef DEBUG
-    else
-        JS_ASSERT(0);   /* unbalanced unlock */
-#endif
-}
-
-#endif /* !NSPR_LOCK */
-
-void
-js_Lock(JSContext *cx, JSThinLock *tl)
-{
-    ThinLock(tl, CX_THINLOCK_ID(cx));
-}
-
-void
-js_Unlock(JSContext *cx, JSThinLock *tl)
-{
-    ThinUnlock(tl, CX_THINLOCK_ID(cx));
-}
-
-#endif /* JS_THREADSAFE */
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -34,207 +34,54 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef jslock_h__
 #define jslock_h__
 
-#include "jstypes.h"
 #include "jsapi.h"
-#include "jsprvtd.h"
 
 #ifdef JS_THREADSAFE
+
 # include "pratom.h"
 # include "prlock.h"
 # include "prcvar.h"
 # include "prthread.h"
 # include "prinit.h"
-#endif
 
-#ifdef JS_THREADSAFE
+# define JS_ATOMIC_INCREMENT(p)      PR_ATOMIC_INCREMENT((PRInt32 *)(p))
+# define JS_ATOMIC_DECREMENT(p)      PR_ATOMIC_DECREMENT((PRInt32 *)(p))
+# define JS_ATOMIC_ADD(p,v)          PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v))
+# define JS_ATOMIC_SET(p,v)          PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v))
 
-#if (defined(_WIN32) && defined(_M_IX86)) ||                                  \
-    (defined(_WIN64) && (defined(_M_AMD64) || defined(_M_X64))) ||            \
-    (defined(__i386) && (defined(__GNUC__) || defined(__SUNPRO_CC))) ||       \
-    (defined(__x86_64) && (defined(__GNUC__) || defined(__SUNPRO_CC))) ||     \
-    (defined(__sparc) && (defined(__GNUC__) || defined(__SUNPRO_CC))) ||      \
-    (defined(__arm__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) ||      \
-    defined(AIX) ||                                                           \
-    defined(USE_ARM_KUSER)
-# define JS_HAS_NATIVE_COMPARE_AND_SWAP 1
-#else
-# define JS_HAS_NATIVE_COMPARE_AND_SWAP 0
-#endif
+#else  /* JS_THREADSAFE */
 
-#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP
-# define NSPR_LOCK 1
-#else
-# undef NSPR_LOCK
-#endif
+# define JS_ATOMIC_INCREMENT(p)      (++*(p))
+# define JS_ATOMIC_DECREMENT(p)      (--*(p))
+# define JS_ATOMIC_ADD(p,v)          (*(p) += (v))
+# define JS_ATOMIC_SET(p,v)          (*(p) = (v))
 
-#define Thin_GetWait(W) ((intptr_t)(W) & 0x1)
-#define Thin_SetWait(W) ((intptr_t)(W) | 0x1)
-#define Thin_RemoveWait(W) ((intptr_t)(W) & ~0x1)
-
-typedef struct JSFatLock JSFatLock;
-
-typedef struct JSThinLock {
-    intptr_t    owner;
-    JSFatLock   *fat;
-} JSThinLock;
-
-#define CX_THINLOCK_ID(cx)       ((intptr_t)(cx)->thread())
-#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId())
-
-typedef PRLock JSLock;
-
-/*
- * Atomic increment and decrement for a reference counter, given jsrefcount *p.
- * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work.
- */
-#define JS_ATOMIC_INCREMENT(p)      PR_ATOMIC_INCREMENT((PRInt32 *)(p))
-#define JS_ATOMIC_DECREMENT(p)      PR_ATOMIC_DECREMENT((PRInt32 *)(p))
-#define JS_ATOMIC_ADD(p,v)          PR_ATOMIC_ADD((PRInt32 *)(p), (PRInt32)(v))
-#define JS_ATOMIC_SET(p,v)          PR_ATOMIC_SET((PRInt32 *)(p), (PRInt32)(v))
-
-#define js_CurrentThreadId()        PR_GetCurrentThread()
-#define JS_NEW_LOCK()               PR_NewLock()
-#define JS_DESTROY_LOCK(l)          PR_DestroyLock(l)
-#define JS_ACQUIRE_LOCK(l)          PR_Lock(l)
-#define JS_RELEASE_LOCK(l)          PR_Unlock(l)
-
-#define JS_NEW_CONDVAR(l)           PR_NewCondVar(l)
-#define JS_DESTROY_CONDVAR(cv)      PR_DestroyCondVar(cv)
-#define JS_WAIT_CONDVAR(cv,to)      PR_WaitCondVar(cv,to)
-#define JS_NO_TIMEOUT               PR_INTERVAL_NO_TIMEOUT
-#define JS_NOTIFY_CONDVAR(cv)       PR_NotifyCondVar(cv)
-#define JS_NOTIFY_ALL_CONDVAR(cv)   PR_NotifyAllCondVar(cv)
-
-#define JS_LOCK(cx, tl)             js_Lock(cx, tl)
-#define JS_UNLOCK(cx, tl)           js_Unlock(cx, tl)
-
-extern void js_Lock(JSContext *cx, JSThinLock *tl);
-extern void js_Unlock(JSContext *cx, JSThinLock *tl);
-extern int js_SetupLocks(int,int);
-extern void js_CleanupLocks();
-extern void js_InitLock(JSThinLock *);
-extern void js_FinishLock(JSThinLock *);
-
-#else  /* !JS_THREADSAFE */
-
-#define JS_ATOMIC_INCREMENT(p)      (++*(p))
-#define JS_ATOMIC_DECREMENT(p)      (--*(p))
-#define JS_ATOMIC_ADD(p,v)          (*(p) += (v))
-#define JS_ATOMIC_SET(p,v)          (*(p) = (v))
-
-#define js_CurrentThreadId()        ((void*)NULL)
-#define JS_NEW_LOCK()               NULL
-#define JS_DESTROY_LOCK(l)          ((void)0)
-#define JS_ACQUIRE_LOCK(l)          ((void)0)
-#define JS_RELEASE_LOCK(l)          ((void)0)
-#define JS_LOCK(cx, tl)             ((void)0)
-#define JS_UNLOCK(cx, tl)           ((void)0)
-
-#define JS_NEW_CONDVAR(l)           NULL
-#define JS_DESTROY_CONDVAR(cv)      ((void)0)
-#define JS_WAIT_CONDVAR(cv,to)      ((void)0)
-#define JS_NOTIFY_CONDVAR(cv)       ((void)0)
-#define JS_NOTIFY_ALL_CONDVAR(cv)   ((void)0)
-
-#endif /* !JS_THREADSAFE */
-
-#define JS_LOCK_GC(rt)              JS_ACQUIRE_LOCK((rt)->gcLock)
-#define JS_UNLOCK_GC(rt)            JS_RELEASE_LOCK((rt)->gcLock)
-#define JS_AWAIT_GC_DONE(rt)        JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
-#define JS_NOTIFY_GC_DONE(rt)       JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
-#define JS_AWAIT_REQUEST_DONE(rt)   JS_WAIT_CONDVAR((rt)->requestDone,        \
-                                                    JS_NO_TIMEOUT)
-#define JS_NOTIFY_REQUEST_DONE(rt)  JS_NOTIFY_CONDVAR((rt)->requestDone)
-
-#ifndef JS_SET_OBJ_INFO
-#define JS_SET_OBJ_INFO(obj,f,l)        ((void)0)
-#endif
-#ifndef JS_SET_TITLE_INFO
-#define JS_SET_TITLE_INFO(title,f,l)    ((void)0)
-#endif
-
-#ifdef JS_THREADSAFE
-
-extern JSBool
-js_CompareAndSwap(volatile intptr_t *w, intptr_t ov, intptr_t nv);
-
-/* Atomically bitwise-or the mask into the word *w using compare and swap. */
-extern void
-js_AtomicSetMask(volatile intptr_t *w, intptr_t mask);
-
-/*
- * Atomically bitwise-and the complement of the mask into the word *w using
- * compare and swap.
- */
-extern void
-js_AtomicClearMask(volatile intptr_t *w, intptr_t mask);
-
-#define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask)
-#define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask)
-
-extern  unsigned
-js_GetCPUCount();
-
-#else
-
-static inline JSBool
-js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv)
-{
-    return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE;
-}
-
-#define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask))
-#define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask))
-
-static inline unsigned
-js_GetCPUCount()
-{
-    return 1;
-}
-
-#endif
-
-#ifdef __cplusplus
+#endif /* JS_THREADSAFE */
 
 namespace js {
 
-#ifdef JS_THREADSAFE
-class AutoLock {
-  private:
-    JSLock *lock;
-
-  public:
-    AutoLock(JSLock *lock) : lock(lock) { JS_ACQUIRE_LOCK(lock); }
-    ~AutoLock() { JS_RELEASE_LOCK(lock); }
-};
-# define JS_AUTO_LOCK_GUARD(name, l) AutoLock name((l));
-#else
-# define JS_AUTO_LOCK_GUARD(name, l)
-#endif
-
-class AutoAtomicIncrement {
+class AutoAtomicIncrement
+{
     int32_t *p;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     AutoAtomicIncrement(int32_t *p JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : p(p) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ATOMIC_INCREMENT(p);
     }
 
     ~AutoAtomicIncrement() {
         JS_ATOMIC_DECREMENT(p);
     }
 };
 
-} /* namespace js */
-
-#endif
+}  /* namespace js */
 
 #endif /* jslock_h___ */
deleted file mode 100644
--- a/js/src/jslocko.asm
+++ /dev/null
@@ -1,60 +0,0 @@
-; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*-
-
-; ***** BEGIN LICENSE BLOCK *****
-; Version: MPL 1.1/GPL 2.0/LGPL 2.1
-; 
-; The contents of this file are subject to the Mozilla Public License Version 
-; 1.1 (the "License"); you may not use this file except in compliance with 
-; the License. You may obtain a copy of the License at 
-; http://www.mozilla.org/MPL/
-; 
-; Software distributed under the License is distributed on an "AS IS" basis,
-; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-; for the specific language governing rights and limitations under the
-; License.
-; 
-; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly.
-; 
-; The Initial Developer of the Original Code is
-; IBM Corporation.
-; Portions created by the Initial Developer are Copyright (C) 2001
-; the Initial Developer. All Rights Reserved.
-; 
-; Contributor(s):
-; 
-; Alternatively, the contents of this file may be used under the terms of
-; either the GNU General Public License Version 2 or later (the "GPL"), or
-; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-; in which case the provisions of the GPL or the LGPL are applicable instead
-; of those above. If you wish to allow use of your version of this file only
-; under the terms of either the GPL or the LGPL, and not to allow others to
-; use your version of this file under the terms of the MPL, indicate your
-; decision by deleting the provisions above and replace them with the notice
-; and other provisions required by the GPL or the LGPL. If you do not delete
-; the provisions above, a recipient may use your version of this file under
-; the terms of any one of the MPL, the GPL or the LGPL.
-; 
-; ***** END LICENSE BLOCK *****
-
-        .486P
-        .MODEL FLAT, OPTLINK
-        .STACK
-
-        .CODE
-
-;;;---------------------------------------------------------------------
-;;; int _Optlink js_CompareAndSwap(intptr_t *w, intptr_t ov, intptr_t nv)
-;;;---------------------------------------------------------------------
-js_CompareAndSwap     PROC OPTLINK EXPORT
-        push ebx
-        mov ebx, eax
-        mov eax, edx
-        mov edx, ebx
-        lock cmpxchg [ebx], ecx
-        sete al
-        and eax, 1h
-        pop ebx
-        ret
-js_CompareAndSwap     endp
-
-        END
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -103,17 +103,17 @@ ComputeAccurateDecimalInteger(JSContext 
         char c = char(start[i]);
         JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
         cstr[i] = c;
     }
     cstr[length] = 0;
 
     char *estr;
     int err = 0;
-    *dp = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
+    *dp = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
     if (err == JS_DTOA_ENOMEM) {
         JS_ReportOutOfMemory(cx);
         cx->free_(cstr);
         return false;
     }
     if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL)
         *dp = js_PositiveInfinity;
     cx->free_(cstr);
@@ -811,17 +811,17 @@ num_to(JSContext *cx, Native native, JSD
             ToCStringBuf cbuf;
             numStr = IntToCString(&cbuf, jsint(precision));
             JS_ASSERT(numStr);
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
             return JS_FALSE;
         }
     }
 
-    numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf,
+    numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf,
                        oneArgMode, (jsint)precision + precisionOffset, d);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
     JSString *str = js_NewStringCopyZ(cx, numStr);
     if (!str)
         return JS_FALSE;
@@ -1082,20 +1082,20 @@ FracNumberToCString(JSContext *cx, ToCSt
          *   Printing floating-point numbers quickly and accurately with integers.
          *   Florian Loitsch, PLDI 2010.
          *
          * It fails on a small number of cases, whereupon we fall back to
          * js_dtostr() (which uses David Gay's dtoa).
          */
         numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
         if (!numStr)
-            numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, cbuf->sbuf, cbuf->sbufSize,
+            numStr = js_dtostr(cx->runtime->dtoaState, cbuf->sbuf, cbuf->sbufSize,
                                DTOSTR_STANDARD, 0, d);
     } else {
-        numStr = cbuf->dbuf = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d);
+        numStr = cbuf->dbuf = js_dtobasestr(cx->runtime->dtoaState, base, d);
     }
     return numStr;
 }
 
 char *
 NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base/* = 10*/)
 {
     int32_t i;
@@ -1416,17 +1416,17 @@ js_strtod(JSContext *cx, const jschar *s
     istr = cstr;
     if ((negative = (*istr == '-')) != 0 || *istr == '+')
         istr++;
     if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
         d = negative ? js_NegativeInfinity : js_PositiveInfinity;
         estr = istr + 8;
     } else {
         int err;
-        d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
+        d = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
         if (d == HUGE_VAL)
             d = js_PositiveInfinity;
         else if (d == -HUGE_VAL)
             d = js_NegativeInfinity;
     }
 
     i = estr - cstr;
     if (cstr != cbuf)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1514,31 +1514,16 @@ JSObject::getPrivateDataOffset(size_t nf
 struct JSObject_Slots2 : JSObject { js::Value fslots[2]; };
 struct JSObject_Slots4 : JSObject { js::Value fslots[4]; };
 struct JSObject_Slots8 : JSObject { js::Value fslots[8]; };
 struct JSObject_Slots12 : JSObject { js::Value fslots[12]; };
 struct JSObject_Slots16 : JSObject { js::Value fslots[16]; };
 
 #define JSSLOT_FREE(clasp)  JSCLASS_RESERVED_SLOTS(clasp)
 
-#ifdef JS_THREADSAFE
-
-/*
- * The GC runs only when all threads except the one on which the GC is active
- * are suspended at GC-safe points, so calling obj->getSlot() from the GC's
- * thread is safe when rt->gcRunning is set. See jsgc.cpp for details.
- */
-#define THREAD_IS_RUNNING_GC(rt, thread)                                      \
-    ((rt)->gcRunning && (rt)->gcThread == (thread))
-
-#define CX_THREAD_IS_RUNNING_GC(cx)                                           \
-    THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread)
-
-#endif /* JS_THREADSAFE */
-
 class JSValueArray {
   public:
     jsval *array;
     size_t length;
 
     JSValueArray(jsval *v, size_t c) : array(v), length(c) {}
 };
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -457,17 +457,17 @@ ToDisassemblySource(JSContext *cx, jsval
             return false;
         nbytes = JS_sprintf_append(NULL, "%s", nbytes);
         if (!nbytes)
             return false;
         bytes->initBytes(nbytes);
         return true;
     }
 
-    if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) {
+    if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) {
         char *source = JS_sprintf_append(NULL, "<value>");
         if (!source)
             return false;
         bytes->initBytes(source);
         return true;
     }
 
     if (!JSVAL_IS_PRIMITIVE(v)) {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -78,17 +78,17 @@ GetFunctionProxyConstruct(JSObject *prox
     JS_ASSERT(IsFunctionProxy(proxy));
     JS_ASSERT(proxy->slotSpan() > JSSLOT_PROXY_CONSTRUCT);
     return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT);
 }
 
 static bool
 OperationInProgress(JSContext *cx, JSObject *proxy)
 {
-    PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
+    PendingProxyOperation *op = cx->runtime->pendingProxyOperation;
     while (op) {
         if (op->object == proxy)
             return true;
         op = op->next;
     }
     return false;
 }
 
@@ -705,28 +705,28 @@ ScriptedProxyHandler::iterate(JSContext 
         return ProxyHandler::iterate(cx, proxy, flags, vp);
     return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
 }
 
 ScriptedProxyHandler ScriptedProxyHandler::singleton;
 
 class AutoPendingProxyOperation {
-    ThreadData              *data;
+    JSRuntime               *rt;
     PendingProxyOperation   op;
   public:
-    AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
-        op.next = data->pendingProxyOperation;
+    AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : rt(cx->runtime) {
+        op.next = rt->pendingProxyOperation;
         op.object = proxy;
-        data->pendingProxyOperation = &op;
+        rt->pendingProxyOperation = &op;
     }
 
     ~AutoPendingProxyOperation() {
-        JS_ASSERT(data->pendingProxyOperation == &op);
-        data->pendingProxyOperation = op.next;
+        JS_ASSERT(rt->pendingProxyOperation == &op);
+        rt->pendingProxyOperation = op.next;
     }
 };
 
 bool
 Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                              PropertyDescriptor *desc)
 {
     JS_CHECK_RECURSION(cx, return false);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -81,17 +81,16 @@ typedef uintptr_t   jsatomid;
 
 /* Struct typedefs. */
 typedef struct JSArgumentFormatMap  JSArgumentFormatMap;
 typedef struct JSGCThing            JSGCThing;
 typedef struct JSGenerator          JSGenerator;
 typedef struct JSNativeEnumerator   JSNativeEnumerator;
 typedef struct JSProperty           JSProperty;
 typedef struct JSSharpObjectMap     JSSharpObjectMap;
-typedef struct JSThread             JSThread;
 typedef struct JSTryNote            JSTryNote;
 
 /* Friend "Advanced API" typedefs. */
 typedef struct JSAtomState          JSAtomState;
 typedef struct JSCodeSpec           JSCodeSpec;
 typedef struct JSPrinter            JSPrinter;
 typedef struct JSStackHeader        JSStackHeader;
 typedef struct JSSubString          JSSubString;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -776,17 +776,17 @@ JSScript::initCounts(JSContext *cx)
         cursor += capacity * sizeof(double);
         next = pc + GetBytecodeLength(pc);
     }
 
     JS_ASSERT(size_t(cursor - base) == bytes);
 
     /* Enable interrupts in any interpreter frames running on this script. */
     InterpreterFrames *frames;
-    for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older)
+    for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
 
     return true;
 }
 
 void
 JSScript::destroyCounts(JSContext *cx)
 {
@@ -1730,17 +1730,17 @@ JSScript::ensureHasDebug(JSContext *cx)
         return false;
 
     /*
      * Ensure that any Interpret() instances running on this script have
      * interrupts enabled. The interrupts must stay enabled until the
      * debug state is destroyed.
      */
     InterpreterFrames *frames;
-    for (frames = JS_THREAD_DATA(cx)->interpreterFrames; frames; frames = frames->older)
+    for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
 
     return true;
 }
 
 bool
 JSScript::recompileForStepMode(JSContext *cx)
 {
deleted file mode 100644
--- a/js/src/lock_sparcv8plus.il
+++ /dev/null
@@ -1,84 +0,0 @@
-! 
-! ***** BEGIN LICENSE BLOCK *****
-! Version: MPL 1.1/GPL 2.0/LGPL 2.1
-!
-! The contents of this file are subject to the Mozilla Public License Version
-! 1.1 (the "License"); you may not use this file except in compliance with
-! the License. You may obtain a copy of the License at
-! http://www.mozilla.org/MPL/
-!
-! Software distributed under the License is distributed on an "AS IS" basis,
-! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-! for the specific language governing rights and limitations under the
-! License.
-!
-! The Original Code is Mozilla Communicator client code, released
-! March 31, 1998.
-!
-! The Initial Developer of the Original Code is
-! Netscape Communications Corporation.
-! Portions created by the Initial Developer are Copyright (C) 1998-1999
-! the Initial Developer. All Rights Reserved.
-!
-! Contributor(s):
-!
-! Alternatively, the contents of this file may be used under the terms of
-! either the GNU General Public License Version 2 or later (the "GPL"), or
-! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-! in which case the provisions of the GPL or the LGPL are applicable instead
-! of those above. If you wish to allow use of your version of this file only
-! under the terms of either the GPL or the LGPL, and not to allow others to
-! use your version of this file under the terms of the MPL, indicate your
-! decision by deleting the provisions above and replace them with the notice
-! and other provisions required by the GPL or the LGPL. If you do not delete
-! the provisions above, a recipient may use your version of this file under
-! the terms of any one of the MPL, the GPL or the LGPL.
-!
-! ***** END LICENSE BLOCK *****
-
-!
-!  atomic compare-and-swap routines for V8+ (ultrasparc)
-!
-!  ======================================================================
-!
-!  Perform the sequence *a = b atomically with respect to previous value
-!  of a (a0). If *a==a0 then assign *a to b, all in one atomic operation.
-!  Returns 1 if assignment happened, and 0 otherwise.	
-!
-!  usage : old_val = compare_and_swap(address, oldval, newval)
-!
-!  -----------------------
-!  Note on REGISTER USAGE:
-!  as this is a LEAF procedure, a new stack frame is not created;
-!  we use the caller stack frame so what would normally be %i (input)
-!  registers are actually %o (output registers).  Also, we must not
-!  overwrite the contents of %l (local) registers as they are not
-!  assumed to be volatile during calls.
-!
-!  So, the registers used are:
-!     %o0  [input]   - the address of the value to increment
-!     %o1  [input]   - the old value to compare with	
-!     %o2  [input]   - the new value to set for [%o0]
-!     %o3  [local]   - work register
-!  -----------------------
-!  ======================================================================
-!
-!  v8plus
-
-    .inline NativeCompareAndSwap,3
-
-	stbar
-	cas [%o0],%o1,%o2               ! compare *w with old value and set to new if equal
-	cmp %o1,%o2                     ! did we succeed?
-	be,a 1f                         ! yes
-	mov 1,%o0                       ! return true (annulled when no jump)
-	mov 0,%o0                       ! return false
-1:
-
-    .end
-
-!
-!  end
-!
-!  ======================================================================
-!
deleted file mode 100644
--- a/js/src/lock_sparcv9.il
+++ /dev/null
@@ -1,84 +0,0 @@
-! 
-! ***** BEGIN LICENSE BLOCK *****
-! Version: MPL 1.1/GPL 2.0/LGPL 2.1
-!
-! The contents of this file are subject to the Mozilla Public License Version
-! 1.1 (the "License"); you may not use this file except in compliance with
-! the License. You may obtain a copy of the License at
-! http://www.mozilla.org/MPL/
-!
-! Software distributed under the License is distributed on an "AS IS" basis,
-! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-! for the specific language governing rights and limitations under the
-! License.
-!
-! The Original Code is Mozilla Communicator client code, released
-! March 31, 1998.
-!
-! The Initial Developer of the Original Code is
-! Netscape Communications Corporation.
-! Portions created by the Initial Developer are Copyright (C) 1998-1999
-! the Initial Developer. All Rights Reserved.
-!
-! Contributor(s):
-!
-! Alternatively, the contents of this file may be used under the terms of
-! either the GNU General Public License Version 2 or later (the "GPL"), or
-! the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-! in which case the provisions of the GPL or the LGPL are applicable instead
-! of those above. If you wish to allow use of your version of this file only
-! under the terms of either the GPL or the LGPL, and not to allow others to
-! use your version of this file under the terms of the MPL, indicate your
-! decision by deleting the provisions above and replace them with the notice
-! and other provisions required by the GPL or the LGPL. If you do not delete
-! the provisions above, a recipient may use your version of this file under
-! the terms of any one of the MPL, the GPL or the LGPL.
-!
-! ***** END LICENSE BLOCK *****
-
-!
-!  atomic compare-and-swap routines for V9 (ultrasparc)
-!
-!  ======================================================================
-!
-!  Perform the sequence *a = b atomically with respect to previous value
-!  of a (a0). If *a==a0 then assign *a to b, all in one atomic operation.
-!  Returns 1 if assignment happened, and 0 otherwise.	
-!
-!  usage : old_val = compare_and_swap(address, oldval, newval)
-!
-!  -----------------------
-!  Note on REGISTER USAGE:
-!  as this is a LEAF procedure, a new stack frame is not created;
-!  we use the caller stack frame so what would normally be %i (input)
-!  registers are actually %o (output registers).  Also, we must not
-!  overwrite the contents of %l (local) registers as they are not
-!  assumed to be volatile during calls.
-!
-!  So, the registers used are:
-!     %o0  [input]   - the address of the value to increment
-!     %o1  [input]   - the old value to compare with	
-!     %o2  [input]   - the new value to set for [%o0]
-!     %o3  [local]   - work register
-!  -----------------------
-!  ======================================================================
-!
-!  v9
-
-    .inline NativeCompareAndSwap,3
-
-	stbar
-	casx [%o0],%o1,%o2              ! compare *w with old value and set to new if equal
-	cmp %o1,%o2                     ! did we succeed?
-	be,a 1f                         ! yes
-	mov 1,%o0                       ! return true (annulled when no jump)
-	mov 0,%o0                       ! return false
-1:
-
-    .end
-
-!
-!  end
-!
-!  ======================================================================
-!
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3928,29 +3928,17 @@ mjit::Compiler::emitStubCall(void *ptr, 
 void
 mjit::Compiler::interruptCheckHelper()
 {
     Jump jump;
     if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) {
         /* For barrier verification, always take the interrupt so we can verify. */
         jump = masm.jump();
     } else {
-        /*
-         * Bake in and test the address of the interrupt counter for the runtime.
-         * This is faster than doing two additional loads for the context's
-         * thread data, but will cause this thread to run slower if there are
-         * pending interrupts on some other thread.  For non-JS_THREADSAFE builds
-         * we can skip this, as there is only one flag to poll.
-         */
-#ifdef JS_THREADSAFE
-        void *interrupt = (void*) &cx->runtime->interruptCounter;
-#else
-        void *interrupt = (void*) &JS_THREAD_DATA(cx)->interruptFlags;
-#endif
-
+        void *interrupt = (void*) &cx->runtime->interrupt;
 #if defined(JS_CPU_X86) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
         jump = masm.branch32(Assembler::NotEqual, AbsoluteAddress(interrupt), Imm32(0));
 #else
         /* Handle processors that can't load from absolute addresses. */
         RegisterID reg = frame.allocReg();
         masm.move(ImmPtr(interrupt), reg);
         jump = masm.branchTest32(Assembler::NonZero, Address(reg, 0));
         frame.freeReg(reg);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1892,17 +1892,17 @@ stubs::InvariantFailure(VMFrame &f, void
     return rval;
 }
 
 void JS_FASTCALL
 stubs::Exception(VMFrame &f)
 {
     // Check the interrupt flag to allow interrupting deeply nested exception
     // handling.
-    if (JS_THREAD_DATA(f.cx)->interruptFlags && !js_HandleExecutionInterrupt(f.cx))
+    if (f.cx->runtime->interrupt && !js_HandleExecutionInterrupt(f.cx))
         THROW();
 
     f.regs.sp[0] = f.cx->getPendingException();
     f.cx->clearPendingException();
 }
 
 void JS_FASTCALL
 stubs::FunctionFramePrologue(VMFrame &f)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3450,17 +3450,17 @@ CancelExecution(JSRuntime *rt)
 {
     gCanceled = true;
     if (gExitCode == 0)
         gExitCode = EXITCODE_TIMEOUT;
 #ifdef JS_THREADSAFE
     if (gWorkerThreadPool)
         js::workers::terminateAll(gWorkerThreadPool);
 #endif
-    JS_TriggerAllOperationCallbacks(rt);
+    JS_TriggerRuntimeOperationCallback(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);
     (void)dummy;
 #else
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -50,16 +50,26 @@
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsstdint.h"
 #include "jslock.h"
 #include "jsworkers.h"
 
 extern size_t gMaxStackSize;
 
+class AutoLock
+{
+  private:
+    PRLock *lock;
+
+  public:
+    AutoLock(PRLock *lock) : lock(lock) { PR_Lock(lock); }
+    ~AutoLock() { PR_Unlock(lock); }
+};
+
 /*
  * JavaScript shell workers.
  *
  * == Object lifetime rules ==
  *
  *   - The ThreadPool lasts from init() to finish().
  *
  *   - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from
@@ -145,17 +155,17 @@ class Worker;
 class WorkerParent {
   protected:
     typedef HashSet<Worker *, DefaultHasher<Worker *>, SystemAllocPolicy> ChildSet;
     ChildSet children;
 
     bool initWorkerParent() { return children.init(8); }
 
   public:
-    virtual JSLock *getLock() = 0;
+    virtual PRLock *getLock() = 0;
     virtual ThreadPool *getThreadPool() = 0;
     virtual bool post(Event *item) = 0;  // false on OOM or queue closed
     virtual void trace(JSTracer *trc) = 0;
 
     bool addChild(Worker *w) {
         AutoLock hold(getLock());
         return children.put(w) != NULL;
     }
@@ -172,65 +182,65 @@ class WorkerParent {
     void notifyTerminating();
 };
 
 template <class T>
 class ThreadSafeQueue
 {
   protected:
     Queue<T, SystemAllocPolicy> queue;
-    JSLock *lock;
+    PRLock *lock;
     PRCondVar *condvar;
     bool closed;
 
   private:
     Vector<T, 8, SystemAllocPolicy> busy;
 
   protected:
     ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {}
 
     ~ThreadSafeQueue() {
         if (condvar)
-            JS_DESTROY_CONDVAR(condvar);
+            PR_DestroyCondVar(condvar);
         if (lock)
-            JS_DESTROY_LOCK(lock);
+            PR_DestroyLock(lock);
     }
 
     // Called by take() with the lock held.
     virtual bool shouldStop() { return closed; }
 
   public:
     bool initThreadSafeQueue() {
         JS_ASSERT(!lock);
         JS_ASSERT(!condvar);
-        return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock));
+        return (lock = PR_NewLock()) && (condvar = PR_NewCondVar(lock));
     }
 
     bool post(T t) {
         AutoLock hold(lock);
         if (closed)
             return false;
         if (queue.empty())
-            JS_NOTIFY_ALL_CONDVAR(condvar);
+            PR_NotifyAllCondVar(condvar);
         return queue.push(t);
     }
 
     void close() {
         AutoLock hold(lock);
         closed = true;
         queue.clear();
-        JS_NOTIFY_ALL_CONDVAR(condvar);
+        PR_NotifyAllCondVar(condvar);
     }
 
     // The caller must hold the lock.
     bool take(T *t) {
         while (queue.empty()) {
             if (shouldStop())
                 return false;
-            JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT);
+            PR_WaitCondVar(condvar, PR_INTERVAL_NO_TIMEOUT);
         }
         *t = queue.pop();
         busy.append(*t);
         return true;
     }
 
     // The caller must hold the lock.
     void drop(T item) {
@@ -248,17 +258,17 @@ class ThreadSafeQueue
 
     bool isIdle() {
         AutoLock hold(lock);
         return lockedIsIdle();
     }
 
     void wake() {
         AutoLock hold(lock);
-        JS_NOTIFY_ALL_CONDVAR(condvar);
+        PR_NotifyAllCondVar(condvar);
     }
 
     void trace(JSTracer *trc) {
         AutoLock hold(lock);
         for (T *p = busy.begin(); p != busy.end(); p++)
             (*p)->trace(trc);
         queue.trace(trc);
     }
@@ -366,17 +376,17 @@ class MainQueue MOZ_FINAL : public Event
     bool init() { return initThreadSafeQueue() && initWorkerParent(); }
 
     void destroy(JSContext *cx) {
         while (!queue.empty())
             queue.pop()->destroy(cx);
         delete this;
     }
 
-    virtual JSLock *getLock() { return lock; }
+    virtual PRLock *getLock() { return lock; }
     virtual ThreadPool *getThreadPool() { return threadPool; }
 
   protected:
     virtual bool shouldStop();
 
   public:
     virtual bool post(Event *event) { return EventQueue::post(event); }
 
@@ -385,17 +395,17 @@ class MainQueue MOZ_FINAL : public Event
     void traceChildren(JSTracer *trc) { EventQueue::trace(trc); }
 
     JSBool mainThreadWork(JSContext *cx, bool continueOnError) {
         JSAutoSuspendRequest suspend(cx);
         AutoLock hold(lock);
 
         Event *event;
         while (take(&event)) {
-            JS_RELEASE_LOCK(lock);
+            PR_Unlock(lock);
             Event::Result result;
             {
                 JSAutoRequest req(cx);
                 result = event->process(cx);
                 if (result == Event::forwardToParent) {
                     // FIXME - pointlessly truncates the string to 8 bits
                     jsval data;
                     JSAutoByteString bytes;
@@ -409,17 +419,17 @@ class MainQueue MOZ_FINAL : public Event
                     result = Event::fail;
                 }
                 if (result == Event::fail && continueOnError) {
                     if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx))
                         JS_ClearPendingException(cx);
                     result = Event::ok;
                 }
             }
-            JS_ACQUIRE_LOCK(lock);
+            PR_Lock(lock);
             drop(event);
             event->destroy(cx);
             if (result != Event::ok)
                 return false;
         }
         return true;
     }
 };
@@ -591,17 +601,17 @@ class ThreadPool
 class Worker MOZ_FINAL : public WorkerParent
 {
   private:
     ThreadPool *threadPool;
     WorkerParent *parent;
     JSObject *object;  // Worker object exposed to parent
     JSRuntime *runtime;
     JSContext *context;
-    JSLock *lock;
+    PRLock *lock;
     Queue<Event *, SystemAllocPolicy> events;  // owning pointers to pending events
     Event *current;
     bool terminated;
     int32_t terminateFlag;
 
     static JSClass jsWorkerClass;
 
     Worker()
@@ -611,17 +621,17 @@ class Worker MOZ_FINAL : public WorkerPa
     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();
+        lock = PR_NewLock();
         return lock &&
                createRuntime(parentcx) &&
                createContext(parentcx, parent) &&
                JS_SetPrivate(parentcx, obj, this);
     }
 
     bool createRuntime(JSContext *parentcx) {
         runtime = JS_NewRuntime(1L * 1024L * 1024L);
@@ -676,17 +686,16 @@ class Worker MOZ_FINAL : public WorkerPa
 
         ctor = JS_GetConstructor(context, proto);
         if (!ctor)
             goto bad;
 
         js::SetFunctionNativeReserved(post, 0, PRIVATE_TO_JSVAL(this));
 
         JS_EndRequest(context);
-        JS_ClearContextThread(context);
         return true;
 
     bad:
         JS_EndRequest(context);
         JS_DestroyContext(context);
         context = NULL;
         return false;
     }
@@ -764,23 +773,22 @@ class Worker MOZ_FINAL : public WorkerPa
         dispose();
     }
 
     void dispose() {
         JS_ASSERT(!current);
         while (!events.empty())
             events.pop()->destroy(context);
         if (lock) {
-            JS_DESTROY_LOCK(lock);
+            PR_DestroyLock(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;
@@ -796,17 +804,17 @@ class Worker MOZ_FINAL : public WorkerPa
                           JSString *scriptName, JSObject *obj);
 
     JSObject *asObject() { return object; }
 
     JSObject *getGlobal() { return JS_GetGlobalObject(context); }
 
     WorkerParent *getParent() { return parent; }
 
-    virtual JSLock *getLock() { return lock; }
+    virtual PRLock *getLock() { return lock; }
 
     virtual ThreadPool *getThreadPool() { return threadPool; }
 
     bool post(Event *event) {
         AutoLock hold(lock);
         if (terminated)
             return false;
         if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this))
@@ -1025,25 +1033,25 @@ MainQueue::trace(JSTracer *trc)
 }
 
 void
 WorkerQueue::work() {
     AutoLock hold(lock);
 
     Worker *w;
     while (take(&w)) {  // can block outside the mutex
-        JS_RELEASE_LOCK(lock);
+        PR_Unlock(lock);
         w->processOneEvent();     // enters request on w->context
-        JS_ACQUIRE_LOCK(lock);
+        PR_Lock(lock);
         drop(w);
 
         if (lockedIsIdle()) {
-            JS_RELEASE_LOCK(lock);
+            PR_Unlock(lock);
             main->wake();
-            JS_ACQUIRE_LOCK(lock);
+            PR_Lock(lock);
         }
     }
 }
 
 const bool mswin =
 #ifdef XP_WIN
     true
 #else
@@ -1131,18 +1139,16 @@ Worker::processOneEvent()
         AutoLock hold1(lock);
         if (lockedCheckTermination() || events.empty())
             return;
 
         event = current = events.pop();
     }
 
     JS_SetRuntimeThread(runtime);
-    JS_SetContextThread(context);
-    JS_SetNativeStackQuota(context, gMaxStackSize);
 
     Event::Result result;
     {
         JSAutoRequest ar(context);
         result = event->process(context);
     }
 
     // Note: we have to leave the above request before calling parent->post or
@@ -1166,17 +1172,16 @@ Worker::processOneEvent()
         }
         if (!err) {
             // FIXME - out of memory, probably should panic
         }
     }
 
     if (event)
         event->destroy(context);
-    JS_ClearContextThread(context);
     JS_ClearRuntimeThread(runtime);
 
     {
         AutoLock hold2(lock);
         current = NULL;
         if (!lockedCheckTermination() && !events.empty()) {
             // Re-enqueue this worker. OOM here effectively kills the worker.
             if (!threadPool->getWorkerQueue()->post(this))
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -258,32 +258,22 @@ RegExpObject::setMultiline(bool enabled)
 inline void
 RegExpObject::setSticky(bool enabled)
 {
     setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
 }
 
 /* RegExpPrivate inlines. */
 
-inline RegExpPrivateCache *
-detail::RegExpPrivate::getOrCreateCache(JSContext *cx)
-{
-    if (RegExpPrivateCache *cache = cx->threadData()->getOrCreateRegExpPrivateCache(cx))
-        return cache;
-
-    js_ReportOutOfMemory(cx);
-    return NULL;
-}
-
 inline bool
 detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
                                    RegExpPrivateCacheKind targetKind,
                                    AlreadyIncRefed<RegExpPrivate> *result)
 {
-    RegExpPrivateCache *cache = getOrCreateCache(cx);
+    RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
     if (!cache)
         return false;
 
     if (RegExpPrivateCache::Ptr p = cache->lookup(atom)) {
         RegExpPrivateCacheValue &cacheValue = p->value;
         if (cacheValue.kind() == targetKind && cacheValue.rep()->getFlags() == flags) {
             NeedsIncRef<RegExpPrivate> cached(cacheValue.rep());
             cached->incref(cx);
@@ -302,17 +292,17 @@ detail::RegExpPrivate::cacheInsert(JSCon
 {
     JS_ASSERT(priv);
 
     /*
      * Note: allocation performed since lookup may cause a garbage collection,
      * so we have to re-lookup the cache (and inside the cache) after the
      * allocation is performed.
      */
-    RegExpPrivateCache *cache = getOrCreateCache(cx);
+    RegExpPrivateCache *cache = cx->runtime->getRegExpPrivateCache(cx);
     if (!cache)
         return false;
 
     if (RegExpPrivateCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
         /* We clobber existing entries with the same source (but different flags or kind). */
         JS_ASSERT(addPtr->value.rep()->getFlags() != priv->getFlags() ||
                   addPtr->value.kind() != kind);
         addPtr->value.reset(priv, kind);
@@ -396,30 +386,30 @@ detail::RegExpPrivateCode::compile(JSCon
     /*
      * The YARR JIT compiler attempts to compile the parsed pattern. If
      * it cannot, it informs us via |codeBlock.isFallBack()|, in which
      * case we have to bytecode compile it.
      */
 
 #ifdef JS_METHODJIT
     if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
-        JSC::ExecutableAllocator *execAlloc = cx->threadData()->getOrCreateExecutableAllocator(cx);
+        JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
         if (!execAlloc) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
         JSGlobalData globalData(execAlloc);
         jitCompile(yarrPattern, &globalData, codeBlock);
         if (!codeBlock.isFallBack())
             return true;
     }
 #endif
 
-    WTF::BumpPointerAllocator *bumpAlloc = cx->threadData()->getOrCreateBumpPointerAllocator(cx);
+    WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
     if (!bumpAlloc) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     codeBlock.setFallBack(true);
     byteCode = byteCompile(yarrPattern, bumpAlloc).get();
     return true;
@@ -505,21 +495,23 @@ detail::RegExpPrivate::decref(JSContext 
 #ifdef JS_THREADSAFE
     JS_OPT_ASSERT_IF(cx->runtime->gcHelperThread.getThread(),
                      PR_GetCurrentThread() != cx->runtime->gcHelperThread.getThread());
 #endif
 
     if (--refCount != 0)
         return;
 
-    RegExpPrivateCache *cache;
-    if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
-        RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
-        if (ptr && ptr->value.rep() == this)
-            cache->remove(ptr);
+    if (RegExpPrivateCache *cache = cx->runtime->maybeRegExpPrivateCache()) {
+        if (source->isAtom()) {
+            if (RegExpPrivateCache::Ptr p = cache->lookup(&source->asAtom())) {
+                if (p->value.rep() == this)
+                    cache->remove(p);
+            }
+        }
     }
 
 #ifdef DEBUG
     this->~RegExpPrivate();
     memset(this, 0xcd, sizeof(*this));
     cx->free_(this);
 #else
     cx->delete_(this);
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -383,17 +383,16 @@ class RegExpPrivate
 
     bool compile(JSContext *cx, TokenStream *ts);
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
 
     static RegExpPrivate *
     createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
                    TokenStream *tokenStream);
 
-    static RegExpPrivateCache *getOrCreateCache(JSContext *cx);
     static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
                             RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
     static bool cacheInsert(JSContext *cx, JSAtom *atom,
                             RegExpPrivateCacheKind kind, RegExpPrivate *priv);
 
   public:
     static AlreadyIncRefed<RegExpPrivate>
     create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -487,49 +487,25 @@ StackSpace::sizeOfCommitted()
     return (trustedEnd_ - base_) * sizeof(Value);
 #endif
 }
 
 /*****************************************************************************/
 
 ContextStack::ContextStack(JSContext *cx)
   : seg_(NULL),
-    space_(&JS_THREAD_DATA(cx)->stackSpace),
+    space_(&cx->runtime->stackSpace),
     cx_(cx)
-{
-    threadReset();
-}
+{}
 
 ContextStack::~ContextStack()
 {
     JS_ASSERT(!seg_);
 }
 
-void
-ContextStack::threadReset()
-{
-#ifdef JS_THREADSAFE
-    if (cx_->thread())
-        space_ = &JS_THREAD_DATA(cx_)->stackSpace;
-    else
-        space_ = NULL;
-#else
-    space_ = &JS_THREAD_DATA(cx_)->stackSpace;
-#endif
-}
-
-#ifdef DEBUG
-void
-ContextStack::assertSpaceInSync() const
-{
-    JS_ASSERT(space_);
-    JS_ASSERT(space_ == &JS_THREAD_DATA(cx_)->stackSpace);
-}
-#endif
-
 bool
 ContextStack::onTop() const
 {
     return seg_ && seg_ == space().seg_;
 }
 
 bool
 ContextStack::containsSlow(const StackFrame *target) const
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -312,17 +312,16 @@ public:
     ~JSCLContextHelper();
 
     void reportErrorAfterPop(char *buf);
 
     operator JSContext*() const {return mContext;}
 
 private:
     JSContext* mContext;
-    intN       mContextThread;
     nsIThreadJSContextStack* mContextStack;
     char*      mBuf;
 
     // prevent copying and assignment
     JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE;
     const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE;
 };
 
@@ -1350,33 +1349,28 @@ mozJSComponentLoader::ModuleEntry::GetFa
         return NULL;
 
     return f.forget();
 }
 
 //----------------------------------------------------------------------
 
 JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
-    : mContext(loader->mContext), mContextThread(0),
+    : mContext(loader->mContext),
       mContextStack(loader->mContextStack),
       mBuf(nsnull)
 {
     mContextStack->Push(mContext);
-    mContextThread = JS_GetContextThread(mContext);
-    if (mContextThread) {
-        JS_BeginRequest(mContext);
-    }
+    JS_BeginRequest(mContext);
 }
 
 JSCLContextHelper::~JSCLContextHelper()
 {
     if (mContextStack) {
-        if (mContextThread) {
-            JS_EndRequest(mContext);
-        }
+        JS_EndRequest(mContext);
 
         mContextStack->Pop(nsnull);
 
         JSContext* cx = nsnull;
         mContextStack->Peek(&cx);
 
         mContextStack = nsnull;
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -941,17 +941,17 @@ XPCJSRuntime::WatchdogMain(void *arg)
             sleepInterval = PR_INTERVAL_NO_TIMEOUT;
             self->mWatchdogHibernating = true;
         }
 #ifdef DEBUG
         PRStatus status =
 #endif
             PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
         JS_ASSERT(status == PR_SUCCESS);
-        js::TriggerOperationCallbacksForActiveContexts(self->mJSRuntime);
+        js::TriggerOperationCallback(self->mJSRuntime);
     }
 
     /* Wake up the main thread waiting for the watchdog to terminate. */
     PR_NotifyCondVar(self->mWatchdogWakeup);
 }
 
 //static
 void
@@ -1038,17 +1038,16 @@ void XPCJSRuntime::SystemIsBeingShutDown
 
 JSContext *
 XPCJSRuntime::GetJSCycleCollectionContext()
 {
     if (!mJSCycleCollectionContext) {
         mJSCycleCollectionContext = JS_NewContext(mJSRuntime, 0);
         if (!mJSCycleCollectionContext)
             return nsnull;
-        JS_ClearContextThread(mJSCycleCollectionContext);
     }
     return mJSCycleCollectionContext;
 }
 
 XPCJSRuntime::~XPCJSRuntime()
 {
     if (mWatchdogWakeup) {
         // If the watchdog thread is running, tell it to terminate waking it
@@ -1062,20 +1061,18 @@ XPCJSRuntime::~XPCJSRuntime()
                 PR_NotifyCondVar(mWatchdogWakeup);
                 PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
             }
         }
         PR_DestroyCondVar(mWatchdogWakeup);
         mWatchdogWakeup = nsnull;
     }
 
-    if (mJSCycleCollectionContext) {
-        JS_SetContextThread(mJSCycleCollectionContext);
+    if (mJSCycleCollectionContext)
         JS_DestroyContextNoGC(mJSCycleCollectionContext);
-    }
 
 #ifdef XPC_DUMP_AT_SHUTDOWN
     {
     // count the total JSContexts in use
     JSContext* iter = nsnull;
     int count = 0;
     while (JS_ContextIterator(mJSRuntime, &iter))
         count ++;
@@ -1634,40 +1631,40 @@ ReportJSRuntimeStats(const JS::IterateDa
                       callback, closure);
 
     ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
                       nsIMemoryReporter::KIND_HEAP, data.runtimeContexts,
                       "Memory used by JSContext objects and certain structures "
                       "hanging off them."  SLOP_BYTES_STRING,
                       callback, closure);
 
-    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/normal"),
-                      nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsNormal,
-                      "Memory used by JSThread objects and their data, "
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/normal"),
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeNormal,
+                      "Memory used by a JSRuntime, "
                       "excluding memory that is reported by "
                       "other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING,
                       callback, closure);
 
-    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/temporary"),
-                      nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsTemporary,
-                      "Memory held transiently in JSThreads and used during "
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeTemporary,
+                      "Memory held transiently in JSRuntime and used during "
                       "compilation.  It mostly holds parse nodes."
                       SLOP_BYTES_STRING,
                       callback, closure);
 
-    ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/regexp-code"),
-                       nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsRegexpCode,
+    ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
+                       nsIMemoryReporter::KIND_NONHEAP, data.runtimeRegexpCode,
                        "Memory used by the regexp JIT to hold generated code.",
                        callback, closure);
 
-    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/stack-committed"),
-                      nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsStackCommitted,
-                      "Memory used for the thread stacks.  This is the committed portions "
-                      "of the stacks; any uncommitted portions are not measured because they "
-                      "hardly cost anything.",
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
+                      nsIMemoryReporter::KIND_NONHEAP, data.runtimeStackCommitted,
+                      "Memory used for the JS call stack.  This is the committed portion "
+                      "of the stack; the uncommitted portion is not measured because it "
+                      "hardly costs anything.",
                       callback, closure);
 
     ReportGCHeapBytes(pathPrefix +
                       NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
                       &gcTotal, data.gcHeapChunkDirtyUnused,
                       "Memory on the garbage-collected JavaScript heap, within chunks with at "
                       "least one allocated GC thing, that could be holding useful data but "
                       "currently isn't.  Memory here is mutually exclusive with memory reported"
@@ -1960,19 +1957,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
                                   xpc::WrapperFactory::Rewrap,
                                   xpc::WrapperFactory::PrepareForWrapping);
         js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
 
 #ifdef MOZ_CRASHREPORTER
         JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
 #endif
         JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
-        mWatchdogWakeup = JS_NEW_CONDVAR(js::GetRuntimeGCLock(mJSRuntime));
+        mWatchdogWakeup = PR_NewCondVar(js::GetRuntimeGCLock(mJSRuntime));
         if (!mWatchdogWakeup)
-            NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
+            NS_RUNTIMEABORT("PR_NewCondVar failed.");
 
         js::SetActivityCallback(mJSRuntime, ActivityCallback, this);
 
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
         NS_RegisterMemoryMultiReporter(new XPConnectJSCompartmentsMultiReporter);
     }
--- a/js/xpconnect/src/XPCThreadContext.cpp
+++ b/js/xpconnect/src/XPCThreadContext.cpp
@@ -50,17 +50,16 @@
 
 using namespace mozilla;
 
 /***************************************************************************/
 
 XPCJSContextStack::~XPCJSContextStack()
 {
     if (mOwnSafeJSContext) {
-        JS_SetContextThread(mOwnSafeJSContext);
         JS_DestroyContext(mOwnSafeJSContext);
         mOwnSafeJSContext = nsnull;
     }
 }
 
 JSContext*
 XPCJSContextStack::Pop()
 {
@@ -104,17 +103,16 @@ GetPrincipalFromCx(JSContext *cx)
             return globalData->GetPrincipal();
     }
     return nsnull;
 }
 
 bool
 XPCJSContextStack::Push(JSContext *cx)
 {
-    MOZ_ASSERT_IF(cx, JS_GetContextThread(cx));
     if (mStack.Length() == 0) {
         mStack.AppendElement(cx);
         return true;
     }
 
     XPCJSContextInfo &e = mStack[mStack.Length() - 1];
     if (e.cx) {
         if (e.cx == cx) {
@@ -418,17 +416,17 @@ XPCPerThreadData::GetDataImpl(JSContext 
         if (PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data)) {
             NS_ERROR("PR_SetThreadPrivate failed!");
             delete data;
             return nsnull;
         }
     }
 
     if (cx && !sMainJSThread && NS_IsMainThread()) {
-        sMainJSThread = js::GetContextThread(cx);
+        sMainJSThread = js::GetOwnerThread(cx);
 
         sMainThreadData = data;
 
         sMainThreadData->mThread = PR_GetCurrentThread();
     }
 
     return data;
 }
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -63,20 +63,18 @@ bool AutoScriptEvaluate::StartEvaluating
     if (!mJSContext)
         return true;
 
     mEvaluated = true;
     if (!JS_GetErrorReporter(mJSContext)) {
         JS_SetErrorReporter(mJSContext, errorReporter);
         mErrorReporterSet = true;
     }
-    mContextHasThread = JS_GetContextThread(mJSContext);
-    if (mContextHasThread)
-        JS_BeginRequest(mJSContext);
 
+    JS_BeginRequest(mJSContext);
     if (!mEnterCompartment.enter(mJSContext, scope))
         return false;
 
     // Saving the exception state keeps us from interfering with another script
     // that may also be running on this context.  This occurred first with the
     // js debugger, as described in
     // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could
     // show up in any situation where a script calls into a wrapped js component
@@ -97,18 +95,17 @@ AutoScriptEvaluate::~AutoScriptEvaluate(
 {
     if (!mJSContext || !mEvaluated)
         return;
     if (mState)
         JS_RestoreExceptionState(mJSContext, mState);
     else
         JS_ClearPendingException(mJSContext);
 
-    if (mContextHasThread)
-        JS_EndRequest(mJSContext);
+    JS_EndRequest(mJSContext);
 
     // If this is a JSContext that has a private context that provides a
     // nsIXPCScriptNotify interface, then notify the object the script has
     // been executed.
     //
     // Note: We rely on the rule that if any JSContext in our JSRuntime has
     // private data that points to an nsISupports subclass, it has also set
     // the JSOPTION_PRIVATE_IS_NSISUPPORTS option.
@@ -538,18 +535,18 @@ GetContextFromObject(JSObject *obj)
     if (!ac.enter(ccx, obj))
         return nsnull;
     XPCWrappedNativeScope* scope =
         XPCWrappedNativeScope::FindInJSObjectScope(ccx, obj);
     XPCContext *xpcc = scope->GetContext();
 
     if (xpcc) {
         JSContext *cx = xpcc->GetJSContext();
-        if (JS_GetContextThread(cx) == JS_GetCurrentThread())
-            return cx;
+        JS_AbortIfWrongThread(JS_GetRuntime(cx));
+        return cx;
     }
 
     return nsnull;
 }
 
 class SameOriginCheckedComponent : public nsISecurityCheckedComponent
 {
 public:
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -549,24 +549,20 @@ nsXPConnect::BeginCycleCollection(nsCycl
     // It is important not to call GetSafeJSContext while on the
     // cycle-collector thread since this context will be destroyed
     // asynchronously and race with the main thread. In particular, we must
     // ensure that a context is passed to the XPCCallContext constructor.
     JSContext *cx = mRuntime->GetJSCycleCollectionContext();
     if (!cx)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    // Clear after mCycleCollectionContext is destroyed
-    JS_SetContextThread(cx);
-
     NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishTraverse?");
     mCycleCollectionContext = new XPCCallContext(NATIVE_CALLER, cx);
     if (!mCycleCollectionContext->IsValid()) {
         mCycleCollectionContext = nsnull;
-        JS_ClearContextThread(cx);
         return NS_ERROR_FAILURE;
     }
 
     static bool gcHasRun = false;
     if (!gcHasRun) {
         JSRuntime* rt = JS_GetRuntime(mCycleCollectionContext->GetJSContext());
         if (!rt)
             NS_RUNTIMEABORT("Failed to get JS runtime!");
@@ -605,21 +601,25 @@ nsXPConnect::BeginCycleCollection(nsCycl
  
     NoteWeakMapsTracer trc(mCycleCollectionContext->GetJSContext(),
                            TraceWeakMapping, cb);
     js::TraceWeakMaps(&trc);
 
     return NS_OK;
 }
 
-void
+bool
 nsXPConnect::NotifyLeaveMainThread()
 {
     NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
-    JS_ClearRuntimeThread(mRuntime->GetJSRuntime());
+    JSRuntime *rt = mRuntime->GetJSRuntime();
+    if (JS_IsInRequest(rt) || JS_IsInSuspendedRequest(rt))
+        return false;
+    JS_ClearRuntimeThread(rt);
+    return true;
 }
 
 void
 nsXPConnect::NotifyEnterCycleCollectionThread()
 {
     NS_ABORT_IF_FALSE(!NS_IsMainThread(), "On main thread");
     JS_SetRuntimeThread(mRuntime->GetJSRuntime());
 }
@@ -636,21 +636,18 @@ nsXPConnect::NotifyEnterMainThread()
 {
     NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
     JS_SetRuntimeThread(mRuntime->GetJSRuntime());
 }
 
 nsresult
 nsXPConnect::FinishTraverse()
 {
-    if (mCycleCollectionContext) {
-        JSContext *cx = mCycleCollectionContext->GetJSContext();
+    if (mCycleCollectionContext)
         mCycleCollectionContext = nsnull;
-        JS_ClearContextThread(cx);
-    }
     return NS_OK;
 }
 
 nsresult
 nsXPConnect::FinishCycleCollection()
 {
 #ifdef DEBUG_CC
     if (mJSRoots.ops) {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -534,17 +534,17 @@ 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 bool 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);
@@ -3660,19 +3660,17 @@ public:
     {
         // Do a release-mode assert that we're not doing anything significant in
         // XPConnect off the main thread. If you're an extension developer hitting
         // this, you need to change your code. See bug 716167.
         if (!NS_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
             JS_Assert("NS_IsMainThread()", __FILE__, __LINE__);
 
         if (cx) {
-            NS_ASSERTION(js::GetContextThread(cx), "Uh, JS context w/o a thread?");
-
-            if (js::GetContextThread(cx) == sMainJSThread)
+            if (js::GetOwnerThread(cx) == sMainJSThread)
                 return sMainThreadData;
         } else if (sMainThreadData && sMainThreadData->mThread == PR_GetCurrentThread()) {
             return sMainThreadData;
         }
 
         return GetDataImpl(cx);
     }
 
@@ -3765,17 +3763,17 @@ public:
     void      ClearWrappedNativeThreadsafetyReportDepth()
         {mWrappedNativeThreadsafetyReportDepth = 0;}
 #endif
 
     static void ShutDown()
         {sMainJSThread = nsnull; sMainThreadData = nsnull;}
 
     static bool IsMainThread(JSContext *cx)
-        { return js::GetContextThread(cx) == sMainJSThread; }
+        { return js::GetOwnerThread(cx) == sMainJSThread; }
 
 private:
     XPCPerThreadData();
     static XPCPerThreadData* GetDataImpl(JSContext *cx);
 
 private:
     XPCJSContextStack*   mJSContextStack;
     XPCPerThreadData*    mNextThread;
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -70,18 +70,16 @@ include $(topsrcdir)/config/rules.mk
 		test_bug679861.xul \
 		test_APIExposer.xul \
 		test_bug664689.xul \
 		test_precisegc.xul \
 		test_nodelists.xul \
 		test_getweakmapkeys.xul \
 		test_weakmaps.xul \
 		test_bug706301.xul \
-		test_ccbeginfail.xul \
-		test_ccdump.xul \
 		$(NULL)
 
 # Disabled until this test gets updated to test the new proxy based
 # wrappers.
 #		test_wrappers-2.xul \
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/js/xpconnect/tests/chrome/test_ccbeginfail.xul
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
-<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=710761
--->
-<window title="Mozilla Bug 710761"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <!-- test results are displayed in the html:body -->
-  <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=710761"
-     target="_blank">Mozilla Bug 710761</a>
-  </body>
-
-  <!-- test code goes here -->
-  <script type="application/javascript">
-  <![CDATA[
-  /** Test for Bug 710761 **/
-
-  let noCallbacks = true;
-
-  var beginFailListener = {
-    QueryInterface: function QueryInterface(aIID) {
-      if (aIID.equals(Components.interfaces.nsICycleCollectorListener) ||
-          aIID.equals(Components.interfaces.nsISupports))
-        return this;
-      throw Components.results.NS_NOINTERFACE;
-    },
-
-    /* nsICycleCollectorListener */
-    begin: function () {
-      throw Components.results.NS_ERROR_FAILURE;
-    },
-    noteRefCountedObject: function (addr, rc, descr) {
-      noCallbacks = false;
-    },
-    noteGCedObject: function (addr, marked, descr) {
-      noCallbacks = false;
-    },
-    noteEdge: function (addr, descr) {
-      noCallbacks = false;
-    },
-    beginResults: function () {
-      noCallbacks = false;
-    },
-    describeRoot: function (addr, known) {
-      noCallbacks = false;
-    },
-    describeGarbage: function (addr) {
-      noCallbacks = false;
-    },
-    end: function () {
-      noCallbacks = false;
-    },
-  };
-
-  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-        .getInterface(Components.interfaces.nsIDOMWindowUtils)
-        .cycleCollect(beginFailListener);
-
-  ok(noCallbacks, "If cycle collector listener begin fails, no further callbacks should be called.");
-
-  ]]>
-  </script>
-</window>
deleted file mode 100644
--- a/js/xpconnect/tests/chrome/test_ccdump.xul
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
-<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=709162
--->
-<window title="Mozilla Bug 709162"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <!-- test results are displayed in the html:body -->
-  <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=709162"
-     target="_blank">Mozilla Bug 709162</a>
-  </body>
-
-  <!-- test code goes here -->
-  <script type="application/javascript">
-  <![CDATA[
-  /** Test for Bug 709162 **/
-
-  var emptyListener = {
-    QueryInterface: function QueryInterface(aIID) {
-      if (aIID.equals(Components.interfaces.nsICycleCollectorListener) ||
-          aIID.equals(Components.interfaces.nsISupports))
-        return this;
-      throw Components.results.NS_NOINTERFACE;
-    },
-
-    /* nsICycleCollectorListener */
-    begin: function () {},
-    noteRefCountedObject: function (addr, rc, descr) {},
-    noteGCedObject: function (addr, marked, descr) {},
-    noteEdge: function (addr, descr) {},
-    beginResults: function () {},
-    describeRoot: function (addr, known) {},
-    describeGarbage: function (addr) {},
-    end: function () {},
-  };
-
-  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-        .getInterface(Components.interfaces.nsIDOMWindowUtils)
-        .cycleCollect(emptyListener);
-
-  ok(true, "Dump cycle collector graph without crashing.");
-
-  ]]>
-  </script>
-</window>
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3743,20 +3743,23 @@ public:
         if (!mCollector->PrepareForCollection(&whiteNodes))
             return 0;
 
         NS_ASSERTION(!mListener, "Should have cleared this already!");
         if (aListener && NS_FAILED(aListener->Begin()))
             aListener = nsnull;
         mListener = aListener;
 
-        GetJSRuntime()->NotifyLeaveMainThread();
-        mRequest.Notify();
-        mReply.Wait();
-        GetJSRuntime()->NotifyEnterMainThread();
+        if (GetJSRuntime()->NotifyLeaveMainThread()) {
+            mRequest.Notify();
+            mReply.Wait();
+            GetJSRuntime()->NotifyEnterMainThread();
+        } else {
+            mCollected = mCollector->BeginCollection(mListener);
+        }
 
         mListener = nsnull;
 
         if (mCollected) {
             mCollected = mCollector->FinishCollection(aListener);
 
             mCollector->CleanupAfterCollection();
 
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -86,18 +86,21 @@ void nsCycleCollector_shutdown();
 // 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.
+     *
+     * NotifyLeaveMainThread may return 'false' to prevent the cycle collector
+     * from leaving the main thread.
      */
-    virtual void NotifyLeaveMainThread() = 0;
+    virtual bool 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;