Bug 1452982 part 1 - Use rt->mainContextFromOwnThread() instead of TlsContext.get() in some places. r=jonco
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 12 Apr 2018 13:04:13 +0200
changeset 466528 473b7d381544313a38af2be2898e4bd92375b45d
parent 466527 b02cae8c720e401ff68364e9b171e6981ff1b5c6
child 466529 ae45e56c3c71ae23084e9ef549c3d58880add8a1
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1452982
milestone61.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 1452982 part 1 - Use rt->mainContextFromOwnThread() instead of TlsContext.get() in some places. r=jonco
js/public/RootingAPI.h
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/Statistics.cpp
js/src/gc/Verifier.cpp
js/src/gc/Zone.cpp
js/src/irregexp/RegExpStack.cpp
js/src/jit/JitFrames.cpp
js/src/jit/JitcodeMap.cpp
js/src/jsapi.cpp
js/src/vm/Runtime.cpp
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -191,19 +191,16 @@ struct PersistentRootedMarker;
 
 } /* namespace js */
 
 namespace JS {
 
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
-/* This is exposing internal state of the GC for inlining purposes. */
-JS_FRIEND_API(bool) isGCEnabled();
-
 JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
 JS_FRIEND_API(void) HeapStringPostBarrier(JSString** objp, JSString* prev, JSString* next);
 
 #ifdef JS_DEBUG
 /**
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1693,32 +1693,32 @@ GCRuntime::setGCCallback(JSGCCallback ca
     gcCallback.op = callback;
     gcCallback.data = data;
 }
 
 void
 GCRuntime::callGCCallback(JSGCStatus status) const
 {
     MOZ_ASSERT(gcCallback.op);
-    gcCallback.op(TlsContext.get(), status, gcCallback.data);
+    gcCallback.op(rt->mainContextFromOwnThread(), status, gcCallback.data);
 }
 
 void
 GCRuntime::setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
                                      void* data)
 {
     tenuredCallback.op = callback;
     tenuredCallback.data = data;
 }
 
 void
 GCRuntime::callObjectsTenuredCallback()
 {
     if (tenuredCallback.op)
-        tenuredCallback.op(TlsContext.get(), tenuredCallback.data);
+        tenuredCallback.op(rt->mainContextFromOwnThread(), tenuredCallback.data);
 }
 
 bool
 GCRuntime::addFinalizeCallback(JSFinalizeCallback callback, void* data)
 {
     return finalizeCallbacks.ref().append(Callback<JSFinalizeCallback>(callback, data));
 }
 
@@ -1758,18 +1758,19 @@ GCRuntime::removeWeakPointerZonesCallbac
             break;
         }
     }
 }
 
 void
 GCRuntime::callWeakPointerZonesCallbacks() const
 {
+    JSContext* cx = rt->mainContextFromOwnThread();
     for (auto const& p : updateWeakPointerZonesCallbacks.ref())
-        p.op(TlsContext.get(), p.data);
+        p.op(cx, p.data);
 }
 
 bool
 GCRuntime::addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data)
 {
     return updateWeakPointerCompartmentCallbacks.ref().append(
             Callback<JSWeakPointerCompartmentCallback>(callback, data));
 }
@@ -1783,18 +1784,19 @@ GCRuntime::removeWeakPointerCompartmentC
             break;
         }
     }
 }
 
 void
 GCRuntime::callWeakPointerCompartmentCallbacks(JSCompartment* comp) const
 {
+    JSContext* cx = rt->mainContextFromOwnThread();
     for (auto const& p : updateWeakPointerCompartmentCallbacks.ref())
-        p.op(TlsContext.get(), comp, p.data);
+        p.op(cx, comp, p.data);
 }
 
 JS::GCSliceCallback
 GCRuntime::setSliceCallback(JS::GCSliceCallback callback) {
     return stats().setSliceCallback(callback);
 }
 
 JS::GCNurseryCollectionCallback
@@ -2058,17 +2060,17 @@ GCRuntime::shouldCompact()
     // GCs if we are currently animating.
     return invocationKind == GC_SHRINK && isCompactingGCEnabled() &&
         (!isIncremental || rt->lastAnimationTime + PRMJ_USEC_PER_SEC < PRMJ_Now());
 }
 
 bool
 GCRuntime::isCompactingGCEnabled() const
 {
-    return compactingEnabled && TlsContext.get()->compactingDisabledCount == 0;
+    return compactingEnabled && rt->mainContextFromOwnThread()->compactingDisabledCount == 0;
 }
 
 AutoDisableCompactingGC::AutoDisableCompactingGC(JSContext* cx)
   : cx(cx)
 {
     ++cx->compactingDisabledCount;
     if (cx->runtime()->gc.isIncrementalGCInProgress() && cx->runtime()->gc.isCompactingGc())
         FinishGC(cx);
@@ -3208,32 +3210,32 @@ GCRuntime::requestMajorGC(JS::gcreason::
     if (majorGCRequested())
         return;
 
     majorGCTriggerReason = reason;
 
     // There's no need to use RequestInterruptUrgent here. It's slower because
     // it has to interrupt (looping) Ion code, but loops in Ion code that
     // affect GC will have an explicit interrupt check.
-    TlsContext.get()->requestInterrupt(JSContext::RequestInterruptCanWait);
+    rt->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
 }
 
 void
 Nursery::requestMinorGC(JS::gcreason::Reason reason) const
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
     MOZ_ASSERT(!CurrentThreadIsPerformingGC());
 
     if (minorGCRequested())
         return;
 
     minorGCTriggerReason_ = reason;
 
     // See comment in requestMajorGC.
-    TlsContext.get()->requestInterrupt(JSContext::RequestInterruptCanWait);
+    runtime()->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /*
      * Don't trigger GCs if this is being called off the active thread from
      * onTooMuchMalloc().
@@ -3313,17 +3315,17 @@ GCRuntime::triggerZoneGC(Zone* zone, JS:
     if (hasZealMode(ZealMode::Alloc)) {
         MOZ_RELEASE_ASSERT(triggerGC(reason));
         return true;
     }
 #endif
 
     if (zone->isAtomsZone()) {
         /* We can't do a zone GC of the atoms compartment. */
-        if (TlsContext.get()->keepAtoms || rt->hasHelperThreadZones()) {
+        if (rt->mainContextFromOwnThread()->keepAtoms || rt->hasHelperThreadZones()) {
             /* Skip GC and retrigger later, since atoms zone won't be collected
              * if keepAtoms is true. */
             fullGCForAtomsRequested_ = true;
             return false;
         }
         stats().recordTrigger(used, threshold);
         MOZ_RELEASE_ASSERT(triggerGC(reason));
         return true;
@@ -3735,17 +3737,17 @@ void
 JSCompartment::destroy(FreeOp* fop)
 {
     JSRuntime* rt = fop->runtime();
     if (auto callback = rt->destroyRealmCallback)
         callback(fop, JS::GetRealmForCompartment(this));
     if (auto callback = rt->destroyCompartmentCallback)
         callback(fop, this);
     if (principals())
-        JS_DropPrincipals(TlsContext.get(), principals());
+        JS_DropPrincipals(rt->mainContextFromOwnThread(), principals());
     fop->delete_(this);
     rt->gc.stats().sweptCompartment();
 }
 
 void
 Zone::destroy(FreeOp* fop)
 {
     MOZ_ASSERT(compartments().empty());
@@ -4072,21 +4074,22 @@ CompartmentCheckTracer::onChild(const JS
         Zone* thingZone = tenured->zoneFromAnyThread();
         MOZ_ASSERT(thingZone == zone || thingZone->isAtomsZone());
     }
 }
 
 void
 GCRuntime::checkForCompartmentMismatches()
 {
-    if (TlsContext.get()->disableStrictProxyCheckingCount)
+    JSContext* cx = rt->mainContextFromOwnThread();
+    if (cx->disableStrictProxyCheckingCount)
         return;
 
     CompartmentCheckTracer trc(rt);
-    AutoAssertEmptyNursery empty(TlsContext.get());
+    AutoAssertEmptyNursery empty(cx);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (auto thingKind : AllAllocKinds()) {
             for (auto i = zone->cellIter<TenuredCell>(thingKind, empty); !i.done(); i.next()) {
                 trc.src = i.getCell();
                 trc.srcKind = MapAllocToTraceKind(thingKind);
                 trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
                                                          trc.src, trc.srcKind);
@@ -4098,19 +4101,19 @@ GCRuntime::checkForCompartmentMismatches
 #endif
 
 static void
 RelazifyFunctions(Zone* zone, AllocKind kind)
 {
     MOZ_ASSERT(kind == AllocKind::FUNCTION ||
                kind == AllocKind::FUNCTION_EXTENDED);
 
-    AutoAssertEmptyNursery empty(TlsContext.get());
-
     JSRuntime* rt = zone->runtimeFromActiveCooperatingThread();
+    AutoAssertEmptyNursery empty(rt->mainContextFromOwnThread());
+
     for (auto i = zone->cellIter<JSObject>(kind, empty); !i.done(); i.next()) {
         JSFunction* fun = &i->as<JSFunction>();
         if (fun->hasScript())
             fun->maybeRelazify(rt);
     }
 }
 
 static bool
@@ -4192,17 +4195,17 @@ GCRuntime::prepareZonesForCollection(JS:
         c->marked = false;
         c->scheduledForDestruction = false;
         c->maybeAlive = c->shouldTraceGlobal() || !c->zone()->isGCScheduled();
         if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode))
             c->zone()->setPreservingCode(true);
     }
 
     if (!cleanUpEverything && canAllocateMoreCode) {
-        jit::JitActivationIterator activation(TlsContext.get());
+        jit::JitActivationIterator activation(rt->mainContextFromOwnThread());
         if (!activation.done())
             activation->compartment()->zone()->setPreservingCode(true);
     }
 
     /*
      * Check that we do collect the atoms zone if we triggered a GC for that
      * purpose.
      */
@@ -4941,17 +4944,17 @@ GCRuntime::findInterZoneEdges()
 void
 GCRuntime::groupZonesForSweeping(JS::gcreason::Reason reason)
 {
 #ifdef DEBUG
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         MOZ_ASSERT(zone->gcSweepGroupEdges().empty());
 #endif
 
-    JSContext* cx = TlsContext.get();
+    JSContext* cx = rt->mainContextFromOwnThread();
     Zone* maybeAtomsZone = atomsZone->wasGCStarted() ? atomsZone.ref() : nullptr;
     ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode], maybeAtomsZone);
     if (!isIncremental || !findInterZoneEdges())
         finder.useOneComponent();
 
 #ifdef JS_GC_ZEAL
     // Use one component for IncrementalSweepThenFinish zeal mode.
     if (isIncremental && reason == JS::gcreason::DEBUG_GC &&
@@ -6592,17 +6595,17 @@ GCRuntime::compactPhase(JS::gcreason::Re
     MOZ_ASSERT(startedCompacting);
 
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::COMPACT);
 
     // TODO: JSScripts can move. If the sampler interrupts the GC in the
     // middle of relocating an arena, invalid JSScript pointers may be
     // accessed. Suppress all sampling until a finer-grained solution can be
     // found. See bug 1295775.
-    AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
+    AutoSuppressProfilerSampling suppressSampling(rt->mainContextFromOwnThread());
 
     ZoneList relocatedZones;
     Arena* relocatedArenas = nullptr;
     while (!zonesToMaybeCompact.ref().isEmpty()) {
 
         Zone* zone = zonesToMaybeCompact.ref().front();
         zonesToMaybeCompact.ref().removeFront();
 
@@ -6706,55 +6709,42 @@ AllNurseriesAreEmpty(JSRuntime* rt)
     }
     return true;
 }
 #endif
 
 /* Start a new heap session. */
 AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
   : runtime(rt),
-    prevState(TlsContext.get()->heapState),
-    pseudoFrame(TlsContext.get(), HeapStateToLabel(heapState), ProfileEntry::Category::GC)
+    prevState(rt->mainContextFromOwnThread()->heapState),
+    pseudoFrame(rt->mainContextFromOwnThread(), HeapStateToLabel(heapState),
+                ProfileEntry::Category::GC)
 {
     MOZ_ASSERT(prevState == JS::HeapState::Idle);
     MOZ_ASSERT(heapState != JS::HeapState::Idle);
     MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, AllNurseriesAreEmpty(rt));
 
     // Session always begins with lock held, see comment in class definition.
     maybeLock.emplace(rt);
 
-    TlsContext.get()->heapState = heapState;
+    rt->mainContextFromOwnThread()->heapState = heapState;
 }
 
 AutoTraceSession::~AutoTraceSession()
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapBusy());
-    TlsContext.get()->heapState = prevState;
+    runtime->mainContextFromOwnThread()->heapState = prevState;
 }
 
 JS_PUBLIC_API(JS::HeapState)
 JS::CurrentThreadHeapState()
 {
     return TlsContext.get()->heapState;
 }
 
