Bug 1290156 - Remove the DEBUG-only lockOwner member from GlobalHelperThreadState; r=terrence
☠☠ backed out by 8786fe9a1993 ☠ ☠
authorNick Fitzgerald <fitzgen@gmail.com>
Mon, 01 Aug 2016 09:45:30 -0700
changeset 351150 1bc0a14de00a86e2a198e40c1eb7a75442d60ce5
parent 351149 573529c879adbdfa560d00b70528e522d32bee97
child 351151 41096329fbed09dd0b344483f896068cc8c802eb
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1290156
milestone50.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 1290156 - Remove the DEBUG-only lockOwner member from GlobalHelperThreadState; r=terrence The checks that the `lockOwner` member was used for are now redundant given that `js::Mutex` uses `PTHREAD_MUTEX_ERRORCHECK` in `DEBUG` builds. In methods where we asserted `isLocked()`, we now pass in an `AutoLockHelperThreadState` reference for compile-time proof of lock holding. This is mostly a mechanical change.
js/src/asmjs/WasmGenerator.cpp
js/src/gc/Allocator.cpp
js/src/gc/Nursery.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/Runtime.cpp
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -71,27 +71,27 @@ ModuleGenerator::ModuleGenerator(ImportV
 
 ModuleGenerator::~ModuleGenerator()
 {
     if (parallel_) {
         // Wait for any outstanding jobs to fail or complete.
         if (outstanding_) {
             AutoLockHelperThreadState lock;
             while (true) {
-                IonCompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist();
+                IonCompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock);
                 MOZ_ASSERT(outstanding_ >= worklist.length());
                 outstanding_ -= worklist.length();
                 worklist.clear();
 
-                IonCompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList();
+                IonCompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock);
                 MOZ_ASSERT(outstanding_ >= finished.length());
                 outstanding_ -= finished.length();
                 finished.clear();
 
-                uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs();
+                uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs(lock);
                 MOZ_ASSERT(outstanding_ >= numFailed);
                 outstanding_ -= numFailed;
 
                 if (!outstanding_)
                     break;
 
                 HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
             }
@@ -194,22 +194,22 @@ ModuleGenerator::finishOutstandingTask()
     MOZ_ASSERT(parallel_);
 
     IonCompileTask* task = nullptr;
     {
         AutoLockHelperThreadState lock;
         while (true) {
             MOZ_ASSERT(outstanding_ > 0);
 
-            if (HelperThreadState().wasmFailed())
+            if (HelperThreadState().wasmFailed(lock))
                 return false;
 
-            if (!HelperThreadState().wasmFinishedList().empty()) {
+            if (!HelperThreadState().wasmFinishedList(lock).empty()) {
                 outstanding_--;
-                task = HelperThreadState().wasmFinishedList().popCopy();
+                task = HelperThreadState().wasmFinishedList(lock).popCopy();
                 break;
             }
 
             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
         }
     }
 
     return finishTask(task);
@@ -785,19 +785,19 @@ ModuleGenerator::startFuncDefs()
     GlobalHelperThreadState& threads = HelperThreadState();
     MOZ_ASSERT(threads.threadCount > 1);
 
     uint32_t numTasks;
     if (CanUseExtraThreads() && threads.wasmCompilationInProgress.compareExchange(false, true)) {
 #ifdef DEBUG
         {
             AutoLockHelperThreadState lock;
-            MOZ_ASSERT(!HelperThreadState().wasmFailed());
-            MOZ_ASSERT(HelperThreadState().wasmWorklist().empty());
-            MOZ_ASSERT(HelperThreadState().wasmFinishedList().empty());
+            MOZ_ASSERT(!HelperThreadState().wasmFailed(lock));
+            MOZ_ASSERT(HelperThreadState().wasmWorklist(lock).empty());
+            MOZ_ASSERT(HelperThreadState().wasmFinishedList(lock).empty());
         }
 #endif
         parallel_ = true;
         numTasks = threads.maxWasmCompilationThreads();
     } else {
         numTasks = 1;
     }
 
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -289,17 +289,17 @@ GCRuntime::startBackgroundAllocTaskIfIdl
 {
     AutoLockHelperThreadState helperLock;
     if (allocTask.isRunningWithLockHeld(helperLock))
         return;
 
     // Join the previous invocation of the task. This will return immediately
     // if the thread has never been started.
     allocTask.joinWithLockHeld(helperLock);
-    allocTask.startWithLockHeld();
+    allocTask.startWithLockHeld(helperLock);
 }
 
 /* static */ TenuredCell*
 GCRuntime::refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind, size_t thingSize)
 {
     cx->arenas()->checkEmptyFreeList(thingKind);
 
     if (cx->isJSContext())
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -709,17 +709,17 @@ js::Nursery::freeMallocedBuffers()
     if (mallocedBuffers.empty())
         return;
 
     bool started;
     {
         AutoLockHelperThreadState lock;
         freeMallocedBuffersTask->joinWithLockHeld(lock);
         freeMallocedBuffersTask->transferBuffersToFree(mallocedBuffers, lock);
-        started = freeMallocedBuffersTask->startWithLockHeld();
+        started = freeMallocedBuffersTask->startWithLockHeld(lock);
     }
 
     if (!started)
         freeMallocedBuffersTask->runFromMainThread(runtime());
 
     MOZ_ASSERT(mallocedBuffers.empty());
 }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -469,20 +469,19 @@ JitCompartment::ensureIonStubsExist(JSCo
         if (!stringConcatStub_)
             return false;
     }
 
     return true;
 }
 
 void
-jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder)
+jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder,
+                            const AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-
     // Clean the references to the pending IonBuilder, if we just finished it.
     if (builder->script()->baselineScript()->hasPendingIonBuilder() &&
         builder->script()->baselineScript()->pendingIonBuilder() == builder)
     {
         builder->script()->baselineScript()->removePendingIonBuilder(builder->script());
     }
 
     // If the builder is still in one of the helper thread list, then remove it.
