Bug 1532803 - Common interface for JSThreadPool tasks r=jonco
authorKristen Wright <kwright@mozilla.com>
Tue, 16 Apr 2019 16:43:43 +0000
changeset 470544 aacb0c9ecddbc2e76361a843faf2d5a0b334c298
parent 470543 1ed5609db4afed63c7b9dd79e2426e907fd19458
child 470545 c473ba78f2e24127b651c1293319f540cffdefb8
push id35908
push useraciure@mozilla.com
push dateWed, 24 Apr 2019 04:28:40 +0000
treeherdermozilla-central@c9f0730a57a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1532803
milestone68.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 1532803 - Common interface for JSThreadPool tasks r=jonco Added 'RunnableTask' struct to Utility.h to unify HelperThread task types in a way that can be exposed to XPCOM thread pools. Re-implemented tasks within native HelperThreads using their runnableTask method. Differential Revision: https://phabricator.services.mozilla.com/D24921
js/public/Utility.h
js/src/gc/GCParallelTask.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/JSScript.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmModule.cpp
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -72,16 +72,25 @@ enum ThreadType {
   THREAD_TYPE_GCPARALLEL,    // 7
   THREAD_TYPE_PROMISE_TASK,  // 8
   THREAD_TYPE_ION_FREE,      // 9
   THREAD_TYPE_WASM_TIER2,    // 10
   THREAD_TYPE_WORKER,        // 11
   THREAD_TYPE_MAX            // Used to check shell function arguments
 };
 