-bool
-GCRuntime::canChangeActiveContext(JSContext* cx)
-{
-    // Threads cannot be in the middle of any operation that affects GC
-    // behavior when execution transfers to another thread for cooperative
-    // scheduling.
-    return cx->heapState == JS::HeapState::Idle
-        && !cx->suppressGC
-        && !cx->inUnsafeRegion
-        && !cx->generationalDisabled
-        && !cx->compactingDisabledCount
-        && !cx->keepAtoms;
-}
-
 GCRuntime::IncrementalResult
 GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoTraceSession& session)
 {
     MOZ_ASSERT(reason != gc::AbortReason::None);
 
     switch (incrementalState) {
       case State::NotActive:
           return IncrementalResult::Ok;
@@ -7136,17 +7126,17 @@ GCRuntime::incrementalCollectSlice(Slice
     }
 
     MOZ_ASSERT(safeToYield);
 }
 
 gc::AbortReason
 gc::IsIncrementalGCUnsafe(JSRuntime* rt)
 {
-    MOZ_ASSERT(!TlsContext.get()->suppressGC);
+    MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
     if (!rt->gc.isIncrementalGCAllowed())
         return gc::AbortReason::IncrementalDisabled;
 
     return gc::AbortReason::None;
 }
 
 static inline void
@@ -7348,20 +7338,20 @@ GCRuntime::gcCycle(bool nonincrementalBy
     majorGCTriggerReason = JS::gcreason::NO_REASON;
 
     number++;
     if (!isIncrementalGCInProgress())
         incMajorGcNumber();
 
     // It's ok if threads other than the active thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
-    MOZ_ASSERT(!TlsContext.get()->suppressGC);
+    MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
     // Assert if this is a GC unsafe region.
-    TlsContext.get()->verifyIsSafeToGC();
+    rt->mainContextFromOwnThread()->verifyIsSafeToGC();
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
 
         // Background finalization and decommit are finished by defininition
         // before we can start a new GC session.
         if (!isIncrementalGCInProgress()) {
             assertBackgroundSweepingFinished();
@@ -7471,23 +7461,23 @@ GCRuntime::maybeDoCycleCollection()
 void
 GCRuntime::checkCanCallAPI()
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
     MOZ_RELEASE_ASSERT(!JS::CurrentThreadIsHeapBusy());
 
-    MOZ_ASSERT(TlsContext.get()->isAllocAllowed());
+    MOZ_ASSERT(rt->mainContextFromOwnThread()->isAllocAllowed());
 }
 
 bool
 GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason)
 {
-    if (TlsContext.get()->suppressGC)
+    if (rt->mainContextFromOwnThread()->suppressGC)
         return false;
 
     // Only allow shutdown GCs when we're destroying the runtime. This keeps
     // the GC callback from triggering a nested GC and resetting global state.
     if (rt->isBeingDestroyed() && !IsShutdownGC(reason))
         return false;
 
 #ifdef JS_GC_ZEAL
@@ -7604,17 +7594,17 @@ GCRuntime::gc(JSGCInvocationKind gckind,
     invocationKind = gckind;
     collect(true, SliceBudget::unlimited(), reason);
 }
 
 void
 GCRuntime::startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
 {
     MOZ_ASSERT(!isIncrementalGCInProgress());
-    if (!JS::IsIncrementalGCEnabled(TlsContext.get())) {
+    if (!JS::IsIncrementalGCEnabled(rt->mainContextFromOwnThread())) {
         gc(gckind, reason);
         return;
     }
     invocationKind = gckind;
     collect(false, defaultBudget(reason, millis), reason);
 }
 
 void
@@ -7644,17 +7634,17 @@ GCRuntime::finishGC(JS::gcreason::Reason
     collect(false, SliceBudget::unlimited(), reason);
 }
 
 void
 GCRuntime::abortGC()
 {
     MOZ_ASSERT(isIncrementalGCInProgress());
     checkCanCallAPI();
-    MOZ_ASSERT(!TlsContext.get()->suppressGC);
+    MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
     collect(false, SliceBudget::unlimited(), JS::gcreason::ABORT_GC);
 }
 
 static bool
 ZonesSelected(JSRuntime* rt)
 {
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
@@ -7724,17 +7714,17 @@ GCRuntime::onOutOfMallocMemory(const Aut
     decommitAllWithoutUnlocking(lock);
 }
 
 void
 GCRuntime::minorGC(JS::gcreason::Reason reason, gcstats::PhaseKind phase)
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
 
-    if (TlsContext.get()->suppressGC)
+    if (rt->mainContextFromOwnThread()->suppressGC)
         return;
 
     gcstats::AutoPhase ap(rt->gc.stats(), phase);
 
     nursery().clearMinorGCRequest();
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logMinorGC(logger, TraceLogger_MinorGC);
     nursery().collect(reason);
@@ -7770,30 +7760,30 @@ JS::AutoDisableGenerationalGC::~AutoDisa
         for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next())
             group->nursery().enable();
     }
 }
 
 JS_PUBLIC_API(bool)
 JS::IsGenerationalGCEnabled(JSRuntime* rt)
 {
-    return !TlsContext.get()->generationalDisabled;
+    return !rt->mainContextFromOwnThread()->generationalDisabled;
 }
 
 bool
 GCRuntime::gcIfRequested()
 {
     // This method returns whether a major GC was performed.
 
     if (nursery().minorGCRequested())
         minorGC(nursery().minorGCTriggerReason());
 
     if (majorGCRequested()) {
         if (majorGCTriggerReason == JS::gcreason::DELAYED_ATOMS_GC &&
-            !TlsContext.get()->canCollectAtoms())
+            !rt->mainContextFromOwnThread()->canCollectAtoms())
         {
             // A GC was requested to collect the atoms zone, but it's no longer
             // possible. Skip this collection.
             majorGCTriggerReason = JS::gcreason::NO_REASON;
             return false;
         }
 
         if (!isIncrementalGCInProgress())
@@ -8103,17 +8093,17 @@ GCRuntime::deleteEmptyZoneGroup(ZoneGrou
     }
     MOZ_CRASH("ZoneGroup not found");
 }
 
 void
 GCRuntime::runDebugGC()
 {
 #ifdef JS_GC_ZEAL
-    if (TlsContext.get()->suppressGC)
+    if (rt->mainContextFromOwnThread()->suppressGC)
         return;
 
     if (hasZealMode(ZealMode::GenerationalGC))
         return minorGC(JS::gcreason::DEBUG_GC);
 
     PrepareForDebugGC(rt);
 
     auto budget = SliceBudget::unlimited();
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -250,18 +250,16 @@ class GCRuntime
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0);
     void finishGC(JS::gcreason::Reason reason);
     void abortGC();
     void startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget);
     void debugGCSlice(SliceBudget& budget);
 