@@ -510,22 +509,22 @@ jit::FinishOffThreadBuilder(JSRuntime* r
     js_delete(builder->backgroundCodegen());
     js_delete(builder->alloc().lifoAlloc());
 }
 
 static inline void
 FinishAllOffThreadCompilations(JSCompartment* comp)
 {
     AutoLockHelperThreadState lock;
-    GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
+    GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
 
     for (size_t i = 0; i < finished.length(); i++) {
         IonBuilder* builder = finished[i];
         if (builder->compartment == CompileCompartment::get(comp)) {
-            FinishOffThreadBuilder(nullptr, builder);
+            FinishOffThreadBuilder(nullptr, builder, lock);
             HelperThreadState().remove(finished, &i);
         }
     }
 }
 
 static bool
 LinkCodeGen(JSContext* cx, IonBuilder* builder, CodeGenerator *codegen)
 {
@@ -585,17 +584,17 @@ jit::LinkIonScript(JSContext* cx, Handle
 
             // Reset the TypeZone's compiler output for this script, if any.
             InvalidateCompilerOutputsForScript(cx, calleeScript);
         }
     }
 
     {
         AutoLockHelperThreadState lock;
-        FinishOffThreadBuilder(cx->runtime(), builder);
+        FinishOffThreadBuilder(cx->runtime(), builder, lock);
     }
 }
 
 uint8_t*
 jit::LazyLinkTopActivation(JSContext* cx)
 {
     // First frame should be an exit frame.
     JitFrameIterator it(cx);
@@ -2047,17 +2046,17 @@ AttachFinishedCompilations(JSContext* cx
 {
     JitCompartment* ion = cx->compartment()->jitCompartment();
     if (!ion)
         return;
 
     {
         AutoLockHelperThreadState lock;
 
-        GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
+        GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
 
         // Incorporate any off thread compilations for the compartment which have
         // finished, failed or have been cancelled.
         while (true) {
             // Find a finished builder for the compartment.
             IonBuilder* builder = GetFinishedBuilder(cx, finished);
             if (!builder)
                 break;
@@ -3534,9 +3533,8 @@ jit::JitSupportsAtomics()
 #else
     return true;
 #endif
 }
 
 // If you change these, please also change the comment in TempAllocator.
 /* static */ const size_t TempAllocator::BallastSize            = 16 * 1024;
 /* static */ const size_t TempAllocator::PreferredLifoChunkSize = 32 * 1024;
-
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -148,17 +148,18 @@ class LIRGraph;
 class CodeGenerator;
 
 MOZ_MUST_USE bool OptimizeMIR(MIRGenerator* mir);
 LIRGraph* GenerateLIR(MIRGenerator* mir);
 CodeGenerator* GenerateCode(MIRGenerator* mir, LIRGraph* lir);
 CodeGenerator* CompileBackEnd(MIRGenerator* mir);
 
 void AttachFinishedCompilations(JSContext* cx);
-void FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder);
+void FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder,
+                            const AutoLockHelperThreadState& lock);
 void StopAllOffThreadCompilations(Zone* zone);
 void StopAllOffThreadCompilations(JSCompartment* comp);
 
 void LinkIonScript(JSContext* cx, HandleScript calleescript);
 uint8_t* LazyLinkTopActivation(JSContext* cx);
 
 static inline bool
 IsIonEnabled(JSContext* cx)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3284,28 +3284,29 @@ GCHelperState::state(const AutoLockGC&)
 
 void
 GCHelperState::setState(State state, const AutoLockGC&)
 {
     state_ = state;
 }
 
 void
-GCHelperState::startBackgroundThread(State newState, const AutoLockGC& lock)
+GCHelperState::startBackgroundThread(State newState, const AutoLockGC& lock,
+                                     const AutoLockHelperThreadState& helperLock)
 {
     MOZ_ASSERT(!thread && state(lock) == IDLE && newState != IDLE);
     setState(newState, lock);
 
     {
         AutoEnterOOMUnsafeRegion noOOM;
-        if (!HelperThreadState().gcHelperWorklist().append(this))
+        if (!HelperThreadState().gcHelperWorklist(helperLock).append(this))
             noOOM.crash("Could not add to pending GC helpers list");
     }
 
-    HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, helperLock);
 }
 
 void
 GCHelperState::waitForBackgroundThread(js::AutoLockGC& lock)
 {
     done.wait(lock.guard());
 }
 
@@ -3343,17 +3344,17 @@ GCHelperState::work()
 }
 
 void
 GCRuntime::queueZonesForBackgroundSweep(ZoneList& zones)
 {
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
     backgroundSweepZones.transferFrom(zones);
-    helperState.maybeStartBackgroundSweep(lock);
+    helperState.maybeStartBackgroundSweep(lock, helperLock);
 }
 
 void
 GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo)
 {
     MOZ_ASSERT(rt->isHeapBusy());
     AutoLockGC lock(rt);
     blocksToFreeAfterSweeping.transferUnusedFrom(lifo);
@@ -3370,22 +3371,23 @@ GCRuntime::freeAllLifoBlocksAfterSweepin
 void
 GCRuntime::freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     blocksToFreeAfterMinorGC.transferFrom(lifo);
 }
 
 void
-GCHelperState::maybeStartBackgroundSweep(const AutoLockGC& lock)
+GCHelperState::maybeStartBackgroundSweep(const AutoLockGC& lock,
+                                         const AutoLockHelperThreadState& helperLock)
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     if (state(lock) == IDLE)
-        startBackgroundThread(SWEEPING, lock);
+        startBackgroundThread(SWEEPING, lock, helperLock);
 }
 
 void
 GCHelperState::waitBackgroundSweepEnd()
 {
     AutoLockGC lock(rt);
     while (state(lock) == SWEEPING)
         waitForBackgroundThread(lock);
@@ -4878,18 +4880,17 @@ SweepMiscTask::run()
         c->sweepSelfHostingScriptSource();
         c->sweepNativeIterators();
     }
 }
 
 void
 GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    if (!task.startWithLockHeld()) {
+    if (!task.startWithLockHeld(locked)) {
         AutoUnlockHelperThreadState unlock(locked);
         gcstats::AutoPhase ap(stats, phase);
         task.runFromMainThread(rt);
     }
 }
 
 void
 GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked)
@@ -5593,17 +5594,17 @@ AutoTraceSession::~AutoTraceSession()
 {
     MOZ_ASSERT(runtime->isHeapBusy());
 
     if (runtime->exclusiveThreadsPresent()) {
         AutoLockHelperThreadState lock;
         runtime->heapState_ = prevState;
 
         // Notify any helper threads waiting for the trace session to end.
-        HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
+        HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
     } else {
         runtime->heapState_ = prevState;
     }
 }
 
 void
 GCRuntime::resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lock)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -860,17 +860,18 @@ class GCHelperState
     js::ConditionVariable done;
 
     // Activity for the helper to do, protected by the GC lock.
     State state_;
 
     // Thread which work is being performed on, or null.
     PRThread* thread;
 
