Bug 807480 - Add Rooted<T> roots to PerThread state. r=luke
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Tue, 06 Nov 2012 22:32:01 -0500
changeset 120446 651dc9d52259884f7c3924334581530a85fa2dcb
parent 120445 d4f04762dc07a725f33e51435b31333c7794c7c0
child 120447 5697b0c7d3d5e2ab2099c99d7671cdfe3df783b5
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs807480
milestone19.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 807480 - Add Rooted<T> roots to PerThread state. r=luke Currently there are a number of global fields in JSRuntime* which are basically tracking per-thread state. This makes sense on the current trunk since there is only ever a single thread associated with a runtime, but as Parallel JS (nee Rivertrail) starts to land this assumption no longer holds. This patch makes a struct, currently called |JS::PerThreadData|, that stores per-thread data from the runtime. There is one instance of this struct embedded in "Runtime" itself (the field |mainThread|). For now I have only migrated the debug GC fields into |PerThread|, those are the ones causing me immediate pain. Eventually more fields will want to move into there. The eventual goal is to distinguish thread-safe code, which will take as argument a |JS::PerThread*|, from non-thread-safe code, which will take a |JSRuntime*| or |JSContext*|.
js/src/gc/Root.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jspubtd.h
js/src/shell/js.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -514,37 +514,33 @@ class RootedBase {};
  */
 template <typename T>
 class Rooted : public RootedBase<T>
 {
     void init(JSContext *cxArg)
     {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         ContextFriendFields *cx = ContextFriendFields::get(cxArg);
-
-        ThingRootKind kind = RootMethods<T>::kind();
-        this->stack = reinterpret_cast<Rooted<T>**>(&cx->thingGCRooters[kind]);
-        this->prev = *stack;
-        *stack = this;
-
-        JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+        commonInit(cx->thingGCRooters);
 #endif
     }
 
     void init(JSRuntime *rtArg)
     {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
-        RuntimeFriendFields *rt = const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg));
+        PerThreadDataFriendFields *pt = PerThreadDataFriendFields::getMainThread(rtArg);
+        commonInit(pt->thingGCRooters);
+#endif
+    }
 