-    bool canChangeActiveContext(JSContext* cx);
-
     void triggerFullGCForAtoms(JSContext* cx);
 
     void runDebugGC();
     void notifyRootsRemoved();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -685,31 +685,30 @@ IsFullStoreBufferReason(JS::gcreason::Re
            reason == JS::gcreason::FULL_CELL_PTR_BUFFER ||
            reason == JS::gcreason::FULL_SLOT_BUFFER ||
            reason == JS::gcreason::FULL_SHAPE_BUFFER;
 }
 
 void
 js::Nursery::collect(JS::gcreason::Reason reason)
 {
-    MOZ_ASSERT(!TlsContext.get()->suppressGC);
+    JSRuntime* rt = runtime();
+    MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
     if (!isEnabled() || isEmpty()) {
         // Our barriers are not always exact, and there may be entries in the
         // storebuffer even when the nursery is disabled or empty. It's not safe
         // to keep these entries as they may refer to tenured cells which may be
         // freed after this point.
-        runtime()->gc.storeBuffer().clear();
+        rt->gc.storeBuffer().clear();
     }
 
     if (!isEnabled())
         return;
 
-    JSRuntime* rt = runtime();
-
 #ifdef JS_GC_ZEAL
     if (rt->gc.hasZealMode(ZealMode::CheckNursery)) {
         for (auto canary = lastCanary_; canary; canary = canary->next)
             MOZ_ASSERT(canary->magicValue == CanaryMagicValue);
     }
     lastCanary_ = nullptr;
 #endif
 
@@ -745,17 +744,17 @@ js::Nursery::collect(JS::gcreason::Reaso
     startProfile(ProfileKey::Pretenure);
     bool validPromotionRate;
     const float promotionRate = calcPromotionRate(&validPromotionRate);
     uint32_t pretenureCount = 0;
     bool shouldPretenure = (validPromotionRate && promotionRate > 0.6) ||
         IsFullStoreBufferReason(reason);
 
     if (shouldPretenure) {
-        JSContext* cx = TlsContext.get();
+        JSContext* cx = rt->mainContextFromOwnThread();
         for (auto& entry : tenureCounts.entries) {
             if (entry.count >= 3000) {
                 ObjectGroup* group = entry.group;
                 if (group->canPreTenure()) {
                     AutoCompartment ac(cx, group);
                     group->setShouldPreTenure(cx);
                     pretenureCount++;
                 }
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -961,27 +961,27 @@ Statistics::endGC()
 }
 
 void
 Statistics::beginNurseryCollection(JS::gcreason::Reason reason)
 {
     count(STAT_MINOR_GC);
     startingMinorGCNumber = runtime->gc.minorGCCount();
     if (nurseryCollectionCallback) {
-        (*nurseryCollectionCallback)(TlsContext.get(),
+        (*nurseryCollectionCallback)(runtime->mainContextFromOwnThread(),
                                      JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START,
                                      reason);
     }
 }
 
 void
 Statistics::endNurseryCollection(JS::gcreason::Reason reason)
 {
     if (nurseryCollectionCallback) {
-        (*nurseryCollectionCallback)(TlsContext.get(),
+        (*nurseryCollectionCallback)(runtime->mainContextFromOwnThread(),
                                      JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END,
                                      reason);
     }
 }
 
 void
 Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                        SliceBudget budget, JS::gcreason::Reason reason)
@@ -1006,17 +1006,17 @@ Statistics::beginSlice(const ZoneGCStats
         return;
     }
 
     runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
 
     // Slice callbacks should only fire for the outermost level.
     bool wasFullGC = zoneStats.isFullCollection();
     if (sliceCallback) {
-        JSContext* cx = TlsContext.get();
+        JSContext* cx = runtime->mainContextFromOwnThread();
         JS::GCDescription desc(!wasFullGC, false, gckind, reason);
         if (first)
             (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc);
         (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc);
     }
 }
 
 void
@@ -1077,17 +1077,17 @@ Statistics::endSlice()
 
     if (enableProfiling_ && !aborted && slices_.back().duration() >= profileThreshold_)
         printSliceProfile();
 
     // Slice callbacks should only fire for the outermost level.
     if (!aborted) {
         bool wasFullGC = zoneStats.isFullCollection();
         if (sliceCallback) {
-            JSContext* cx = TlsContext.get();
+            JSContext* cx = runtime->mainContextFromOwnThread();
             JS::GCDescription desc(!wasFullGC, last, gckind, slices_.back().reason);
             (*sliceCallback)(cx, JS::GC_SLICE_END, desc);
             if (last)
                 (*sliceCallback)(cx, JS::GC_CYCLE_END, desc);
         }
     }
 
     // Do this after the slice callback since it uses these values.
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -96,17 +96,17 @@ class js::VerifyPreTracer final : public
     /* This graph represents the initial GC "snapshot". */
     VerifyNode* curnode;
     VerifyNode* root;
     char* edgeptr;
     char* term;
     NodeMap nodemap;
 
     explicit VerifyPreTracer(JSRuntime* rt)
-      : JS::CallbackTracer(rt), noggc(TlsContext.get()), number(rt->gc.gcNumber()),
+      : JS::CallbackTracer(rt), noggc(rt->mainContextFromOwnThread()), number(rt->gc.gcNumber()),
         count(0), curnode(nullptr), root(nullptr), edgeptr(nullptr), term(nullptr)
     {}
 
     ~VerifyPreTracer() {
         js_free(root);
     }
 };
 
@@ -175,29 +175,29 @@ NextNode(VerifyNode* node)
 
 void
 gc::GCRuntime::startVerifyPreBarriers()
 {
     if (verifyPreData || isIncrementalGCInProgress())
         return;
 
     if (IsIncrementalGCUnsafe(rt) != AbortReason::None ||
-        TlsContext.get()->keepAtoms ||
+        rt->mainContextFromOwnThread()->keepAtoms ||
         rt->hasHelperThreadZones())
     {
         return;
     }
 
     number++;
 
     VerifyPreTracer* trc = js_new<VerifyPreTracer>(rt);
     if (!trc)
         return;
 
-    JSContext* cx = TlsContext.get();
+    JSContext* cx = rt->mainContextFromOwnThread();
     AutoPrepareForTracing prep(cx);
 
     {
         AutoLockGC lock(cx->runtime());
         for (auto chunk = allNonEmptyChunks(lock); !chunk.done(); chunk.next())
             chunk->bitmap.clear();
     }
 
@@ -352,17 +352,17 @@ gc::GCRuntime::endVerifyPreBarriers()
     MOZ_ASSERT(trc->number == number);
     number++;
 
     verifyPreData = nullptr;
     incrementalState = State::NotActive;
 
     if (!compartmentCreated &&
         IsIncrementalGCUnsafe(rt) == AbortReason::None &&
-        !TlsContext.get()->keepAtoms &&
+        !rt->mainContextFromOwnThread()->keepAtoms &&
         !rt->hasHelperThreadZones())
     {
         CheckEdgeTracer cetrc(rt);
 
         /* Start after the roots. */
         VerifyNode* node = NextNode(trc->root);
         while ((char*)node < trc->edgeptr) {
             cetrc.node = node;
@@ -413,17 +413,17 @@ gc::VerifyBarriers(JSRuntime* rt, Verifi
 }
 
 void
 gc::GCRuntime::maybeVerifyPreBarriers(bool always)
 {
     if (!hasZealMode(ZealMode::VerifierPre))
         return;
 
-    if (TlsContext.get()->suppressGC)
+    if (rt->mainContextFromOwnThread()->suppressGC)
         return;
 
     if (verifyPreData) {
         if (++verifyPreData->count < zealFrequency && !always)
             return;
 
         endVerifyPreBarriers();
     }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -308,19 +308,21 @@ Zone::canCollect()
     // Zones that will be or are currently used by other threads cannot be
     // collected.
     return !group()->createdForHelperThread();
 }
 
 void
 Zone::notifyObservingDebuggers()
 {
+    JSRuntime* rt = runtimeFromActiveCooperatingThread();
+    JSContext* cx = rt->mainContextFromOwnThread();
+
     for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
-        JSRuntime* rt = runtimeFromAnyThread();
-        RootedGlobalObject global(TlsContext.get(), comps->unsafeUnbarrieredMaybeGlobal());
+        RootedGlobalObject global(cx, comps->unsafeUnbarrieredMaybeGlobal());
         if (!global)
             continue;
 
         GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
         if (!dbgs)
             continue;
 
         for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront()) {
--- a/js/src/irregexp/RegExpStack.cpp
+++ b/js/src/irregexp/RegExpStack.cpp
@@ -43,17 +43,17 @@ RegExpStackScope::~RegExpStackScope()
 {
     regexp_stack->reset();
 }
 
 bool
 irregexp::GrowBacktrackStack(JSRuntime* rt)
 {
     AutoUnsafeCallWithABI unsafe;
-    return TlsContext.get()->regexpStack.ref().grow();
+    return rt->mainContextFromOwnThread()->regexpStack.ref().grow();
 }
 
 RegExpStack::RegExpStack()
   : base_(nullptr), size(0), limit_(nullptr)
 {}
 
 RegExpStack::~RegExpStack()
 {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1306,17 +1306,17 @@ TraceJitActivations(JSContext* cx, JSTra
     for (JitActivationIterator activations(cx); !activations.done(); ++activations)
         TraceJitActivation(trc, activations->asJit());
 }
 
 void
 UpdateJitActivationsForMinorGC(JSRuntime* rt)
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
-    JSContext* cx = TlsContext.get();
+    JSContext* cx = rt->mainContextFromOwnThread();
     for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
         for (OnlyJSJitFrameIter iter(activations); !iter.done(); ++iter) {
             if (iter.frame().type() == JitFrame_IonJS)
                 UpdateIonJSFrameForMinorGC(iter.frame());
         }
     }
 }
 
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -737,17 +737,18 @@ struct Unconditionally
 void
 JitcodeGlobalTable::traceForMinorGC(JSTracer* trc)
 {
     // Trace only entries that can directly contain nursery pointers.
 
     MOZ_ASSERT(trc->runtime()->geckoProfiler().enabled());
     MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
 
-    AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
+    JSContext* cx = trc->runtime()->mainContextFromOwnThread();
+    AutoSuppressProfilerSampling suppressSampling(cx);
     JitcodeGlobalEntry::IonEntry* entry = nurseryEntries_;
     while (entry) {
         entry->trace<Unconditionally>(trc);
         JitcodeGlobalEntry::IonEntry* prev = entry;
         entry = entry->nextNursery_;
         removeFromNurseryList(prev);
     }
 }
@@ -825,17 +826,17 @@ JitcodeGlobalTable::markIteratively(GCMa
     }
 
     return markedAny;
 }
 
 void
 JitcodeGlobalTable::sweep(JSRuntime* rt)
 {
-    AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
+    AutoSuppressProfilerSampling suppressSampling(rt->mainContextFromOwnThread());
     for (Enum e(*this, rt); !e.empty(); e.popFront()) {
         JitcodeGlobalEntry* entry = e.front();
 
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
             continue;
 
         if (entry->baseEntry().isJitcodeAboutToBeFinalized())
             e.removeFront();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -454,26 +454,16 @@ JS_PUBLIC_API(JSObject*)
 JS_GetBoundFunctionTarget(JSFunction* fun)
 {
     return fun->isBoundFunction() ?
                fun->getBoundFunctionTarget() : nullptr;
 }
 
 /************************************************************************/
 
-#ifdef DEBUG
-JS_FRIEND_API(bool)
-JS::isGCEnabled()
-{
-    return !TlsContext.get()->suppressGC;
-}
-#else
-JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
-#endif
-
 JS_PUBLIC_API(JSContext*)
 JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
                "must call JS_Init prior to creating any JSContexts");
 
     // Make sure that all parent runtimes are the topmost parent.
     while (parentRuntime && parentRuntime->parentRuntime)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -276,17 +276,17 @@ JSRuntime::destroyRuntime()
 
     sharedIntlData.ref().destroyInstance();
 
     if (gcInitialized) {
         /*
          * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
          * list is empty in CancelOffThreadParses.
          */
-        JSContext* cx = TlsContext.get();
+        JSContext* cx = mainContextFromOwnThread();
         if (JS::IsIncrementalGCInProgress(cx))
             FinishGC(cx);
 
         /* Free source hook early, as its destructor may want to delete roots. */
         sourceHook = nullptr;
 
         /*
          * Cancel any pending, in progress or completed Ion compilations and
@@ -799,17 +799,17 @@ JSRuntime::setUsedByHelperThread(Zone* z
 }
 
 void
 JSRuntime::clearUsedByHelperThread(Zone* zone)
 {
     MOZ_ASSERT(zone->group()->usedByHelperThread());
     zone->group()->clearUsedByHelperThread();
     numActiveHelperThreadZones--;
-    JSContext* cx = TlsContext.get();
+    JSContext* cx = mainContextFromOwnThread();
     if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
         gc.triggerFullGCForAtoms(cx);
 }
 
 bool
 js::CurrentThreadCanAccessRuntime(const JSRuntime* rt)
 {
     return rt->mainContextFromAnyThread() == TlsContext.get();