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
treeherdermozilla-inbound@e517d4c43143 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersigor, mccr8
bugs675078
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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)
-{
-#ifdef DEBUG
-    noGCOrAllocationCheck = 0;
-#endif
-}
-
-ThreadData::~ThreadData()
+void
+JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                               size_t *regexpCode, size_t *stackCommitted)
 {
-    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)
 {
-    JSRuntime *rt = cx->runtime;
-    ThreadData *td = JS_THREAD_DATA(cx);
+    JS_ASSERT_REQUEST_DEPTH(cx);
 
-    JS_ASSERT_REQUEST_DEPTH(cx);
-    JS_ASSERT(td->interruptFlags != 0);
+    JSRuntime *rt = cx->runtime;
+    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;
-
-    /*
-     * 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);
+typedef Vector<ScriptOpcodeCountsPair, 0, SystemAllocPolicy> ScriptOpcodeCountsVector;
 
-  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;
+
+    union {
+        jmp_buf         jmpbuf;
+        uintptr_t       words[JS_HOWMANY(sizeof(jmp_buf), sizeof(uintptr_t))];
+    } registerSnapshot;
 
-    ConservativeGCThreadData conservativeGC;
+    /*
+     * 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;
 
-#ifdef DEBUG
-    size_t              noGCOrAllocationCheck;
+    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
-
-    ThreadData(JSRuntime *rt);
-    ~ThreadData();
-
-    bool init();
-
-    void mark(JSTracer *trc) {
-        stackSpace.mark(trc);
     }
 
-    void purge(JSContext *cx) {
-        tempLifoAlloc.freeUnused();
-        gsnCache.purge();
-
-        /* FIXME: bug 506341. */
-        propertyCache.purge(cx);
-    }
+    JS_NEVER_INLINE void recordStackTop();
 
 #ifdef JS_THREADSAFE
-    void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
-                             size_t *regexpCode, size_t *stackCommitted);
+    void updateForRequestEnd(unsigned suspendCount) {
+        if (suspendCount)
+            recordStackTop();
+        else
+            nativeStackTop = NULL;
+    }
 #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;
+    bool hasStackToScan() const {
+        return !!nativeStackTop;
+    }
 };
 
 } /* 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
-
-#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
-
-#if defined(JS_USE_ONLY_NSPR_LOCKS) || !JS_HAS_NATIVE_COMPARE_AND_SWAP
-# define NSPR_LOCK 1
-#else
-# undef NSPR_LOCK
-#endif
-
-#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)      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_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
+#else  /* 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();
+# 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))
 
-#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;