Bug 1720906 - Give GCParallelTask a PhaseKind and remove duplication in GCRuntime::joinTask r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 19 Jul 2021 08:22:43 +0000
changeset 585926 e7e916470a4aafc5005ae2a60bab63c56466770d
parent 585925 3b1f9f3413f4723951562044ad69bddb3b992418
child 585927 ad93176ef80e03f18d84d41fdaab648d68f679af
push id146474
push userjcoppeard@mozilla.com
push dateMon, 19 Jul 2021 08:25:10 +0000
treeherderautoland@e7e916470a4a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1720906
milestone92.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 1720906 - Give GCParallelTask a PhaseKind and remove duplication in GCRuntime::joinTask r=sfink Differential Revision: https://phabricator.services.mozilla.com/D120078
js/src/gc/Allocator.cpp
js/src/gc/GC.cpp
js/src/gc/GCParallelTask.cpp
js/src/gc/GCParallelTask.h
js/src/gc/GCRuntime.h
js/src/gc/Nursery.cpp
js/src/gc/ParallelWork.h
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -843,19 +843,21 @@ TenuredChunk* GCRuntime::pickChunk(AutoL
   MOZ_ASSERT(!availableChunks(lock).contains(chunk));
 
   availableChunks(lock).push(chunk);
 
   return chunk;
 }
 
 BackgroundAllocTask::BackgroundAllocTask(GCRuntime* gc, ChunkPool& pool)
-    : GCParallelTask(gc),
+    : GCParallelTask(gc, gcstats::PhaseKind::NONE),
       chunkPool_(pool),