+/*
+ * Threads need a universal way to dispatch from xpcom thread pools,
+ * so having objects inherit from this struct enables
+ * mozilla::HelperThreadPool's runnable handler to call runTask() on each type.
+ */
+struct RunnableTask {
+  virtual void runTask() = 0;
+};
+
 namespace oom {
 
 /*
  * Theads are tagged only in certain debug contexts.  Notably, to make testing
  * OOM in certain helper threads more effective, we allow restricting the OOM
  * testing to a certain helper thread type. This allows us to fail e.g. in
  * off-thread script parsing without causing an OOM in the active thread first.
  *
--- a/js/src/gc/GCParallelTask.h
+++ b/js/src/gc/GCParallelTask.h
@@ -17,17 +17,17 @@ namespace js {
 class AutoLockHelperThreadState;
 
 // A generic task used to dispatch work to the helper thread system.
 // Users supply a function pointer to call.
 //
 // Note that we don't use virtual functions here because destructors can write
 // the vtable pointer on entry, which can causes races if synchronization
 // happens there.
-class GCParallelTask {
+class GCParallelTask : public RunnableTask {
  public:
   using TaskFunc = void (*)(GCParallelTask*);
 
  private:
   JSRuntime* const runtime_;
   TaskFunc func_;
 
   // The state of the parallel computation.
@@ -56,17 +56,17 @@ class GCParallelTask {
       : runtime_(other.runtime_),
         func_(other.func_),
         state_(other.state_),
         duration_(nullptr),
         cancel_(false) {}
 
   // Derived classes must override this to ensure that join() gets called
   // before members get destructed.
-  ~GCParallelTask();
+  virtual ~GCParallelTask();
 
   JSRuntime* runtime() { return runtime_; }
 
   // Time spent in the most recent invocation of this task.
   mozilla::TimeDuration duration() const { return duration_; }
 
   // The simple interface to a parallel task works exactly like pthreads.
   MOZ_MUST_USE bool start();
@@ -92,16 +92,18 @@ class GCParallelTask {
   }
 
   // Check if a task is running and has not called setFinishing().
   bool isRunningWithLockHeld(const AutoLockHelperThreadState& lock) const {
     return isDispatched(lock);
   }
   bool isRunning() const;
 
+  void runTask() override { func_(this); }
+
  private:
   void assertNotStarted() const {
     // Don't lock here because that adds extra synchronization in debug
     // builds that may hide bugs. There's no race if the assertion passes.
     MOZ_ASSERT(state_ == State::NotStarted);
   }
   bool isNotStarted(const AutoLockHelperThreadState& lock) const {
     return state_ == State::NotStarted;
@@ -120,18 +122,16 @@ class GCParallelTask {
     MOZ_ASSERT(state_ == State::Dispatched || state_ == State::Finishing);
     state_ = State::Finished;
   }
   void setNotStarted(const AutoLockHelperThreadState& lock) {
     MOZ_ASSERT(state_ == State::Finished);
     state_ = State::NotStarted;
   }
 
-  void runTask() { func_(this); }
-
  protected:
   // Can be called to indicate that although the task is still
   // running, it is about to finish.
   void setFinishing(const AutoLockHelperThreadState& lock) {
     MOZ_ASSERT(state_ == State::NotStarted || state_ == State::Dispatched);
     if (state_ == State::Dispatched) {
       state_ = State::Finishing;
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1119,16 +1119,30 @@ AbortReasonOr<Ok> IonBuilder::buildInlin
 
   if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) {
     return abort(AbortReason::PreliminaryObjects);
   }
 
   return Ok();
 }
 
+void IonBuilder::runTask() {
+  // This is the entry point when ion compiles are run offthread.
+  JSRuntime* rt = script()->runtimeFromAnyThread();
+
+  TraceLoggerThread* logger = TraceLoggerForCurrentThread();
+  TraceLoggerEvent event(TraceLogger_AnnotateScripts, script());
+  AutoTraceLog logScript(logger, event);
+  AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
+
+  jit::JitContext jctx(jit::CompileRuntime::get(rt),
+                       jit::CompileRealm::get(script()->realm()), &alloc());
+  setBackgroundCodegen(jit::CompileBackEnd(this));
+}
+
 void IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition* param) {
   MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
 
   TemporaryTypeSet* types = param->resultTypeSet();
   MDefinition* actual = ensureDefiniteType(param, types->getKnownMIRType());
   if (actual == param) {
     return;
   }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -38,17 +38,18 @@ enum class InlinableNative : uint16_t;
 // Records information about a baseline frame for compilation that is stable
 // when later used off thread.
 BaselineFrameInspector* NewBaselineFrameInspector(TempAllocator* temp,
                                                   BaselineFrame* frame);
 
 using CallTargets = Vector<JSFunction*, 6, JitAllocPolicy>;
 
 class IonBuilder : public MIRGenerator,
-                   public mozilla::LinkedListElement<IonBuilder> {
+                   public mozilla::LinkedListElement<IonBuilder>,
+                   public RunnableTask {
  public:
   IonBuilder(JSContext* analysisContext, CompileRealm* realm,
              const JitCompileOptions& options, TempAllocator* temp,
              MIRGraph* graph, CompilerConstraintList* constraints,
              BaselineInspector* inspector, CompileInfo* info,
              const OptimizationInfo* optimizationInfo,
              BaselineFrameInspector* baselineFrame, size_t inliningDepth = 0,
              uint32_t loopDepth = 0);
@@ -61,16 +62,18 @@ class IonBuilder : public MIRGenerator,
                                 MResumePoint* callerResumePoint,
                                 CallInfo& callInfo);
 
   mozilla::GenericErrorResult<AbortReason> abort(AbortReason r);
   mozilla::GenericErrorResult<AbortReason> abort(AbortReason r,
                                                  const char* message, ...)
       MOZ_FORMAT_PRINTF(3, 4);
 
+  void runTask() override;
+
  private:
   AbortReasonOr<Ok> traverseBytecode();
   AbortReasonOr<Ok> processIterators();
   AbortReasonOr<Ok> inspectOpcode(JSOp op);
   uint32_t readIndex(jsbytecode* pc);
   JSAtom* readAtom(jsbytecode* pc);
 
   void trackActionableAbort(const char* message);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -414,16 +414,23 @@ bool js::HasOffThreadIonCompile(Realm* r
     }
     builder = builder->getNext();
   }
 
   return false;
 }
 #endif
 
+struct MOZ_RAII AutoSetContextRuntime {
+  explicit AutoSetContextRuntime(JSRuntime* rt) {
+    TlsContext.get()->setRuntime(rt);
+  }
+  ~AutoSetContextRuntime() { TlsContext.get()->setRuntime(nullptr); }
+};
+
 static const JSClass parseTaskGlobalClass = {"internal-parse-task-global",
                                              JSCLASS_GLOBAL_FLAGS,
                                              &JS::DefaultGlobalClassOps};
 
 ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx,
                      JS::OffThreadCompileCallback callback, void* callbackData)
     : kind(kind),
       options(cx),
@@ -475,16 +482,37 @@ void ParseTask::trace(JSTracer* trc) {
 }
 
 size_t ParseTask::sizeOfExcludingThis(
     mozilla::MallocSizeOf mallocSizeOf) const {
   return options.sizeOfExcludingThis(mallocSizeOf) +
          errors.sizeOfExcludingThis(mallocSizeOf);
 }
 
+void ParseTask::runTask() {
+  JSContext* cx = TlsContext.get();
+  JSRuntime* runtime = parseGlobal->runtimeFromAnyThread();
+
+  AutoSetContextRuntime ascr(runtime);
+
+  Zone* zone = parseGlobal->zoneFromAnyThread();
+  zone->setHelperThreadOwnerContext(cx);
+  auto resetOwnerContext = mozilla::MakeScopeExit(
+      [&] { zone->setHelperThreadOwnerContext(nullptr); });
+
+  AutoRealm ar(cx, parseGlobal);
+
+  parse(cx);
+
+  MOZ_ASSERT(cx->tempLifoAlloc().isEmpty());
+  cx->tempLifoAlloc().freeAll();
+  cx->frontendCollectionPool().purge();
+  cx->atomsZoneFreeLists().clear();
+}
+
 ScriptParseTask::ScriptParseTask(JSContext* cx,
                                  JS::SourceText<char16_t>& srcBuf,
                                  JS::OffThreadCompileCallback callback,
                                  void* callbackData)
     : ParseTask(ParseTaskKind::Script, cx, callback, callbackData),
       data(std::move(srcBuf)) {}
 
 void ScriptParseTask::parse(JSContext* cx) {
@@ -1203,23 +1231,16 @@ void GlobalHelperThreadState::triggerFre
 
   AutoLockHelperThreadState lock;
   for (auto& thread : *threads) {
     thread.shouldFreeUnusedMemory = true;
   }
   notifyAll(PRODUCER, lock);
 }
 
-struct MOZ_RAII AutoSetContextRuntime {
-  explicit AutoSetContextRuntime(JSRuntime* rt) {
-    TlsContext.get()->setRuntime(rt);
-  }
-  ~AutoSetContextRuntime() { TlsContext.get()->setRuntime(nullptr); }
-};
-
 static inline bool IsHelperThreadSimulatingOOM(js::ThreadType threadType) {
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   return js::oom::simulator.targetThread() == threadType;
 #else
   return false;
 #endif
 }
 
@@ -1948,17 +1969,17 @@ void HelperThread::handleWasmWorkload(Au
   MOZ_ASSERT(idle());
 
   currentTask.emplace(
       HelperThreadState().wasmWorklist(locked, mode).popCopyFront());
 
   wasm::CompileTask* task = wasmTask();
   {
     AutoUnlockHelperThreadState unlock(locked);
-    wasm::ExecuteCompileTaskFromHelperThread(task);
+    task->runTask();
   }
 
   currentTask.reset();
 
   // Since currentTask is only now reset(), this could be the last active thread
   // waitForAllThreads() is waiting for. No one else should be waiting, though.
   HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
@@ -1969,17 +1990,17 @@ void HelperThread::handleWasmTier2Genera
   MOZ_ASSERT(idle());
 
   currentTask.emplace(
       HelperThreadState().wasmTier2GeneratorWorklist(locked).popCopy());
 
   wasm::Tier2GeneratorTask* task = wasmTier2GeneratorTask();
   {
     AutoUnlockHelperThreadState unlock(locked);
-    task->execute();
+    task->runTask();
   }
 
   currentTask.reset();
   js_delete(task);
 
   // During shutdown the main thread will wait for any ongoing (cancelled)
   // tier-2 generation to shut down normally.  To do so, it waits on the
   // CONSUMER condition for the count of finished generators to rise.
@@ -1993,57 +2014,47 @@ void HelperThread::handlePromiseHelperTa
   MOZ_ASSERT(idle());
 
   PromiseHelperTask* task =
       HelperThreadState().promiseHelperTasks(locked).popCopy();
   currentTask.emplace(task);
 
   {
     AutoUnlockHelperThreadState unlock(locked);
-    task->execute();
-    task->dispatchResolveAndDestroy();
+    task->runTask();
   }
 
   currentTask.reset();
 
   // Since currentTask is only now reset(), this could be the last active thread
   // waitForAllThreads() is waiting for. No one else should be waiting, though.
   HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked) {
   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(locked);
 
   // The build is taken by this thread. Unfreeze the LifoAlloc to allow
   // mutations.
   builder->alloc().lifoAlloc()->setReadWrite();
 
   currentTask.emplace(builder);
 
   JSRuntime* rt = builder->script()->runtimeFromAnyThread();
 
   {
     AutoUnlockHelperThreadState unlock(locked);
-
-    TraceLoggerThread* logger = TraceLoggerForCurrentThread();
-    TraceLoggerEvent event(TraceLogger_AnnotateScripts, builder->script());
-    AutoTraceLog logScript(logger, event);
-    AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
-
     AutoSetContextRuntime ascr(rt);
-    jit::JitContext jctx(jit::CompileRuntime::get(rt),
-                         jit::CompileRealm::get(builder->script()->realm()),
-                         &builder->alloc());
-    builder->setBackgroundCodegen(jit::CompileBackEnd(builder));
+
+    builder->runTask();
   }
 
   FinishOffThreadIonCompile(builder, locked);
 
   // Ping the main thread so that the compiled code can be incorporated at the
   // next interrupt callback.
   //
   // This must happen before the current task is reset. DestroyContext
@@ -2119,43 +2130,25 @@ void JSContext::addPendingOutOfMemory() 
 
 void HelperThread::handleParseWorkload(AutoLockHelperThreadState& locked) {
   MOZ_ASSERT(HelperThreadState().canStartParseTask(locked));
   MOZ_ASSERT(idle());
 
   currentTask.emplace(HelperThreadState().parseWorklist(locked).popCopy());
   ParseTask* task = parseTask();
 
+#ifdef DEBUG
   JSRuntime* runtime = task->parseGlobal->runtimeFromAnyThread();
-
-#ifdef DEBUG
   runtime->incOffThreadParsesRunning();
 #endif
 
   {
     AutoUnlockHelperThreadState unlock(locked);
-    AutoSetContextRuntime ascr(runtime);
-
-    JSContext* cx = TlsContext.get();
-
-    Zone* zone = task->parseGlobal->zoneFromAnyThread();
-    zone->setHelperThreadOwnerContext(cx);
-    auto resetOwnerContext = mozilla::MakeScopeExit(
-        [&] { zone->setHelperThreadOwnerContext(nullptr); });
-
-    AutoRealm ar(cx, task->parseGlobal);
-
-    task->parse(cx);
-
-    MOZ_ASSERT(cx->tempLifoAlloc().isEmpty());
-    cx->tempLifoAlloc().freeAll();
-    cx->frontendCollectionPool().purge();
-    cx->atomsZoneFreeLists().clear();
+    task->runTask();
   }
-
   // The callback is invoked while we are still off thread.
   task->callback(task, task->callbackData);
 
   // FinishOffThreadScript will need to be called on the script to
   // migrate it into the correct compartment.
   HelperThreadState().parseFinishedList(locked).insertBack(task);
 
 #ifdef DEBUG
@@ -2182,17 +2175,17 @@ void HelperThread::handleCompressionWork
   }
 
   {
     AutoUnlockHelperThreadState unlock(locked);
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_CompressSource);
 
-    task->work();
+    task->runTask();
   }
 
   {
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!HelperThreadState().compressionFinishedList(locked).append(
             std::move(task))) {
       oomUnsafe.crash("handleCompressionWorkload");
     }
@@ -2293,16 +2286,21 @@ void js::RunPendingSourceCompressions(JS
   AttachFinishedCompressions(runtime, lock);
 }
 
 void PromiseHelperTask::executeAndResolveAndDestroy(JSContext* cx) {
   execute();
   run(cx, JS::Dispatchable::NotShuttingDown);
 }
 
+void PromiseHelperTask::runTask() {
+  execute();
+  dispatchResolveAndDestroy();
+}
+
 bool js::StartOffThreadPromiseHelperTask(JSContext* cx,
                                          UniquePtr<PromiseHelperTask> task) {
   // Execute synchronously if there are no helper threads.
   if (!CanUseExtraThreads()) {
     task.release()->executeAndResolveAndDestroy(cx);
     return true;
   }
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -58,20 +58,19 @@ enum class ParseTaskKind {
   MultiScriptsDecode
 };
 
 namespace wasm {
 
 struct CompileTask;
 typedef Fifo<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrFifo;
 
-struct Tier2GeneratorTask {
+struct Tier2GeneratorTask : public RunnableTask {
   virtual ~Tier2GeneratorTask() = default;
   virtual void cancel() = 0;
-  virtual void execute() = 0;
 };
 
 typedef UniquePtr<Tier2GeneratorTask> UniqueTier2GeneratorTask;
 typedef Vector<Tier2GeneratorTask*, 0, SystemAllocPolicy>
     Tier2GeneratorTaskPtrVector;
 
 }  // namespace wasm
 
@@ -689,17 +688,18 @@ class MOZ_RAII AutoUnlockHelperThreadSta
   explicit AutoUnlockHelperThreadState(
       AutoLockHelperThreadState& locked MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : Base(locked) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
 };
 
 struct ParseTask : public mozilla::LinkedListElement<ParseTask>,
-                   public JS::OffThreadToken {
+                   public JS::OffThreadToken,
+                   public RunnableTask {
   ParseTaskKind kind;
   JS::OwningCompileOptions options;
 
   // The global object to use while parsing.
   JSObject* parseGlobal;
 
   // Callback invoked off thread when the parse finishes.
   JS::OffThreadCompileCallback callback;
@@ -734,16 +734,18 @@ struct ParseTask : public mozilla::Linke
   }
 
   void trace(JSTracer* trc);
 
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
   }
+
+  void runTask() override;
 };
 
 struct ScriptParseTask : public ParseTask {
   JS::SourceText<char16_t> data;
 
   ScriptParseTask(JSContext* cx, JS::SourceText<char16_t>& srcBuf,
                   JS::OffThreadCompileCallback callback, void* callbackData);
   void parse(JSContext* cx) override;
@@ -796,17 +798,17 @@ extern bool OffThreadParsingMustWaitForG
 //
 // To this end, compression tasks are heap allocated and enqueued in a pending
 // list by ScriptSource::setSourceCopy. When a major GC occurs, we schedule
 // pending compression tasks and move the ones that are ready to be compressed
 // to the worklist. Currently, a compression task is considered ready 2 major
 // GCs after being enqueued. Completed tasks are handled during the sweeping
 // phase by AttachCompressedSourcesTask, which runs in parallel with other GC
 // sweeping tasks.
-class SourceCompressionTask {
+class SourceCompressionTask : public RunnableTask {
   friend struct HelperThread;
   friend class ScriptSource;
 
   // The runtime that the ScriptSource is associated with, in the sense that
   // it uses the runtime's immutable string cache.
   JSRuntime* runtime_;
 
   // The major GC number of the runtime when the task was enqueued.
@@ -822,31 +824,32 @@ class SourceCompressionTask {
   mozilla::Maybe<SharedImmutableString> resultString_;
 
  public:
   // The majorGCNumber is used for scheduling tasks.
   SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
       : runtime_(rt),
         majorGCNumber_(rt->gc.majorGCCount()),
         sourceHolder_(source) {}
+  virtual ~SourceCompressionTask() {}
 
   bool runtimeMatches(JSRuntime* runtime) const { return runtime == runtime_; }
   bool shouldStart() const {
     // We wait 2 major GCs to start compressing, in order to avoid
     // immediate compression.
     return runtime_->gc.majorGCCount() > majorGCNumber_ + 1;
   }
 
   bool shouldCancel() const {
     // If the refcount is exactly 1, then nothing else is holding on to the
     // ScriptSource, so no reason to compress it and we should cancel the task.
     return sourceHolder_.get()->refs == 1;
   }
 
-  void work();
+  void runTask() override;
   void complete();
 
  private:
   struct PerformTaskWork;
   friend struct PerformTaskWork;
 
   // The work algorithm, aware whether it's compressing one-byte UTF-8 source
   // text or UTF-16, for CharT either Utf8Unit or char16_t.  Invoked by
@@ -858,26 +861,27 @@ class SourceCompressionTask {
 // A PromiseHelperTask is an OffThreadPromiseTask that executes a single job on
 // a helper thread. Call js::StartOffThreadPromiseHelperTask to submit a
 // PromiseHelperTask for execution.
 //
 // Concrete subclasses must implement execute and OffThreadPromiseTask::resolve.
 // The helper thread will call execute() to do the main work. Then, the thread
 // of the JSContext used to create the PromiseHelperTask will call resolve() to
 // resolve promise according to those results.
-struct PromiseHelperTask : OffThreadPromiseTask {
+struct PromiseHelperTask : OffThreadPromiseTask, public RunnableTask {
   PromiseHelperTask(JSContext* cx, Handle<PromiseObject*> promise)
       : OffThreadPromiseTask(cx, promise) {}
 
   // To be called on a helper thread and implemented by the derived class.
   virtual void execute() = 0;
 
   // May be called in the absence of helper threads or off-thread promise
   // support to synchronously execute and resolve a PromiseTask.
   //
   // Warning: After this function returns, 'this' can be deleted at any time, so
   // the caller must immediately return from the stream callback.
   void executeAndResolveAndDestroy(JSContext* cx);
+  void runTask() override;
 };
 
 } /* namespace js */
 
 #endif /* vm_HelperThreads_h */
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2333,17 +2333,17 @@ struct SourceCompressionTask::PerformTas
   }
 };
 
 void ScriptSource::performTaskWork(SourceCompressionTask* task) {
   MOZ_ASSERT(hasUncompressedSource());
   data.match(SourceCompressionTask::PerformTaskWork(task));
 }
 