-    void startBackgroundThread(State newState, const js::AutoLockGC&);
+    void startBackgroundThread(State newState, const AutoLockGC& lock,
+                               const AutoLockHelperThreadState& helperLock);
     void waitForBackgroundThread(js::AutoLockGC& lock);
 
     State state(const AutoLockGC&);
     void setState(State state, const AutoLockGC&);
 
     friend class js::gc::ArenaLists;
 
     static void freeElementsAndArray(void** array, void** end) {
@@ -889,17 +890,18 @@ class GCHelperState
         state_(IDLE),
         thread(nullptr)
     { }
 
     void finish();
 
     void work();
 
-    void maybeStartBackgroundSweep(const AutoLockGC& lock);
+    void maybeStartBackgroundSweep(const AutoLockGC& lock,
+                                   const AutoLockHelperThreadState& helperLock);
     void startBackgroundShrink(const AutoLockGC& lock);
 
     /* Must be called without the GC lock taken. */
     void waitBackgroundSweepEnd();
 
     bool onBackgroundThread();
 
     /*
@@ -950,17 +952,17 @@ class GCParallelTask
     int64_t duration() const { return duration_; }
 
     // The simple interface to a parallel task works exactly like pthreads.
     bool start();
     void join();
 
     // If multiple tasks are to be started or joined at once, it is more
     // efficient to take the helper thread lock once and use these methods.
-    bool startWithLockHeld();
+    bool startWithLockHeld(AutoLockHelperThreadState& locked);
     void joinWithLockHeld(AutoLockHelperThreadState& locked);
 
     // Instead of dispatching to a helper, run the task on the main thread.
     void runFromMainThread(JSRuntime* rt);
 
     // Dispatch a cancelation request.
     enum CancelMode { CancelNoWait, CancelAndWait};
     void cancel(CancelMode mode = CancelNoWait) {
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -81,48 +81,48 @@ js::SetFakeCPUCount(size_t count)
 }
 
 bool
 js::StartOffThreadWasmCompile(wasm::IonCompileTask* task)
 {
     AutoLockHelperThreadState lock;
 
     // Don't append this task if another failed.
-    if (HelperThreadState().wasmFailed())
+    if (HelperThreadState().wasmFailed(lock))
         return false;
 
-    if (!HelperThreadState().wasmWorklist().append(task))
+    if (!HelperThreadState().wasmWorklist(lock).append(task))
         return false;
 
-    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     return true;
 }
 
 bool
 js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder)
 {
     AutoLockHelperThreadState lock;
 
-    if (!HelperThreadState().ionWorklist().append(builder))
+    if (!HelperThreadState().ionWorklist(lock).append(builder))
         return false;
 
-    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     return true;
 }
 
 /*
  * Move an IonBuilder for which compilation has either finished, failed, or
  * been cancelled into the global finished compilation list. All off thread
  * compilations which are started must eventually be finished.
  */
 static void
-FinishOffThreadIonCompile(jit::IonBuilder* builder)
+FinishOffThreadIonCompile(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock)
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
-    if (!HelperThreadState().ionFinishedList().append(builder))
+    if (!HelperThreadState().ionFinishedList(lock).append(builder))
         oomUnsafe.crash("FinishOffThreadIonCompile");
 }
 
 static inline bool
 CompiledScriptMatches(JSCompartment* compartment, JSScript* script, JSScript* target)
 {
     if (script)
         return target == script;
@@ -138,59 +138,59 @@ js::CancelOffThreadIonCompile(JSCompartm
         return;
 
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().threads)
         return;
 
     /* Cancel any pending entries for which processing hasn't started. */
-    GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist();
+    GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     for (size_t i = 0; i < worklist.length(); i++) {
         jit::IonBuilder* builder = worklist[i];
         if (CompiledScriptMatches(compartment, script, builder->script())) {
-            FinishOffThreadIonCompile(builder);
+            FinishOffThreadIonCompile(builder, lock);
             HelperThreadState().remove(worklist, &i);
         }
     }
 
     /* Wait for in progress entries to finish up. */
     for (size_t i = 0; i < HelperThreadState().threadCount; i++) {
         HelperThread& helper = HelperThreadState().threads[i];
         while (helper.ionBuilder() &&
                CompiledScriptMatches(compartment, script, helper.ionBuilder()->script()))
         {
             helper.ionBuilder()->cancel();
             if (helper.pause) {
                 helper.pause = false;
-                HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE);
+                HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE, lock);
             }
             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
         }
     }
 
     /* Cancel code generation for any completed entries. */
-    GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
+    GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
     for (size_t i = 0; i < finished.length(); i++) {
         jit::IonBuilder* builder = finished[i];
         if (CompiledScriptMatches(compartment, script, builder->script())) {
-            jit::FinishOffThreadBuilder(nullptr, builder);
+            jit::FinishOffThreadBuilder(nullptr, builder, lock);
             HelperThreadState().remove(finished, &i);
         }
     }
 
     /* Cancel lazy linking for pending builders (attached to the ionScript). */
     if (discardLazyLinkList) {
         MOZ_ASSERT(compartment);
         JSRuntime* runtime = compartment->runtimeFromMainThread();
         jit::IonBuilder* builder = runtime->ionLazyLinkList().getFirst();
         while (builder) {
             jit::IonBuilder* next = builder->getNext();
             if (CompiledScriptMatches(compartment, script, builder->script()))
-                jit::FinishOffThreadBuilder(runtime, builder);
+                jit::FinishOffThreadBuilder(runtime, builder, lock);
             builder = next;
         }
     }
 }
 
 static const JSClassOps parseTaskGlobalClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
@@ -303,27 +303,28 @@ void
 js::CancelOffThreadParses(JSRuntime* rt)
 {
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().threads)
         return;
 
 #ifdef DEBUG
-    GlobalHelperThreadState::ParseTaskVector& waitingOnGC = HelperThreadState().parseWaitingOnGC();
+    GlobalHelperThreadState::ParseTaskVector& waitingOnGC =
+        HelperThreadState().parseWaitingOnGC(lock);
     for (size_t i = 0; i < waitingOnGC.length(); i++)
         MOZ_ASSERT(!waitingOnGC[i]->runtimeMatches(rt));
 #endif
 
     // Instead of forcibly canceling pending parse tasks, just wait for all scheduled
     // and in progress ones to complete. Otherwise the final GC may not collect
     // everything due to zones being used off thread.
     while (true) {
         bool pending = false;
-        GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist();
+        GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist(lock);
         for (size_t i = 0; i < worklist.length(); i++) {
             ParseTask* task = worklist[i];
             if (task->runtimeMatches(rt))
                 pending = true;
         }
         if (!pending) {
             bool inProgress = false;
             for (size_t i = 0; i < HelperThreadState().threadCount; i++) {
@@ -333,17 +334,17 @@ js::CancelOffThreadParses(JSRuntime* rt)
             }
             if (!inProgress)
                 break;
         }
         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     }
 
     // Clean up any parse tasks which haven't been finished by the main thread.
