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 307657 1bc0a14de00a86e2a198e40c1eb7a75442d60ce5
parent 307656 573529c879adbdfa560d00b70528e522d32bee97
child 307658 41096329fbed09dd0b344483f896068cc8c802eb
push id20188
push usercbook@mozilla.com
push dateTue, 02 Aug 2016 15:14:32 +0000
treeherderfx-team@cbdc4af95b6a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1290156
milestone50.0a1
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();
     }
 }