-        ThingRootKind kind = RootMethods<T>::kind();
-        this->stack = reinterpret_cast<Rooted<T>**>(&rt->thingGCRooters[kind]);
-        this->prev = *stack;
-        *stack = this;
-
-        JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+    void init(js::PerThreadData *ptArg)
+    {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+        PerThreadDataFriendFields *pt = PerThreadDataFriendFields::get(ptArg);
+        commonInit(pt->thingGCRooters);
 #endif
     }
 
   public:
     Rooted(JSRuntime *rt
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(RootMethods<T>::initial())
     {
@@ -571,25 +567,50 @@ class Rooted : public RootedBase<T>
     Rooted(JSContext *cx, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
+    Rooted(js::PerThreadData *pt
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(RootMethods<T>::initial())
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
+    Rooted(js::PerThreadData *pt, T initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
     template <typename S>
     Rooted(JSContext *cx, const Return<S> &initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial.ptr_)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
+    template <typename S>
+    Rooted(js::PerThreadData *pt, const Return<S> &initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial.ptr_)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(pt);
+    }
+
     ~Rooted()
     {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         JS_ASSERT(*stack == this);
         *stack = prev;
 #endif
     }
 
@@ -620,16 +641,27 @@ class Rooted : public RootedBase<T>
     template <typename S>
     T & operator =(const Return<S> &value)
     {
         ptr = value.ptr_;
         return ptr;
     }
 
   private:
+    void commonInit(Rooted<void*> **thingGCRooters) {
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+        ThingRootKind kind = RootMethods<T>::kind();
+        this->stack = reinterpret_cast<Rooted<T>**>(&thingGCRooters[kind]);
+        this->prev = *stack;
+        *stack = this;
+
+        JS_ASSERT(!RootMethods<T>::poisoned(ptr));
+#endif
+    }
+
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
     Rooted<T> **stack, *prev;
 #endif
     T ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     Rooted(const Rooted &) MOZ_DELETE;
 };
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -671,57 +671,69 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * Has a new runtime ever been created?  This flag is used to control things
  * that should happen only once across all runtimes.
  */
 static JSBool js_NewRuntimeWasCalled = JS_FALSE;
 
 /*
  * Thread Local Storage slot for storing the runtime for a thread.
  */
+namespace js {
+mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
+}
+
 namespace JS {
-mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
 
 #ifdef DEBUG
 JS_FRIEND_API(void)
 EnterAssertNoGCScope()
 {
-    ++TlsRuntime.get()->gcAssertNoGCDepth;
+    ++TlsPerThreadData.get()->gcAssertNoGCDepth;
 }
 
 JS_FRIEND_API(void)
 LeaveAssertNoGCScope()
 {
-    --TlsRuntime.get()->gcAssertNoGCDepth;
-    JS_ASSERT(TlsRuntime.get()->gcAssertNoGCDepth >= 0);
+    --TlsPerThreadData.get()->gcAssertNoGCDepth;
+    JS_ASSERT(TlsPerThreadData.get()->gcAssertNoGCDepth >= 0);
 }
 
 JS_FRIEND_API(bool)
 InNoGCScope()
 {
-    return TlsRuntime.get()->gcAssertNoGCDepth > 0;
+    return TlsPerThreadData.get()->gcAssertNoGCDepth > 0;
 }
 
 JS_FRIEND_API(bool)
 NeedRelaxedRootChecks()
 {
-    return TlsRuntime.get()->gcRelaxRootChecks;
+    return TlsPerThreadData.get()->gcRelaxRootChecks;
 }
 #else
 JS_FRIEND_API(void) EnterAssertNoGCScope() {}
 JS_FRIEND_API(void) LeaveAssertNoGCScope() {}
 JS_FRIEND_API(bool) InNoGCScope() { return false; }
 JS_FRIEND_API(bool) NeedRelaxedRootChecks() { return false; }
 #endif
 
 } /* namespace JS */
 
 static const JSSecurityCallbacks NullSecurityCallbacks = { };
 
+js::PerThreadData::PerThreadData(JSRuntime *runtime)
+  : runtime_(runtime)
+#ifdef DEBUG
+  , gcRelaxRootChecks(false)
+  , gcAssertNoGCDepth(0)
+#endif
+{}
+
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
-  : atomsCompartment(NULL),
+  : mainThread(this),
+    atomsCompartment(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
 #ifdef JS_METHODJIT
@@ -783,20 +795,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     gcSweepPhase(0),
     gcSweepCompartmentIndex(0),
     gcSweepKindIndex(0),
     gcArenasAllocatedDuringSweep(NULL),
     gcInterFrameGC(0),
     gcSliceBudget(SliceBudget::Unlimited),
     gcIncrementalEnabled(true),
     gcExactScanningEnabled(true),
-#ifdef DEBUG
-    gcRelaxRootChecks(false),
-    gcAssertNoGCDepth(0),
-#endif
     gcPoke(false),
     heapState(Idle),
 #ifdef JS_GC_ZEAL
     gcZeal_(0),
     gcZealFrequency(0),
     gcNextScheduled(0),
     gcDeterministicOnly(false),
     gcIncrementalLimit(0),
@@ -880,26 +888,22 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
 
 bool
 JSRuntime::init(uint32_t maxbytes)
 {
 #ifdef JS_THREADSAFE
     ownerThread_ = PR_GetCurrentThread();
 #endif
 
-    JS::TlsRuntime.set(this);
+    js::TlsPerThreadData.set(&mainThread);
 
 #ifdef JS_METHODJIT_SPEW
     JMCheckLogging();
 #endif
 
-#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
-    PodArrayZero(thingGCRooters);
-#endif
-
     if (!js_InitGC(this, maxbytes))
         return false;
 
     if (!gcMarker.init())
         return false;
 
     const char *size = getenv("JSGC_MARK_STACK_LIMIT");
     if (size)
@@ -1013,54 +1017,54 @@ JSRuntime::~JSRuntime()
 
 #ifdef JS_THREADSAFE
 void
 JSRuntime::setOwnerThread()
 {
     JS_ASSERT(ownerThread_ == (void *)0xc1ea12);  /* "clear" */
     JS_ASSERT(requestDepth == 0);
     JS_ASSERT(js_NewRuntimeWasCalled);
-    JS_ASSERT(JS::TlsRuntime.get() == NULL);
+    JS_ASSERT(js::TlsPerThreadData.get() == NULL);
     ownerThread_ = PR_GetCurrentThread();
-    JS::TlsRuntime.set(this);
+    js::TlsPerThreadData.set(&mainThread);
     nativeStackBase = GetNativeStackBase();
     if (nativeStackQuota)
         JS_SetNativeStackQuota(this, nativeStackQuota);
 }
 
 void
 JSRuntime::clearOwnerThread()
 {
     assertValidThread();
     JS_ASSERT(requestDepth == 0);
     JS_ASSERT(js_NewRuntimeWasCalled);
     ownerThread_ = (void *)0xc1ea12;  /* "clear" */
-    JS::TlsRuntime.set(NULL);
+    js::TlsPerThreadData.set(NULL);
     nativeStackBase = 0;
 #if JS_STACK_GROWTH_DIRECTION > 0
     nativeStackLimit = UINTPTR_MAX;
 #else
     nativeStackLimit = 0;
 #endif
 }
 
 JS_FRIEND_API(void)
 JSRuntime::abortIfWrongThread() const
 {
     if (ownerThread_ != PR_GetCurrentThread())
         MOZ_CRASH();
-    if (this != JS::TlsRuntime.get())
+    if (!js::TlsPerThreadData.get()->associatedWith(this))
         MOZ_CRASH();
 }
 
 JS_FRIEND_API(void)
 JSRuntime::assertValidThread() const
 {
     JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
-    JS_ASSERT(this == JS::TlsRuntime.get());
+    JS_ASSERT(js::TlsPerThreadData.get()->associatedWith(this));
 }
 #endif  /* JS_THREADSAFE */
 
 JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
 {
     if (!js_NewRuntimeWasCalled) {
 #ifdef DEBUG
@@ -1087,17 +1091,17 @@ JS_NewRuntime(uint32_t maxbytes, JSUseHe
         JS_ASSERT(count == numfmtspecs);                                      \
     JS_END_MACRO;
 #include "js.msg"
 #undef MSG_DEF
 #endif /* DEBUG */
 
         InitMemorySubsystem();
 
-        if (!JS::TlsRuntime.init())
+        if (!js::TlsPerThreadData.init())
             return NULL;
 
         js_NewRuntimeWasCalled = JS_TRUE;
     }
 
     JSRuntime *rt = js_new<JSRuntime>(useHelperThreads);
     if (!rt)
         return NULL;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2884,18 +2884,16 @@ JS_BeginRequest(JSContext *cx);
 extern JS_PUBLIC_API(void)
 JS_EndRequest(JSContext *cx);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsInRequest(JSRuntime *rt);
 
 namespace JS {
 
-extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
-
 inline bool
 IsPoisonedId(jsid iden)
 {
     if (JSID_IS_STRING(iden))
         return JS::IsPoisonedPtr(JSID_TO_STRING(iden));
     if (JSID_IS_OBJECT(iden))
         return JS::IsPoisonedPtr(JSID_TO_OBJECT(iden));
     return false;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -384,18 +384,80 @@ struct JSAtomState
 #define PROPERTYNAME_FIELD(name, code, init) js::FixedHeapPtr<js::PropertyName> name;
     JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
 };
 
 #define NAME_OFFSET(name)       offsetof(JSAtomState, name)
 #define OFFSET_TO_NAME(rt,off)  (*(js::FixedHeapPtr<js::PropertyName>*)((char*)&(rt)->atomState + (off)))
 
+namespace js {
+
+/*
+ * Encapsulates portions of the runtime/context that are tied to a
+ * single active thread.  Normally, as most JS is single-threaded,
+ * there is only one instance of this struct, embedded in the
+ * JSRuntime as the field |mainThread|.  During Parallel JS sections,
+ * however, there will be one instance per worker thread.
+ *
+ * The eventual plan is to designate thread-safe portions of the
+ * interpreter and runtime by having them take |PerThreadData*|
+ * arguments instead of |JSContext*| or |JSRuntime*|.
+ */
+class PerThreadData : public js::PerThreadDataFriendFields
+{
+    /*
+     * Backpointer to the full shared JSRuntime* with which this
+     * thread is associaed.  This is private because accessing the
+     * fields of this runtime can provoke race conditions, so the
+     * intention is that access will be mediated through safe
+     * functions like |associatedWith()| below.
+     */
+    JSRuntime *runtime_;
+
+  public:
+    /*
+     * We save all conservative scanned roots in this vector so that
+     * conservative scanning can be "replayed" deterministically. In DEBUG mode,
+     * this allows us to run a non-incremental GC after every incremental GC to
+     * ensure that no objects were missed.
+     */
+#ifdef DEBUG
+    struct SavedGCRoot {
+        void *thing;
+        JSGCTraceKind kind;
+
+        SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
+    };
+    js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
+
+    bool                gcRelaxRootChecks;
+    int                 gcAssertNoGCDepth;
+#endif
+
+    PerThreadData(JSRuntime *runtime);
+
+    bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
+};
+
+} // namespace js
+
 struct JSRuntime : js::RuntimeFriendFields
 {
+    /* Per-thread data for the main thread that is associated with
+     * this JSRuntime, as opposed to any worker threads used in
+     * parallel sections.  See definition of |PerThreadData| struct
+     * above for more details.
+     *
+     * NB: This field is statically asserted to be at offset
+     * sizeof(RuntimeFriendFields). See
+     * PerThreadDataFriendFields::getMainThread.
+     */
+    js::PerThreadData mainThread;
+
     /* 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
@@ -649,35 +711,16 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     /*
      * Whether exact stack scanning is enabled for this runtime. This is
      * currently only used for dynamic root analysis. Exact scanning starts out
      * enabled, and is disabled if e4x has been used.
      */
     bool                gcExactScanningEnabled;
 
-    /*
-     * We save all conservative scanned roots in this vector so that
-     * conservative scanning can be "replayed" deterministically. In DEBUG mode,
-     * this allows us to run a non-incremental GC after every incremental GC to
-     * ensure that no objects were missed.
-     */
-#ifdef DEBUG
-    struct SavedGCRoot {
-        void *thing;
-        JSGCTraceKind kind;
-
-        SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
-    };
-    js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
-
-    bool                gcRelaxRootChecks;
-    int                 gcAssertNoGCDepth;
-#endif
-
     bool                gcPoke;
 
     enum HeapState {
         Idle,       // doing nothing with the GC heap
         Tracing,    // tracing the GC heap without collecting, e.g. IterateCompartments()
         Collecting  // doing a GC of the heap
     };
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -17,16 +17,26 @@
 
 #include "builtin/TestingFunctions.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
+// Required by PerThreadDataFriendFields::getMainThread()
+JS_STATIC_ASSERT(offsetof(JSRuntime, mainThread) == sizeof(RuntimeFriendFields));
+
+PerThreadDataFriendFields::PerThreadDataFriendFields()
+{
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+    PodArrayZero(thingGCRooters);
+#endif
+}
+
 JS_FRIEND_API(void)
 JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook)
 {
     rt->sourceHook = hook;
 }
 
 JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -178,16 +178,18 @@ JS_DefineFunctionsWithHelp(JSContext *cx
 
 typedef bool (* JS_SourceHook)(JSContext *cx, JSScript *script, jschar **src, uint32_t *length);
 
 extern JS_FRIEND_API(void)
 JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
 
 namespace js {
 
+extern mozilla::ThreadLocal<PerThreadData *> TlsPerThreadData;
+
 inline JSRuntime *
 GetRuntime(const JSContext *cx)
 {
     return ContextFriendFields::get(cx)->runtime;
 }
 
 typedef bool
 (* PreserveWrapperCallback)(JSContext *cx, JSObject *obj);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -987,17 +987,17 @@ MarkExactStackRooters(JSTracer *trc, Roo
 
 static void
 MarkExactStackRoots(JSTracer *trc)
 {
     for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
         for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
             MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
         }
-        MarkExactStackRooters(trc, rt->thingGCRooters[i], ThingRootKind(i));
+        MarkExactStackRooters(trc, rt->mainThread.thingGCRooters[i], ThingRootKind(i));
     }
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
@@ -1120,17 +1120,18 @@ MarkIfGCThingWord(JSTracer *trc, uintptr
 #endif
     JS_SET_TRACING_LOCATION(trc, (void *)w);
     void *tmp = thing;
     MarkKind(trc, &tmp, traceKind);
     JS_ASSERT(tmp == thing);
 
 #ifdef DEBUG
     if (trc->runtime->gcIncrementalState == MARK_ROOTS)
-        trc->runtime->gcSavedRoots.append(JSRuntime::SavedGCRoot(thing, traceKind));
+        trc->runtime->mainThread.gcSavedRoots.append(
+            PerThreadData::SavedGCRoot(thing, traceKind));
 #endif
 
     return CGCT_VALID;
 }
 
 static void
 MarkWordConservatively(JSTracer *trc, uintptr_t w)
 {
@@ -1181,28 +1182,28 @@ MarkRangeConservativelyAndSkipIon(JSTrac
 
 static JS_NEVER_INLINE void
 MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
 {
     JSRuntime *rt = trc->runtime;
 
 #ifdef DEBUG
     if (useSavedRoots) {
-        for (JSRuntime::SavedGCRoot *root = rt->gcSavedRoots.begin();
-             root != rt->gcSavedRoots.end();
+        for (PerThreadData::SavedGCRoot *root = rt->mainThread.gcSavedRoots.begin();
+             root != rt->mainThread.gcSavedRoots.end();
              root++)
         {
             JS_SET_TRACING_NAME(trc, "cstack");
             MarkKind(trc, &root->thing, root->kind);
         }
         return;
     }
 
     if (rt->gcIncrementalState == MARK_ROOTS)
-        rt->gcSavedRoots.clearAndFree();
+        rt->mainThread.gcSavedRoots.clearAndFree();
 #endif
 
     ConservativeGCData *cgcd = &rt->conservativeGC;
     if (!cgcd->hasStackToScan()) {
 #ifdef JS_THREADSAFE
         JS_ASSERT(!rt->requestDepth);
 #endif
         return;
@@ -4907,17 +4908,18 @@ CheckStackRoot(JSTracer *trc, uintptr_t 
 #endif
 
     ConservativeGCTest test = MarkIfGCThingWord(trc, *w);
 
     if (test == CGCT_VALID) {
         bool matched = false;
         JSRuntime *rt = trc->runtime;
         for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-            CheckStackRootThings(w, rt->thingGCRooters[i], ThingRootKind(i), &matched);
+            CheckStackRootThings(w, rt->mainThread.thingGCRooters[i],
+                                 ThingRootKind(i), &matched);
             for (ContextIter cx(rt); !cx.done(); cx.next()) {
                 CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched);
                 SkipRoot *skip = cx->skipGCRooters;
                 while (skip) {
                     if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
                         matched = true;
                     skip = skip->previous();
                 }
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -302,30 +302,48 @@ struct RuntimeFriendFields {
      * If non-zero, we were been asked to call the operation callback as soon
      * as possible.
      */
     volatile int32_t    interrupt;
 
     /* Limit pointer for checking native stack consumption. */
     uintptr_t           nativeStackLimit;
 
-#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
-    /*
-     * Stack allocated GC roots for stack GC heap pointers, which may be
-     * overwritten if moved during a GC.
-     */
-    Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
-#endif
-
     RuntimeFriendFields()
       : interrupt(0),
         nativeStackLimit(0) { }
 
     static const RuntimeFriendFields *get(const JSRuntime *rt) {
         return reinterpret_cast<const RuntimeFriendFields *>(rt);
     }
 };
 
+class PerThreadData;
+
+struct PerThreadDataFriendFields
+{
+    PerThreadDataFriendFields();
+
+#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
+    /*
+     * Stack allocated GC roots for stack GC heap pointers, which may be
+     * overwritten if moved during a GC.
+     */
+    Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
+#endif
+
+    static PerThreadDataFriendFields *get(js::PerThreadData *pt) {
+        return reinterpret_cast<PerThreadDataFriendFields *>(pt);
+    }
+
+    static PerThreadDataFriendFields *getMainThread(JSRuntime *rt) {
+        // mainThread must always appear directly after |RuntimeFriendFields|.
+        // Tested by a JS_STATIC_ASSERT in |jsfriendapi.cpp|
+        return reinterpret_cast<PerThreadDataFriendFields *>(
+            reinterpret_cast<char*>(rt) + sizeof(RuntimeFriendFields));
+    }
+};
+
 } /* namespace js */
 
 #endif /* __cplusplus */
 
 #endif /* jspubtd_h___ */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3412,17 +3412,17 @@ RelaxRootChecks(JSContext *cx, unsigned 
 {
     if (argc > 0) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS,
                              "relaxRootChecks");
         return false;
     }
 
 #ifdef DEBUG
-    cx->runtime->gcRelaxRootChecks = true;
+    cx->runtime->mainThread.gcRelaxRootChecks = true;
 #endif
 
     return true;
 }
 
 static JSBool
 GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp)
 {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1397,17 +1397,18 @@ StackIter::settleOnNewState()
         }
 
         /* Pop the call and keep looking. */
         popCall();
     }
 }
 
 StackIter::StackIter(JSContext *cx, SavedOption savedOption)
-  : maybecx_(cx),
+  : perThread_(&cx->runtime->mainThread),
+    maybecx_(cx),
     savedOption_(savedOption),
     script_(cx, NULL)
 #ifdef JS_ION
     , ionActivations_(cx),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
@@ -1421,17 +1422,19 @@ StackIter::StackIter(JSContext *cx, Save
         startOnSegment(seg);
         settleOnNewState();
     } else {
         state_ = DONE;
     }
 }
 
 StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
-  : maybecx_(NULL), savedOption_(STOP_AT_SAVED),
+  : perThread_(&rt->mainThread),
+    maybecx_(NULL),
+    savedOption_(STOP_AT_SAVED),
     script_(rt, NULL)
 #ifdef JS_ION
     , ionActivations_(rt),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
@@ -1439,24 +1442,25 @@ StackIter::StackIter(JSRuntime *rt, Stac
     for (size_t i = 0; i < v.length(); i++)
         mjit::ExpandInlineFrames(v[i]);
 #endif
     startOnSegment(&seg);
     settleOnNewState();
 }
 
 StackIter::StackIter(const StackIter &other)
-  : maybecx_(other.maybecx_),
+  : perThread_(other.perThread_),
+    maybecx_(other.maybecx_),
     savedOption_(other.savedOption_),
     state_(other.state_),
     fp_(other.fp_),
     calls_(other.calls_),
     seg_(other.seg_),
     pc_(other.pc_),
-    script_(other.maybecx_ ? other.maybecx_->runtime : TlsRuntime.get(), other.script_),
+    script_(perThread_, other.script_),
     args_(other.args_)
 #ifdef JS_ION
     , ionActivations_(other.ionActivations_),
     ionFrames_(other.ionFrames_),
     ionInlineFrames_(other.ionInlineFrames_)
 #endif
 {
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1707,16 +1707,17 @@ class GeneratorFrameGuard : public Frame
  *   }
  *
  * The SavedOption parameter additionally lets the iterator continue through
  * breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
  */
 class StackIter
 {
     friend class ContextStack;
+    PerThreadData *perThread_;
     JSContext    *maybecx_;
   public:
     enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
   private:
     SavedOption  savedOption_;
 
     enum State { DONE, SCRIPTED, NATIVE, ION };