-void SourceCompressionTask::work() {
+void SourceCompressionTask::runTask() {
   if (shouldCancel()) {
     return;
   }
 
   ScriptSource* source = sourceHolder_.get();
   MOZ_ASSERT(source->hasUncompressedSource());
 
   source->performTaskWork(this);
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1237,16 +1237,18 @@ bool ModuleGenerator::finishTier2(const 
     // Introduce an artificial delay when testing wasmDelayTier2, since we
     // want to exercise both tier1 and tier2 code in this case.
     std::this_thread::sleep_for(std::chrono::milliseconds(500));
   }
 
   return module.finishTier2(*linkData_, std::move(codeTier));
 }
 
+void CompileTask::runTask() { ExecuteCompileTaskFromHelperThread(this); }
+
 size_t CompiledCode::sizeOfExcludingThis(
     mozilla::MallocSizeOf mallocSizeOf) const {
   size_t trapSitesSize = 0;
   for (const TrapSiteVector& vec : trapSites) {
     trapSitesSize += vec.sizeOfExcludingThis(mallocSizeOf);
   }
 
   return bytes.sizeOfExcludingThis(mallocSizeOf) +
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -106,28 +106,32 @@ struct CompileTaskState {
   }
 };
 
 typedef ExclusiveWaitableData<CompileTaskState> ExclusiveCompileTaskState;
 
 // A CompileTask holds a batch of input functions that are to be compiled on a
 // helper thread as well as, eventually, the results of compilation.
 
-struct CompileTask {
+struct CompileTask : public RunnableTask {
   const ModuleEnvironment& env;
   ExclusiveCompileTaskState& state;
   LifoAlloc lifo;
   FuncCompileInputVector inputs;
   CompiledCode output;
 
   CompileTask(const ModuleEnvironment& env, ExclusiveCompileTaskState& state,
               size_t defaultChunkSize)
       : env(env), state(state), lifo(defaultChunkSize) {}
 
+  virtual ~CompileTask(){};
+
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+  void runTask() override;
 };
 
 // A ModuleGenerator encapsulates the creation of a wasm module. During the
 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
 // and destroyed to compile the individual function bodies. After generating all
 // functions, ModuleGenerator::finish() must be called to complete the
 // compilation and extract the resulting wasm module.
 
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -57,17 +57,17 @@ class Module::Tier2GeneratorTaskImpl : p
 
   ~Tier2GeneratorTaskImpl() override {
     module_->tier2Listener_ = nullptr;
     module_->testingTier2Active_ = false;
   }
 
   void cancel() override { cancelled_ = true; }
 
-  void execute() override {
+  void runTask() override {
     CompileTier2(*compileArgs_, bytecode_->bytes, *module_, &cancelled_);
   }
 };
 
 Module::~Module() {
   // Note: Modules can be destroyed on any thread.
   MOZ_ASSERT(!tier2Listener_);
   MOZ_ASSERT(!testingTier2Active_);