-    GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList();
+    GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList(lock);
     while (true) {
         bool found = false;
         for (size_t i = 0; i < finished.length(); i++) {
             ParseTask* task = finished[i];
             if (task->runtimeMatches(rt)) {
                 found = true;
                 AutoUnlockHelperThreadState unlock(lock);
                 HelperThreadState().cancelParseTask(rt->contextFromMainThread(), task->kind, task);
@@ -443,29 +444,29 @@ CreateGlobalForOffThreadParse(JSContext*
     return global;
 }
 
 static bool
 QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
 {
     if (OffThreadParsingMustWaitForGC(cx->runtime())) {
         AutoLockHelperThreadState lock;
-        if (!HelperThreadState().parseWaitingOnGC().append(task)) {
+        if (!HelperThreadState().parseWaitingOnGC(lock).append(task)) {
             ReportOutOfMemory(cx);
             return false;
         }
     } else {
         AutoLockHelperThreadState lock;
-        if (!HelperThreadState().parseWorklist().append(task)) {
+        if (!HelperThreadState().parseWorklist(lock).append(task)) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         task->activate(cx->runtime());
-        HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+        HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     }
 
     return true;
 }
 
 bool
 js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
                               const char16_t* chars, size_t length,
@@ -543,17 +544,18 @@ js::StartOffThreadParseModule(JSContext*
 void
 js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
 {
     MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
 
     GlobalHelperThreadState::ParseTaskVector newTasks;
     {
         AutoLockHelperThreadState lock;
-        GlobalHelperThreadState::ParseTaskVector& waiting = HelperThreadState().parseWaitingOnGC();
+        GlobalHelperThreadState::ParseTaskVector& waiting =
+            HelperThreadState().parseWaitingOnGC(lock);
 
         for (size_t i = 0; i < waiting.length(); i++) {
             ParseTask* task = waiting[i];
             if (task->runtimeMatches(rt)) {
                 AutoEnterOOMUnsafeRegion oomUnsafe;
                 if (!newTasks.append(task))
                     oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
                 HelperThreadState().remove(waiting, &i);
@@ -569,21 +571,21 @@ js::EnqueuePendingParseTasksAfterGC(JSRu
 
     for (size_t i = 0; i < newTasks.length(); i++)
         newTasks[i]->activate(rt);
 
     AutoLockHelperThreadState lock;
 
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!HelperThreadState().parseWorklist().appendAll(newTasks))
+        if (!HelperThreadState().parseWorklist(lock).appendAll(newTasks))
             oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
     }
 
-    HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
 }
 
 static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
 static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
 
 // TSan enforces a minimum stack size that's just slightly larger than our
 // default helper stack size.  It does this to store blobs of TSan-specific
 // data on each thread's stack.  Unfortunately, that means that even though
@@ -662,74 +664,48 @@ GlobalHelperThreadState::finishThreads()
         threads[i].destroy();
     js_free(threads);
     threads = nullptr;
 }
 
 void
 GlobalHelperThreadState::lock()
 {
-    MOZ_ASSERT(!isLocked());
     AssertCurrentThreadCanLock(HelperThreadStateLock);
     helperLock.lock();
-#ifdef DEBUG
-    lockOwner = PR_GetCurrentThread();
-#endif
 }
 
 void
 GlobalHelperThreadState::unlock()
 {
-    MOZ_ASSERT(isLocked());
-#ifdef DEBUG
-    lockOwner = nullptr;
-#endif
     helperLock.unlock();
 }
 
-#ifdef DEBUG
-bool
-GlobalHelperThreadState::isLocked()
-{
-    return lockOwner == PR_GetCurrentThread();
-}
-#endif
-
 void
 GlobalHelperThreadState::wait(AutoLockHelperThreadState& locked, CondVar which,
                               TimeDuration timeout /* = TimeDuration::Forever() */)
 {
-    MOZ_ASSERT(isLocked());
-#ifdef DEBUG
-    lockOwner = nullptr;
-#endif
     whichWakeup(which).wait_for(locked, timeout);
-#ifdef DEBUG
-    lockOwner = PR_GetCurrentThread();
-#endif
 }
 
 void
-GlobalHelperThreadState::notifyAll(CondVar which)
+GlobalHelperThreadState::notifyAll(CondVar which, const AutoLockHelperThreadState&)
 {
-    MOZ_ASSERT(isLocked());
     whichWakeup(which).notify_all();
 }
 
 void
-GlobalHelperThreadState::notifyOne(CondVar which)
+GlobalHelperThreadState::notifyOne(CondVar which, const AutoLockHelperThreadState&)
 {
-    MOZ_ASSERT(isLocked());
     whichWakeup(which).notify_one();
 }
 
 bool
-GlobalHelperThreadState::hasActiveThreads()
+GlobalHelperThreadState::hasActiveThreads(const AutoLockHelperThreadState&)
 {
-    MOZ_ASSERT(isLocked());
     if (!threads)
         return false;
 
     for (size_t i = 0; i < threadCount; i++) {
         if (!threads[i].idle())
             return true;
     }
 
@@ -737,17 +713,17 @@ GlobalHelperThreadState::hasActiveThread
 }
 
 void
 GlobalHelperThreadState::waitForAllThreads()
 {
     CancelOffThreadIonCompile(nullptr, nullptr, /* discardLazyLinkList = */ false);
 
     AutoLockHelperThreadState lock;
-    while (hasActiveThreads())
+    while (hasActiveThreads(lock))
         wait(lock, CONSUMER);
 }
 
 template <typename T>
 bool
 GlobalHelperThreadState::checkTaskThreadLimit(size_t maxThreads) const
 {
     if (maxThreads >= threadCount)
@@ -831,21 +807,20 @@ size_t
 GlobalHelperThreadState::maxGCParallelThreads() const
 {
     if (IsHelperThreadSimulatingOOM(js::oom::THREAD_TYPE_GCPARALLEL))
         return 1;
     return threadCount;
 }
 
 bool
-GlobalHelperThreadState::canStartWasmCompile()
+GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lock)
 {
     // Don't execute an wasm job if an earlier one failed.
-    MOZ_ASSERT(isLocked());
-    if (wasmWorklist().empty() || numWasmFailedJobs)
+    if (wasmWorklist(lock).empty() || numWasmFailedJobs)
         return false;
 
     // Honor the maximum allowed threads to compile wasm jobs at once,
     // to avoid oversaturating the machine.
     if (!checkTaskThreadLimit<wasm::IonCompileTask*>(maxWasmCompilationThreads()))
         return false;
 
     return true;
@@ -866,49 +841,48 @@ IonBuilderHasHigherPriority(jit::IonBuil
         return !first->scriptHasIonScript();
 
     // A higher warm-up counter indicates a higher priority.
     return first->script()->getWarmUpCount() / first->script()->length() >
            second->script()->getWarmUpCount() / second->script()->length();
 }
 
 bool
-GlobalHelperThreadState::canStartIonCompile()
+GlobalHelperThreadState::canStartIonCompile(const AutoLockHelperThreadState& lock)
 {
-    return !ionWorklist().empty() &&
+    return !ionWorklist(lock).empty() &&
            checkTaskThreadLimit<jit::IonBuilder*>(maxIonCompilationThreads());
 }
 
 jit::IonBuilder*
-GlobalHelperThreadState::highestPriorityPendingIonCompile(bool remove /* = false */)
+GlobalHelperThreadState::highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock,
+                                                          bool remove /* = false */)
 {
-    MOZ_ASSERT(isLocked());
-
-    if (ionWorklist().empty()) {
+    auto& worklist = ionWorklist(lock);
+    if (worklist.empty()) {
         MOZ_ASSERT(!remove);
         return nullptr;
     }
 
     // Get the highest priority IonBuilder which has not started compilation yet.
     size_t index = 0;
-    for (size_t i = 1; i < ionWorklist().length(); i++) {
-        if (IonBuilderHasHigherPriority(ionWorklist()[i], ionWorklist()[index]))
+    for (size_t i = 1; i < worklist.length(); i++) {
+        if (IonBuilderHasHigherPriority(worklist[i], worklist[index]))
             index = i;
     }
-    jit::IonBuilder* builder = ionWorklist()[index];
+    jit::IonBuilder* builder = worklist[index];
     if (remove)
-        ionWorklist().erase(&ionWorklist()[index]);
+        worklist.erase(&worklist[index]);
     return builder;
 }
 
 HelperThread*
-GlobalHelperThreadState::lowestPriorityUnpausedIonCompileAtThreshold()
+GlobalHelperThreadState::lowestPriorityUnpausedIonCompileAtThreshold(
+    const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-
     // Get the lowest priority IonBuilder which has started compilation and
     // isn't paused, unless there are still fewer than the maximum number of
     // such builders permitted.
     size_t numBuilderThreads = 0;
     HelperThread* thread = nullptr;
     for (size_t i = 0; i < threadCount; i++) {
         if (threads[i].ionBuilder() && !threads[i].pause) {
             numBuilderThreads++;
@@ -917,138 +891,130 @@ GlobalHelperThreadState::lowestPriorityU
         }
     }
     if (numBuilderThreads < maxUnpausedIonCompilationThreads())
         return nullptr;
     return thread;
 }
 
 HelperThread*
-GlobalHelperThreadState::highestPriorityPausedIonCompile()
+GlobalHelperThreadState::highestPriorityPausedIonCompile(const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-
     // Get the highest priority IonBuilder which has started compilation but
     // which was subsequently paused.
     HelperThread* thread = nullptr;
     for (size_t i = 0; i < threadCount; i++) {
         if (threads[i].pause) {
             // Currently, only threads with IonBuilders can be paused.
             MOZ_ASSERT(threads[i].ionBuilder());
             if (!thread || IonBuilderHasHigherPriority(threads[i].ionBuilder(), thread->ionBuilder()))
                 thread = &threads[i];
         }
     }
     return thread;
 }
 
 bool
-GlobalHelperThreadState::pendingIonCompileHasSufficientPriority()
+GlobalHelperThreadState::pendingIonCompileHasSufficientPriority(
+    const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-
     // Can't compile anything if there are no scripts to compile.
-    if (!canStartIonCompile())
+    if (!canStartIonCompile(lock))
         return false;
 
     // Count the number of threads currently compiling scripts, and look for
     // the thread with the lowest priority.
-    HelperThread* lowestPriorityThread = lowestPriorityUnpausedIonCompileAtThreshold();
+    HelperThread* lowestPriorityThread = lowestPriorityUnpausedIonCompileAtThreshold(lock);
 
     // If the number of threads building scripts is less than the maximum, the
     // compilation can start immediately.
     if (!lowestPriorityThread)
         return true;
 
     // If there is a builder in the worklist with higher priority than some
     // builder currently being compiled, then that current compilation can be
     // paused, so allow the compilation.
-    if (IonBuilderHasHigherPriority(highestPriorityPendingIonCompile(),
+    if (IonBuilderHasHigherPriority(highestPriorityPendingIonCompile(lock),
                                     lowestPriorityThread->ionBuilder()))
         return true;
 
     // Compilation will have to wait until one of the active compilations finishes.
     return false;
 }
 
 bool
-GlobalHelperThreadState::canStartParseTask()
+GlobalHelperThreadState::canStartParseTask(const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-    return !parseWorklist().empty() && checkTaskThreadLimit<ParseTask*>(maxParseThreads());
+    return !parseWorklist(lock).empty() && checkTaskThreadLimit<ParseTask*>(maxParseThreads());
 }
 
 bool
-GlobalHelperThreadState::canStartCompressionTask()
+GlobalHelperThreadState::canStartCompressionTask(const AutoLockHelperThreadState& lock)
 {
-    return !compressionWorklist().empty() &&
+    return !compressionWorklist(lock).empty() &&
            checkTaskThreadLimit<SourceCompressionTask*>(maxCompressionThreads());
 }
 
 bool
-GlobalHelperThreadState::canStartGCHelperTask()
+GlobalHelperThreadState::canStartGCHelperTask(const AutoLockHelperThreadState& lock)
 {
-    return !gcHelperWorklist().empty() &&
+    return !gcHelperWorklist(lock).empty() &&
            checkTaskThreadLimit<GCHelperState*>(maxGCHelperThreads());
 }
 
 bool
-GlobalHelperThreadState::canStartGCParallelTask()
+GlobalHelperThreadState::canStartGCParallelTask(const AutoLockHelperThreadState& lock)
 {
-    return !gcParallelWorklist().empty() &&
+    return !gcParallelWorklist(lock).empty() &&
            checkTaskThreadLimit<GCParallelTask*>(maxGCParallelThreads());
 }
 
 js::GCParallelTask::~GCParallelTask()
 {
     // Only most-derived classes' destructors may do the join: base class
     // destructors run after those for derived classes' members, so a join in a
     // base class can't ensure that the task is done using the members. All we
     // can do now is check that someone has previously stopped the task.
 #ifdef DEBUG
     AutoLockHelperThreadState helperLock;
     MOZ_ASSERT(state == NotStarted);
 #endif
 }
 
 bool
-js::GCParallelTask::startWithLockHeld()
+js::GCParallelTask::startWithLockHeld(AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-
     // Tasks cannot be started twice.
     MOZ_ASSERT(state == NotStarted);
 
     // If we do the shutdown GC before running anything, we may never
     // have initialized the helper threads. Just use the serial path
     // since we cannot safely intialize them at this point.
     if (!HelperThreadState().threads)
         return false;
 
-    if (!HelperThreadState().gcParallelWorklist().append(this))
+    if (!HelperThreadState().gcParallelWorklist(lock).append(this))
         return false;
     state = Dispatched;
 
-    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
 
     return true;
 }
 
 bool
 js::GCParallelTask::start()
 {
     AutoLockHelperThreadState helperLock;
-    return startWithLockHeld();
+    return startWithLockHeld(helperLock);
 }
 
 void
 js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-
     if (state == NotStarted)
         return;
 
     while (state != Finished)
         HelperThreadState().wait(locked, GlobalHelperThreadState::CONSUMER);
     state = NotStarted;
     cancel_ = false;
 }
@@ -1068,57 +1034,53 @@ js::GCParallelTask::runFromMainThread(JS
     uint64_t timeStart = PRMJ_Now();
     run();
     duration_ = PRMJ_Now() - timeStart;
 }
 
 void
 js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-
     {
         AutoUnlockHelperThreadState parallelSection(locked);
         uint64_t timeStart = PRMJ_Now();
         run();
         duration_ = PRMJ_Now() - timeStart;
     }
 
     state = Finished;
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
 js::GCParallelTask::isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
     return state == Dispatched;
 }
 
 bool
 js::GCParallelTask::isRunning() const
 {
     AutoLockHelperThreadState helperLock;
     return isRunningWithLockHeld(helperLock);
 }
 
 void
 HelperThread::handleGCParallelWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
+    MOZ_ASSERT(HelperThreadState().canStartGCParallelTask(locked));
     MOZ_ASSERT(idle());
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_GC);
 
-    currentTask.emplace(HelperThreadState().gcParallelWorklist().popCopy());
+    currentTask.emplace(HelperThreadState().gcParallelWorklist(locked).popCopy());
     gcParallelTask()->runFromHelperThread(locked);
     currentTask.reset();
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 static void
 LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
 {
     // Mark the zone as no longer in use by an ExclusiveContext, and available
     // to be collected by the GC.
     task->cx->leaveCompartment(task->cx->compartment());
@@ -1127,17 +1089,17 @@ LeaveParseTaskZone(JSRuntime* rt, ParseT
 
 ParseTask*
 GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, void* token)
 {
     // The token is a ParseTask* which should be in the finished list.
     // Find and remove its entry.
 
     AutoLockHelperThreadState lock;
-    ParseTaskVector& finished = parseFinishedList();
+    ParseTaskVector& finished = parseFinishedList(lock);
 
     for (size_t i = 0; i < finished.length(); i++) {
         if (finished[i] == token) {
             ParseTask* parseTask = finished[i];
             remove(finished, &i);
             MOZ_ASSERT(parseTask);
             MOZ_ASSERT(parseTask->kind == kind);
             return parseTask;
@@ -1305,17 +1267,17 @@ void
 HelperThread::destroy()
 {
     if (thread) {
         {
             AutoLockHelperThreadState lock;
             terminate = true;
 
             /* Notify all helpers, to ensure that this thread wakes up. */
-            HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
+            HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
         }
 
         PR_JoinThread(thread);
     }
 
     threadData.reset();
 }
 
@@ -1346,60 +1308,59 @@ HelperThread::ThreadMain(void* arg)
     FIX_FPU();
 
     static_cast<HelperThread*>(arg)->threadLoop();
 }
 
 void
 HelperThread::handleWasmWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartWasmCompile());
+    MOZ_ASSERT(HelperThreadState().canStartWasmCompile(locked));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().wasmWorklist().popCopy());
+    currentTask.emplace(HelperThreadState().wasmWorklist(locked).popCopy());
     bool success = false;
 
     wasm::IonCompileTask* task = wasmTask();
     {
         AutoUnlockHelperThreadState unlock(locked);
         success = wasm::CompileFunction(task);
     }
 
     // On success, try to move work to the finished list.
     if (success)
-        success = HelperThreadState().wasmFinishedList().append(task);
+        success = HelperThreadState().wasmFinishedList(locked).append(task);
 
     // On failure, note the failure for harvesting by the parent.
     if (!success)
-        HelperThreadState().noteWasmFailure();
+        HelperThreadState().noteWasmFailure(locked);
 
     // Notify the main thread in case it's waiting.
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
     currentTask.reset();
 }
 
 void
 HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartIonCompile());
+    MOZ_ASSERT(HelperThreadState().canStartIonCompile(locked));
     MOZ_ASSERT(idle());
 
     // Find the IonBuilder in the worklist with the highest priority, and
     // remove it from the worklist.
     jit::IonBuilder* builder =
-        HelperThreadState().highestPriorityPendingIonCompile(/* remove = */ true);
+        HelperThreadState().highestPriorityPendingIonCompile(locked, /* remove = */ true);
 
     // If there are now too many threads with active IonBuilders, indicate to
     // the one with the lowest priority that it should pause. Note that due to
     // builder priorities changing since pendingIonCompileHasSufficientPriority
     // was called, the builder we are pausing may actually be higher priority
     // than the one we are about to start. Oh well.
-    if (HelperThread* other = HelperThreadState().lowestPriorityUnpausedIonCompileAtThreshold()) {
+    HelperThread* other = HelperThreadState().lowestPriorityUnpausedIonCompileAtThreshold(locked);
+    if (other) {
         MOZ_ASSERT(other->ionBuilder() && !other->pause);
         other->pause = true;
     }
 
     currentTask.emplace(builder);
     builder->setPauseFlag(&pause);
 
     JSRuntime* rt = builder->script()->compartment()->runtimeFromAnyThread();
@@ -1415,48 +1376,48 @@ HelperThread::handleIonWorkload(AutoLock
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               builder->script()->runtimeFromAnyThread());
         jit::JitContext jctx(jit::CompileRuntime::get(rt),
                              jit::CompileCompartment::get(builder->script()->compartment()),
                              &builder->alloc());
         builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
     }
 
-    FinishOffThreadIonCompile(builder);
+    FinishOffThreadIonCompile(builder, locked);
     currentTask.reset();
     pause = false;
 
     // Ping the main thread so that the compiled code can be incorporated
     // at the next interrupt callback. Don't interrupt Ion code for this, as
     // this incorporation can be delayed indefinitely without affecting
     // performance as long as the main thread is actually executing Ion code.
     rt->requestInterrupt(JSRuntime::RequestInterruptCanWait);
 
     // Notify the main thread in case it is waiting for the compilation to finish.
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 
     // When finishing Ion compilation jobs, we can start unpausing compilation
     // threads that were paused to restrict the number of active compilations.
     // Only unpause one at a time, to make sure we don't exceed the restriction.
     // Since threads are currently only paused for Ion compilations, this
     // strategy will eventually unpause all paused threads, regardless of how
     // many there are, since each thread we unpause will eventually finish and
     // end up back here.
-    if (HelperThread* other = HelperThreadState().highestPriorityPausedIonCompile()) {
+    if (HelperThread* other = HelperThreadState().highestPriorityPausedIonCompile(locked)) {
         MOZ_ASSERT(other->ionBuilder() && other->pause);
 
         // Only unpause the other thread if there isn't a higher priority
         // builder which this thread or another can start on.
-        jit::IonBuilder* builder = HelperThreadState().highestPriorityPendingIonCompile();
+        jit::IonBuilder* builder = HelperThreadState().highestPriorityPendingIonCompile(locked);
         if (!builder || IonBuilderHasHigherPriority(other->ionBuilder(), builder)) {
             other->pause = false;
 
             // Notify all paused threads, to make sure the one we just
             // unpaused wakes up.
-            HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE);
+            HelperThreadState().notifyAll(GlobalHelperThreadState::PAUSE, locked);
         }
     }
 }
 
 static HelperThread*
 CurrentHelperThread()
 {
     PRThread* prThread = PR_GetCurrentThread();
@@ -1516,21 +1477,20 @@ ExclusiveContext::addPendingOutOfMemory(
     // Keep in sync with recoverFromOutOfMemory.
     if (helperThread()->parseTask())
         helperThread()->parseTask()->outOfMemory = true;
 }
 
 void
 HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartParseTask());
+    MOZ_ASSERT(HelperThreadState().canStartParseTask(locked));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().parseWorklist().popCopy());
+    currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
     ParseTask* task = parseTask();
     task->cx->setHelperThread(this);
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               task->exclusiveContextGlobal->runtimeFromAnyThread());
         task->parse();
@@ -1538,74 +1498,73 @@ HelperThread::handleParseWorkload(AutoLo
 
     // The callback is invoked while we are still off the main thread.
     task->callback(task, task->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!HelperThreadState().parseFinishedList().append(task))
+        if (!HelperThreadState().parseFinishedList(locked).append(task))
             oomUnsafe.crash("handleParseWorkload");
     }
 
     currentTask.reset();
 
     // Notify the main thread in case it is waiting for the parse/emit to finish.
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
 HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartCompressionTask());
+    MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().compressionWorklist().popCopy());
+    currentTask.emplace(HelperThreadState().compressionWorklist(locked).popCopy());
     SourceCompressionTask* task = compressionTask();
     task->helperThread = this;
 
     {
         AutoUnlockHelperThreadState unlock(locked);
 
         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
         AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
 
         task->result = task->work();
     }
 
     task->helperThread = nullptr;
     currentTask.reset();
 
     // Notify the main thread in case it is waiting for the compression to finish.
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
 js::StartOffThreadCompression(ExclusiveContext* cx, SourceCompressionTask* task)
 {
     AutoLockHelperThreadState lock;
 
-    if (!HelperThreadState().compressionWorklist().append(task)) {
+    if (!HelperThreadState().compressionWorklist(lock).append(task)) {
         if (JSContext* maybecx = cx->maybeJSContext())
             ReportOutOfMemory(maybecx);
         return false;
     }
 
-    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
     return true;
 }
 
 bool
-GlobalHelperThreadState::compressionInProgress(SourceCompressionTask* task)
+GlobalHelperThreadState::compressionInProgress(SourceCompressionTask* task,
+                                               const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-    for (size_t i = 0; i < compressionWorklist().length(); i++) {
-        if (compressionWorklist()[i] == task)
+    for (size_t i = 0; i < compressionWorklist(lock).length(); i++) {
+        if (compressionWorklist(lock)[i] == task)
             return true;
     }
     for (size_t i = 0; i < threadCount; i++) {
         if (threads[i].compressionTask() == task)
             return true;
     }
     return false;
 }
@@ -1613,17 +1572,17 @@ GlobalHelperThreadState::compressionInPr
 bool
 SourceCompressionTask::complete()
 {
     if (!active())
         return true;
 
     {
         AutoLockHelperThreadState lock;
-        while (HelperThreadState().compressionInProgress(this))
+        while (HelperThreadState().compressionInProgress(this, lock))
             HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     }
 
     if (result == Success) {
         MOZ_ASSERT(resultString);
         ss->setCompressedSource(mozilla::Move(*resultString), ss->length());
     } else {
         if (result == OOM)
@@ -1632,49 +1591,48 @@ SourceCompressionTask::complete()
 
     ss = nullptr;
     MOZ_ASSERT(!active());
 
     return result != OOM;
 }
 
 SourceCompressionTask*
-GlobalHelperThreadState::compressionTaskForSource(ScriptSource* ss)
+GlobalHelperThreadState::compressionTaskForSource(ScriptSource* ss,
+                                                  const AutoLockHelperThreadState& lock)
 {
-    MOZ_ASSERT(isLocked());
-    for (size_t i = 0; i < compressionWorklist().length(); i++) {
-        SourceCompressionTask* task = compressionWorklist()[i];
+    for (size_t i = 0; i < compressionWorklist(lock).length(); i++) {
+        SourceCompressionTask* task = compressionWorklist(lock)[i];
         if (task->source() == ss)
             return task;
     }
     for (size_t i = 0; i < threadCount; i++) {
         SourceCompressionTask* task = threads[i].compressionTask();
         if (task && task->source() == ss)
             return task;
     }
     return nullptr;
 }
 
 void
 HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
 {
-    MOZ_ASSERT(HelperThreadState().isLocked());
-    MOZ_ASSERT(HelperThreadState().canStartGCHelperTask());
+    MOZ_ASSERT(HelperThreadState().canStartGCHelperTask(locked));
     MOZ_ASSERT(idle());
 
-    currentTask.emplace(HelperThreadState().gcHelperWorklist().popCopy());
+    currentTask.emplace(HelperThreadState().gcHelperWorklist(locked).popCopy());
     GCHelperState* task = gcHelperTask();
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->work();
     }
 
     currentTask.reset();
-    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
 HelperThread::threadLoop()
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     JS::AutoSuppressGCAnalysis nogc;
@@ -1697,44 +1655,44 @@ HelperThread::threadLoop()
 
         // Block until a task is available. Save the value of whether we are
         // going to do an Ion compile, in case the value returned by the method
         // changes.
         bool ionCompile = false;
         while (true) {
             if (terminate)
                 return;
-            if (HelperThreadState().canStartWasmCompile() ||
-                (ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority()) ||
-                HelperThreadState().canStartParseTask() ||
-                HelperThreadState().canStartCompressionTask() ||
-                HelperThreadState().canStartGCHelperTask() ||
-                HelperThreadState().canStartGCParallelTask())
+            if (HelperThreadState().canStartWasmCompile(lock) ||
+                (ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority(lock)) ||
+                HelperThreadState().canStartParseTask(lock) ||
+                HelperThreadState().canStartCompressionTask(lock) ||
+                HelperThreadState().canStartGCHelperTask(lock) ||
+                HelperThreadState().canStartGCParallelTask(lock))
             {
                 break;
             }
             HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
         }
 
         // Dispatch tasks, prioritizing wasm work.
-        if (HelperThreadState().canStartWasmCompile()) {
+        if (HelperThreadState().canStartWasmCompile(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_ASMJS);
             handleWasmWorkload(lock);
         } else if (ionCompile) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_ION);
             handleIonWorkload(lock);
-        } else if (HelperThreadState().canStartParseTask()) {
+        } else if (HelperThreadState().canStartParseTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_PARSE);
             handleParseWorkload(lock);
-        } else if (HelperThreadState().canStartCompressionTask()) {
+        } else if (HelperThreadState().canStartCompressionTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_COMPRESS);
             handleCompressionWorkload(lock);
-        } else if (HelperThreadState().canStartGCHelperTask()) {
+        } else if (HelperThreadState().canStartGCHelperTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCHELPER);
             handleGCHelperWorkload(lock);
-        } else if (HelperThreadState().canStartGCParallelTask()) {
+        } else if (HelperThreadState().canStartGCParallelTask(lock)) {
             js::oom::SetThreadType(js::oom::THREAD_TYPE_GCPARALLEL);
             handleGCParallelWorkload(lock);
         } else {
             MOZ_CRASH("No task to perform");
         }
     }
 }
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -116,119 +116,104 @@ class GlobalHelperThreadState
 
     bool ensureInitialized();
     void finish();
     void finishThreads();
 
     void lock();
     void unlock();
 