-      enabled_(CanUseExtraThreads() && GetCPUCount() >= 2) {}
+      enabled_(CanUseExtraThreads() && GetCPUCount() >= 2) {
+  // This can occur outside GCs so doesn't have a stats phase.
+}
 
 void BackgroundAllocTask::run(AutoLockHelperThreadState& lock) {
   AutoUnlockHelperThreadState unlock(lock);
 
   TraceLoggerThread* logger = TraceLoggerForCurrentThread();
   AutoTraceLog logAllocation(logger, TraceLogger_GCAllocation);
 
   AutoLockGC gcLock(gc);
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -3576,16 +3576,19 @@ void GCRuntime::startDecommit() {
   if (sweepOnBackgroundThread) {
     decommitTask.start();
     return;
   }
 
   decommitTask.runFromMainThread();
 }
 
+BackgroundDecommitTask::BackgroundDecommitTask(GCRuntime* gc)
+    : GCParallelTask(gc, gcstats::PhaseKind::DECOMMIT) {}
+
 void js::gc::BackgroundDecommitTask::run(AutoLockHelperThreadState& lock) {
   {
     AutoUnlockHelperThreadState unlock(lock);
 
     ChunkPool emptyChunksToFree;
     {
       AutoLockGC gcLock(gc);
 
@@ -3744,16 +3747,19 @@ void GCRuntime::queueZonesAndStartBackgr
     }
   }
   if (!sweepOnBackgroundThread) {
     sweepTask.join();
     sweepTask.runFromMainThread();
   }
 }
 
+BackgroundSweepTask::BackgroundSweepTask(GCRuntime* gc)
+    : GCParallelTask(gc, gcstats::PhaseKind::SWEEP) {}
+
 void BackgroundSweepTask::run(AutoLockHelperThreadState& lock) {
   AutoTraceLog logSweeping(TraceLoggerForCurrentThread(),
                            TraceLogger_GCSweeping);
 
   gc->sweepFromBackgroundThread(lock);
 }
 
 void GCRuntime::sweepFromBackgroundThread(AutoLockHelperThreadState& lock) {
@@ -3809,16 +3815,21 @@ void GCRuntime::queueBuffersForFreeAfter
   std::swap(buffersToFreeAfterMinorGC.ref(), buffers);
 }
 
 void GCRuntime::startBackgroundFree() {
   AutoLockHelperThreadState lock;
   freeTask.startOrRunIfIdle(lock);
 }
 
+BackgroundFreeTask::BackgroundFreeTask(GCRuntime* gc)
+    : GCParallelTask(gc, gcstats::PhaseKind::NONE) {
+  // This can occur outside GCs so doesn't have a stats phase.
+}
+
 void BackgroundFreeTask::run(AutoLockHelperThreadState& lock) {
   AutoTraceLog logFreeing(TraceLoggerForCurrentThread(), TraceLogger_GCFree);
 
   gc->freeFromBackgroundThread(lock);
 }
 
 void GCRuntime::freeFromBackgroundThread(AutoLockHelperThreadState& lock) {
   do {
@@ -4017,27 +4028,26 @@ void ArenaLists::checkEmptyArenaList(All
   MOZ_ASSERT(arenaList(kind).isEmpty());
 }
 
 class MOZ_RAII AutoRunParallelTask : public GCParallelTask {
   // This class takes a pointer to a member function of GCRuntime.
   using TaskFunc = JS_MEMBER_FN_PTR_TYPE(GCRuntime, void);
 
   TaskFunc func_;
-  gcstats::PhaseKind phase_;
   AutoLockHelperThreadState& lock_;
 
  public:
   AutoRunParallelTask(GCRuntime* gc, TaskFunc func, gcstats::PhaseKind phase,
                       AutoLockHelperThreadState& lock)
-      : GCParallelTask(gc), func_(func), phase_(phase), lock_(lock) {
-    gc->startTask(*this, phase_, lock_);
-  }
-
-  ~AutoRunParallelTask() { gc->joinTask(*this, phase_, lock_); }
+      : GCParallelTask(gc, phase), func_(func), lock_(lock) {
+    gc->startTask(*this, lock_);
+  }
+
+  ~AutoRunParallelTask() { gc->joinTask(*this, lock_); }
 
   void run(AutoLockHelperThreadState& lock) override {
     AutoUnlockHelperThreadState unlock(lock);
 
     // The hazard analysis can't tell what the call to func_ will do but it's
     // not allowed to GC.
     JS::AutoSuppressGCAnalysis nogc;
 
@@ -4447,16 +4457,19 @@ bool GCRuntime::beginPreparePhase(JS::GC
   if (!IsShutdownReason(reason) && reason != JS::GCReason::ROOTS_REMOVED &&
       reason != JS::GCReason::XPCONNECT_SHUTDOWN) {
     StartHandlingCompressionsOnGC(rt);
   }
 
   return true;
 }
 
+BackgroundUnmarkTask::BackgroundUnmarkTask(GCRuntime* gc)
+    : GCParallelTask(gc, gcstats::PhaseKind::UNMARK) {}
+
 void BackgroundUnmarkTask::initZones() {
   MOZ_ASSERT(isIdle());
   MOZ_ASSERT(zones.empty());
   MOZ_ASSERT(!isCancelled());
 
   // We can't safely iterate the zones vector from another thread so we copy the
   // zones to be collected into another vector.
   AutoEnterOOMUnsafeRegion oomUnsafe;
@@ -5022,17 +5035,17 @@ void GCRuntime::getNextSweepGroup() {
   }
 
   for (Zone* zone = currentSweepGroup; zone; zone = zone->nextNodeInGroup()) {
     MOZ_ASSERT(zone->isGCMarkingBlackOnly());
     MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
   }
 
   if (abortSweepAfterCurrentGroup) {
-    joinTask(markTask, gcstats::PhaseKind::SWEEP_MARK);
+    markTask.join();
 
     // Abort collection of subsequent sweep groups.
     for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       MOZ_ASSERT(!zone->gcNextGraphComponent);
       zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
       zone->arenas.unmarkPreMarkedFreeCells();
       zone->arenas.mergeNewArenasInMarkPhase();
       zone->gcGrayRoots().Clear();
@@ -5423,17 +5436,19 @@ class ImmediateSweepWeakCacheTask : publ
   Zone* zone;
   JS::detail::WeakCacheBase& cache;
 
   ImmediateSweepWeakCacheTask(const ImmediateSweepWeakCacheTask&) = delete;
 
  public:
   ImmediateSweepWeakCacheTask(GCRuntime* gc, Zone* zone,
                               JS::detail::WeakCacheBase& wc)
-      : GCParallelTask(gc), zone(zone), cache(wc) {}
+      : GCParallelTask(gc, gcstats::PhaseKind::SWEEP_WEAK_CACHES),
+        zone(zone),
+        cache(wc) {}
 
   ImmediateSweepWeakCacheTask(ImmediateSweepWeakCacheTask&& other)
       : GCParallelTask(std::move(other)),
         zone(other.zone),
         cache(other.cache) {}
 
   void run(AutoLockHelperThreadState& lock) override {
     AutoUnlockHelperThreadState unlock(lock);
@@ -5531,56 +5546,32 @@ void GCRuntime::sweepFinalizationRegistr
   gcstats::AutoPhase ap2(stats(),
                          gcstats::PhaseKind::SWEEP_FINALIZATION_REGISTRIES);
   AutoLockStoreBuffer lock(&storeBuffer());
   for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     sweepFinalizationRegistries(zone);
   }
 }
 
-void GCRuntime::startTask(GCParallelTask& task, gcstats::PhaseKind phase,
+void GCRuntime::startTask(GCParallelTask& task,
                           AutoLockHelperThreadState& lock) {
   if (!CanUseExtraThreads()) {
     AutoUnlockHelperThreadState unlock(lock);
     task.runFromMainThread();
-    stats().recordParallelPhase(phase, task.duration());
+    stats().recordParallelPhase(task.phaseKind, task.duration());
     return;
   }
 
   task.startWithLockHeld(lock);
 }
 
-void GCRuntime::joinTask(GCParallelTask& task, gcstats::PhaseKind phase,
+void GCRuntime::joinTask(GCParallelTask& task,
                          AutoLockHelperThreadState& lock) {
-  // This is similar to GCParallelTask::joinWithLockHeld but handles recording
-  // execution and wait time.
-
-  if (task.isIdle(lock)) {
-    return;
-  }
-
-  if (task.isDispatched(lock)) {
-    // If the task was dispatched but has not yet started then cancel the task
-    // and run it from the main thread. This stops us from blocking here when
-    // the helper threads are busy with other tasks.
-    task.cancelDispatchedTask(lock);
-    AutoUnlockHelperThreadState unlock(lock);
-    task.runFromMainThread();
-  } else {
-    // Otherwise wait for the task to complete.
-    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::JOIN_PARALLEL_TASKS);
-    task.joinRunningOrFinishedTask(lock);
-  }
-
-  stats().recordParallelPhase(phase, task.duration());
-}
-
-void GCRuntime::joinTask(GCParallelTask& task, gcstats::PhaseKind phase) {
-  AutoLockHelperThreadState lock;
-  joinTask(task, phase, lock);
+  gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::JOIN_PARALLEL_TASKS);
+  task.joinWithLockHeld(lock);
 }
 
 void GCRuntime::sweepDebuggerOnMainThread(JSFreeOp* fop) {
   AutoLockStoreBuffer lock(&storeBuffer());
 
   // Detach unreachable debuggers and global objects from each other.
   // This can modify weakmaps and so must happen before weakmap sweeping.
   DebugAPI::sweepAll(fop);
@@ -5794,32 +5785,32 @@ IncrementalProgress GCRuntime::beginSwee
                                       PhaseKind::SWEEP_WEAKREFS, lock);
 
     WeakCacheTaskVector sweepCacheTasks;
     bool canSweepWeakCachesOffThread =
         PrepareWeakCacheTasks(rt, &sweepCacheTasks);
     if (canSweepWeakCachesOffThread) {
       weakCachesToSweep.ref().emplace(currentSweepGroup);
       for (auto& task : sweepCacheTasks) {
-        startTask(task, PhaseKind::SWEEP_WEAK_CACHES, lock);
+        startTask(task, lock);
       }
     }
 
     {
       AutoUnlockHelperThreadState unlock(lock);
       sweepJitDataOnMainThread(fop);
 
       if (!canSweepWeakCachesOffThread) {
         MOZ_ASSERT(sweepCacheTasks.empty());
         SweepAllWeakCachesOnMainThread(rt);
       }
     }
 
     for (auto& task : sweepCacheTasks) {
-      joinTask(task, PhaseKind::SWEEP_WEAK_CACHES, lock);
+      joinTask(task, lock);
     }
   }
 
   if (sweepingAtoms) {
     startSweepingAtomsTable();
   }
 
   // FinalizationRegistry sweeping touches weak maps and so must not run in
@@ -6008,31 +5999,35 @@ bool ArenaLists::foregroundFinalize(JSFr
   al.insertListWithCursorAtEnd(newArenasInMarkPhase(thingKind));
   al.insertListWithCursorAtEnd(allocatedDuringSweep);
 
   newArenasInMarkPhase(thingKind).clear();
 
   return true;
 }
 
+BackgroundMarkTask::BackgroundMarkTask(GCRuntime* gc)
+    : GCParallelTask(gc, gcstats::PhaseKind::SWEEP_MARK),
+      budget(SliceBudget::unlimited()) {}
+
 void js::gc::BackgroundMarkTask::run(AutoLockHelperThreadState& lock) {
   AutoUnlockHelperThreadState unlock(lock);
 
   // Time reporting is handled separately for parallel tasks.
   gc->sweepMarkResult =
       gc->markUntilBudgetExhausted(this->budget, GCMarker::DontReportMarkTime);
 }
 
 IncrementalProgress GCRuntime::joinBackgroundMarkTask() {
   AutoLockHelperThreadState lock;
   if (markTask.isIdle(lock)) {
     return Finished;
   }
 
-  joinTask(markTask, gcstats::PhaseKind::SWEEP_MARK, lock);
+  joinTask(markTask, lock);
 
   IncrementalProgress result = sweepMarkResult;
   sweepMarkResult = Finished;
   return result;
 }
 
 IncrementalProgress GCRuntime::markUntilBudgetExhausted(
     SliceBudget& sliceBudget, GCMarker::ShouldReportMarkTime reportTime) {
--- a/js/src/gc/GCParallelTask.cpp
+++ b/js/src/gc/GCParallelTask.cpp
@@ -77,27 +77,31 @@ void js::GCParallelTask::join() {
 }
 
 void js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock) {
   // Task has not been started; there's nothing to do.
   if (isIdle(lock)) {
     return;
   }
 
-  // If the task was dispatched but has not yet started then cancel the task and
-  // run it from the main thread. This stops us from blocking here when the
-  // helper threads are busy with other tasks.
   if (isDispatched(lock)) {
+    // If the task was dispatched but has not yet started then cancel the task
+    // and run it from the main thread. This stops us from blocking here when
+    // the helper threads are busy with other tasks.
     cancelDispatchedTask(lock);
     AutoUnlockHelperThreadState unlock(lock);
     runFromMainThread();
-    return;
+  } else {
+    // Otherwise wait for the task to complete.
+    joinRunningOrFinishedTask(lock);
   }
 
-  joinRunningOrFinishedTask(lock);
+  if (phaseKind != gcstats::PhaseKind::NONE) {
+    gc->stats().recordParallelPhase(phaseKind, duration());
+  }
 }
 
 void js::GCParallelTask::joinRunningOrFinishedTask(
     AutoLockHelperThreadState& lock) {
   MOZ_ASSERT(isRunning(lock) || isFinished(lock));
 
   // Wait for the task to run to completion.
   while (!isFinished(lock)) {
--- a/js/src/gc/GCParallelTask.h
+++ b/js/src/gc/GCParallelTask.h
@@ -20,30 +20,37 @@
 #define JS_MEMBER_FN_PTR_TYPE(ClassT, ReturnT, /* ArgTs */...) \
   ReturnT (ClassT::*)(__VA_ARGS__)
 
 #define JS_CALL_MEMBER_FN_PTR(Receiver, Ptr, /* Args */...) \
   ((Receiver)->*(Ptr))(__VA_ARGS__)
 
 namespace js {
 
+namespace gcstats {
+enum class PhaseKind : uint8_t;
+}
+
 namespace gc {
 class GCRuntime;
 }
 
 class AutoLockHelperThreadState;
 class HelperThread;
 
 // A generic task used to dispatch work to the helper thread system.
 // Users override the pure-virtual run() method.
 class GCParallelTask : public mozilla::LinkedListElement<GCParallelTask>,
                        public HelperThreadTask {
  public:
   gc::GCRuntime* const gc;
 
+  // This can be PhaseKind::NONE for tasks that take place outside a GC.
+  const gcstats::PhaseKind phaseKind;
+
  private:
   // The state of the parallel computation.
   enum class State {
     // The task is idle. Either start() has not been called or join() has
     // returned.
     Idle,
 
     // The task has been started but has not yet begun running on a helper
@@ -65,20 +72,25 @@ class GCParallelTask : public mozilla::L
 
   explicit GCParallelTask(const GCParallelTask&) = delete;
 
  protected:
   // A flag to signal a request for early completion of the off-thread task.
   mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire> cancel_;
 
  public:
-  explicit GCParallelTask(gc::GCRuntime* gc)
-      : gc(gc), state_(State::Idle), duration_(nullptr), cancel_(false) {}
+  explicit GCParallelTask(gc::GCRuntime* gc, gcstats::PhaseKind phaseKind)
+      : gc(gc),
+        phaseKind(phaseKind),
+        state_(State::Idle),
+        duration_(nullptr),
+        cancel_(false) {}
   GCParallelTask(GCParallelTask&& other)
       : gc(other.gc),
+        phaseKind(other.phaseKind),
         state_(other.state_),
         duration_(nullptr),
         cancel_(false) {}
 
   // Derived classes must override this to ensure that join() gets called
   // before members get destructed.
   virtual ~GCParallelTask();
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -127,46 +127,45 @@ class ChunkPool {
 
    private:
     TenuredChunk* current_;
   };
 };
 
 class BackgroundMarkTask : public GCParallelTask {
  public:
-  explicit BackgroundMarkTask(GCRuntime* gc)
-      : GCParallelTask(gc), budget(SliceBudget::unlimited()) {}
+  explicit BackgroundMarkTask(GCRuntime* gc);
   void setBudget(const SliceBudget& budget) { this->budget = budget; }
   void run(AutoLockHelperThreadState& lock) override;
 
  private:
   SliceBudget budget;
 };
 
 class BackgroundUnmarkTask : public GCParallelTask {
  public:
-  explicit BackgroundUnmarkTask(GCRuntime* gc) : GCParallelTask(gc) {}
+  explicit BackgroundUnmarkTask(GCRuntime* gc);
   void initZones();
   void run(AutoLockHelperThreadState& lock) override;
 
  private:
   void unmarkZones(AutoLockGC& lock);
 
   ZoneVector zones;
 };
 
 class BackgroundSweepTask : public GCParallelTask {
  public:
-  explicit BackgroundSweepTask(GCRuntime* gc) : GCParallelTask(gc) {}
+  explicit BackgroundSweepTask(GCRuntime* gc);
   void run(AutoLockHelperThreadState& lock) override;
 };
 
 class BackgroundFreeTask : public GCParallelTask {
  public:
-  explicit BackgroundFreeTask(GCRuntime* gc) : GCParallelTask(gc) {}
+  explicit BackgroundFreeTask(GCRuntime* gc);
   void run(AutoLockHelperThreadState& lock) override;
 };
 
 // Performs extra allocation off thread so that when memory is required on the
 // main thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTask {
   // Guarded by the GC lock.
   GCLockData<ChunkPool&> chunkPool_;
@@ -178,18 +177,17 @@ class BackgroundAllocTask : public GCPar
   bool enabled() const { return enabled_; }
 
   void run(AutoLockHelperThreadState& lock) override;
 };
 
 // Search the provided chunks for free arenas and decommit them.
 class BackgroundDecommitTask : public GCParallelTask {
  public:
-  explicit BackgroundDecommitTask(GCRuntime* gc) : GCParallelTask(gc) {}
-
+  explicit BackgroundDecommitTask(GCRuntime* gc);
   void run(AutoLockHelperThreadState& lock) override;
 };
 
 template <typename F>
 struct Callback {
   F op;
   void* data;
 
@@ -604,21 +602,18 @@ class GCRuntime {
   static TenuredCell* refillFreeListInGC(Zone* zone, AllocKind thingKind);
 
   void setParallelAtomsAllocEnabled(bool enabled);
   void setParallelUnmarkEnabled(bool enabled);
 
   /*
    * Concurrent sweep infrastructure.
    */
-  void startTask(GCParallelTask& task, gcstats::PhaseKind phase,
-                 AutoLockHelperThreadState& locked);
-  void joinTask(GCParallelTask& task, gcstats::PhaseKind phase,
-                AutoLockHelperThreadState& locked);
-  void joinTask(GCParallelTask& task, gcstats::PhaseKind phase);
+  void startTask(GCParallelTask& task, AutoLockHelperThreadState& lock);
+  void joinTask(GCParallelTask& task, AutoLockHelperThreadState& lock);
   void updateHelperThreadCount();
   size_t parallelWorkerCount() const;
 
   void mergeRealms(JS::Realm* source, JS::Realm* target);
 
   // WeakRefs
   bool registerWeakRef(HandleObject target, HandleObject weakRef);
   bool unregisterWeakRefWrapper(JSObject* wrapper);
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -121,17 +121,19 @@ inline bool js::NurseryChunk::markPagesI
 }
 
 // static
 inline js::NurseryChunk* js::NurseryChunk::fromChunk(TenuredChunk* chunk) {
   return reinterpret_cast<NurseryChunk*>(chunk);
 }
 
 js::NurseryDecommitTask::NurseryDecommitTask(gc::GCRuntime* gc)
-    : GCParallelTask(gc) {}
+    : GCParallelTask(gc, gcstats::PhaseKind::NONE) {
+  // This can occur outside GCs so doesn't have a stats phase.
+}
 
 bool js::NurseryDecommitTask::isEmpty(
     const AutoLockHelperThreadState& lock) const {
   return chunksToDecommit().empty() && !partialChunk;
 }
 
 bool js::NurseryDecommitTask::reserveSpaceForBytes(size_t nbytes) {
   MOZ_ASSERT(isIdle());
--- a/js/src/gc/ParallelWork.h
+++ b/js/src/gc/ParallelWork.h
@@ -32,19 +32,20 @@ using ParallelWorkFunc = size_t (*)(GCRu
 //
 // The WorkItemIterator class must supply done(), next() and get() methods. The
 // get() method must return WorkItems objects.
 template <typename WorkItem, typename WorkItemIterator>
 class ParallelWorker : public GCParallelTask {
  public:
   using WorkFunc = ParallelWorkFunc<WorkItem>;
 
-  ParallelWorker(GCRuntime* gc, WorkFunc func, WorkItemIterator& work,
-                 const SliceBudget& budget, AutoLockHelperThreadState& lock)
-      : GCParallelTask(gc),
+  ParallelWorker(GCRuntime* gc, gcstats::PhaseKind phaseKind, WorkFunc func,
+                 WorkItemIterator& work, const SliceBudget& budget,
+                 AutoLockHelperThreadState& lock)
+      : GCParallelTask(gc, phaseKind),
         func_(func),
         work_(work),
         budget_(budget),
         item_(work.get()) {
     // Consume a work item on creation so that we can stop creating workers if
     // the number of workers exceeds the number of work items.
     work.next();
   }
@@ -105,27 +106,27 @@ class MOZ_RAII AutoRunParallelWork {
                       const SliceBudget& budget,
                       AutoLockHelperThreadState& lock)
       : gc(gc), phaseKind(phaseKind), lock(lock), tasksStarted(0) {
     size_t workerCount = gc->parallelWorkerCount();
     MOZ_ASSERT(workerCount <= MaxParallelWorkers);
     MOZ_ASSERT_IF(workerCount == 0, work.done());
 
     for (size_t i = 0; i < workerCount && !work.done(); i++) {
-      tasks[i].emplace(gc, func, work, budget, lock);
-      gc->startTask(*tasks[i], phaseKind, lock);
+      tasks[i].emplace(gc, phaseKind, func, work, budget, lock);
+      gc->startTask(*tasks[i], lock);
       tasksStarted++;
     }
   }
 
   ~AutoRunParallelWork() {
     gHelperThreadLock.assertOwnedByCurrentThread();
 
     for (size_t i = 0; i < tasksStarted; i++) {
-      gc->joinTask(*tasks[i], phaseKind, lock);
+      gc->joinTask(*tasks[i], lock);
     }
     for (size_t i = tasksStarted; i < MaxParallelWorkers; i++) {
       MOZ_ASSERT(tasks[i].isNothing());
     }
   }
 
  private:
   GCRuntime* gc;