-#ifdef DEBUG
-    bool isLocked();
-#endif
-
     enum CondVar {
         // For notifying threads waiting for work that they may be able to make progress.
         CONSUMER,
 
         // For notifying threads doing work that they may be able to make progress.
         PRODUCER,
 
         // For notifying threads doing work which are paused that they may be
         // able to resume making progress.
         PAUSE
     };
 
     void wait(AutoLockHelperThreadState& locked, CondVar which,
               mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever());
-    void notifyAll(CondVar which);
-    void notifyOne(CondVar which);
+    void notifyAll(CondVar which, const AutoLockHelperThreadState&);
+    void notifyOne(CondVar which, const AutoLockHelperThreadState&);
 
     // Helper method for removing items from the vectors below while iterating over them.
     template <typename T>
     void remove(T& vector, size_t* index)
     {
         vector[(*index)--] = vector.back();
         vector.popBack();
     }
 
-    IonBuilderVector& ionWorklist() {
-        MOZ_ASSERT(isLocked());
+    IonBuilderVector& ionWorklist(const AutoLockHelperThreadState&) {
         return ionWorklist_;
     }
-    IonBuilderVector& ionFinishedList() {
-        MOZ_ASSERT(isLocked());
+    IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) {
         return ionFinishedList_;
     }
 
-    wasm::IonCompileTaskPtrVector& wasmWorklist() {
-        MOZ_ASSERT(isLocked());
+    wasm::IonCompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&) {
         return wasmWorklist_;
     }
-    wasm::IonCompileTaskPtrVector& wasmFinishedList() {
-        MOZ_ASSERT(isLocked());
+    wasm::IonCompileTaskPtrVector& wasmFinishedList(const AutoLockHelperThreadState&) {
         return wasmFinishedList_;
     }
 
-    ParseTaskVector& parseWorklist() {
-        MOZ_ASSERT(isLocked());
+    ParseTaskVector& parseWorklist(const AutoLockHelperThreadState&) {
         return parseWorklist_;
     }
-    ParseTaskVector& parseFinishedList() {
-        MOZ_ASSERT(isLocked());
+    ParseTaskVector& parseFinishedList(const AutoLockHelperThreadState&) {
         return parseFinishedList_;
     }
-    ParseTaskVector& parseWaitingOnGC() {
-        MOZ_ASSERT(isLocked());
+    ParseTaskVector& parseWaitingOnGC(const AutoLockHelperThreadState&) {
         return parseWaitingOnGC_;
     }
 
-    SourceCompressionTaskVector& compressionWorklist() {
-        MOZ_ASSERT(isLocked());
+    SourceCompressionTaskVector& compressionWorklist(const AutoLockHelperThreadState&) {
         return compressionWorklist_;
     }
 
-    GCHelperStateVector& gcHelperWorklist() {
-        MOZ_ASSERT(isLocked());
+    GCHelperStateVector& gcHelperWorklist(const AutoLockHelperThreadState&) {
         return gcHelperWorklist_;
     }
 
-    GCParallelTaskVector& gcParallelWorklist() {
-        MOZ_ASSERT(isLocked());
+    GCParallelTaskVector& gcParallelWorklist(const AutoLockHelperThreadState&) {
         return gcParallelWorklist_;
     }
 
-    bool canStartWasmCompile();
-    bool canStartIonCompile();
-    bool canStartParseTask();
-    bool canStartCompressionTask();
-    bool canStartGCHelperTask();
-    bool canStartGCParallelTask();
+    bool canStartWasmCompile(const AutoLockHelperThreadState& lock);
+    bool canStartIonCompile(const AutoLockHelperThreadState& lock);
+    bool canStartParseTask(const AutoLockHelperThreadState& lock);
+    bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
+    bool canStartGCHelperTask(const AutoLockHelperThreadState& lock);
+    bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
 
     // Unlike the methods above, the value returned by this method can change
     // over time, even if the helper thread state lock is held throughout.
-    bool pendingIonCompileHasSufficientPriority();
+    bool pendingIonCompileHasSufficientPriority(const AutoLockHelperThreadState& lock);
 
-    jit::IonBuilder* highestPriorityPendingIonCompile(bool remove = false);
-    HelperThread* lowestPriorityUnpausedIonCompileAtThreshold();
-    HelperThread* highestPriorityPausedIonCompile();
+    jit::IonBuilder* highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock,
+                                                      bool remove = false);
+    HelperThread* lowestPriorityUnpausedIonCompileAtThreshold(
+        const AutoLockHelperThreadState& lock);
+    HelperThread* highestPriorityPausedIonCompile(const AutoLockHelperThreadState& lock);
 
-    uint32_t harvestFailedWasmJobs() {
-        MOZ_ASSERT(isLocked());
+    uint32_t harvestFailedWasmJobs(const AutoLockHelperThreadState&) {
         uint32_t n = numWasmFailedJobs;
         numWasmFailedJobs = 0;
         return n;
     }
-    void noteWasmFailure() {
+    void noteWasmFailure(const AutoLockHelperThreadState&) {
         // Be mindful to signal the main thread after calling this function.
-        MOZ_ASSERT(isLocked());
         numWasmFailedJobs++;
     }
-    bool wasmFailed() {
-        MOZ_ASSERT(isLocked());
+    bool wasmFailed(const AutoLockHelperThreadState&) {
         return bool(numWasmFailedJobs);
     }
 
     JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
     void cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token);
 
     void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
                                    Handle<GlobalObject*> global,
@@ -239,35 +224,32 @@ class GlobalHelperThreadState
      * Number of wasm jobs that encountered failure for the active module.
      * Their parent is logically the main thread, and this number serves for harvesting.
      */
     uint32_t numWasmFailedJobs;
 
   public:
     JSScript* finishScriptParseTask(JSContext* cx, void* token);
     JSObject* finishModuleParseTask(JSContext* cx, void* token);
-    bool compressionInProgress(SourceCompressionTask* task);
-    SourceCompressionTask* compressionTaskForSource(ScriptSource* ss);
+    bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
+    SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
 
-    bool hasActiveThreads();
+    bool hasActiveThreads(const AutoLockHelperThreadState&);
     void waitForAllThreads();
 
     template <typename T>
     bool checkTaskThreadLimit(size_t maxThreads) const;
 
   private:
 
     /*
      * Lock protecting all mutable shared state accessed by helper threads, and
      * used by all condition variables.
      */
     js::Mutex helperLock;
-#ifdef DEBUG
-    mozilla::Atomic<PRThread*> lockOwner;
-#endif
 
     /* Condvars for threads waiting/notifying each other. */
     js::ConditionVariable consumerWakeup;
     js::ConditionVariable producerWakeup;
     js::ConditionVariable pauseWakeup;
 
     js::ConditionVariable& whichWakeup(CondVar which) {
         switch (which) {
@@ -454,57 +436,32 @@ class MOZ_RAII AutoLockHelperThreadState
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoLockHelperThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
       : Base(HelperThreadState().helperLock)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-
-#ifdef DEBUG
-        HelperThreadState().lockOwner = PR_GetCurrentThread();
-#endif
-    }
-
-    ~AutoLockHelperThreadState() {
-#ifdef DEBUG
-        HelperThreadState().lockOwner = nullptr;
-#endif
     }
 };
 
-class MOZ_RAII AutoUnlockHelperThreadState
+class MOZ_RAII AutoUnlockHelperThreadState : public UnlockGuard<Mutex>
 {
-    // This is only in a Maybe so that we can update the DEBUG-only lockOwner
-    // thread in the proper order.
-    mozilla::Maybe<UnlockGuard<Mutex>> unlocked;
+    using Base = UnlockGuard<Mutex>;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
 
     explicit AutoUnlockHelperThreadState(AutoLockHelperThreadState& locked
                                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : Base(locked)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-
-#ifdef DEBUG
-        HelperThreadState().lockOwner = nullptr;
-#endif
-
-        unlocked.emplace(locked);
-    }
-
-    ~AutoUnlockHelperThreadState() {
-        unlocked.reset();
-
-#ifdef DEBUG
-        HelperThreadState().lockOwner = PR_GetCurrentThread();
-#endif
     }
 };
 
 struct ParseTask
 {
     ParseTaskKind kind;
     ExclusiveContext* cx;
     OwningCompileOptions options;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -897,17 +897,16 @@ JSRuntime::assertCanLock(RuntimeLock whi
     // In the switch below, each case falls through to the one below it. None
     // of the runtime locks are reentrant, and when multiple locks are acquired
     // it must be done in the order below.
     switch (which) {
       case ExclusiveAccessLock:
         MOZ_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
         MOZ_FALLTHROUGH;
       case HelperThreadStateLock:
-        MOZ_ASSERT(!HelperThreadState().isLocked());
         MOZ_FALLTHROUGH;
       case GCLock:
         break;
       default:
         MOZ_CRASH();
     }
 }