Bug 1518193 - Add a new parallel task for freeing LIFO data r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 10 Jan 2019 11:00:20 +0000
changeset 453216 1bdbec17ea7c84775814586a6b21536c6f12d7c2
parent 453215 86f8236cd20fa9f805cc78b8138b1f7a7fb81220
child 453217 7d9e12dcfe7f70e64ea9126eeee3e3627d80a796
push id111060
push userjcoppeard@mozilla.com
push dateThu, 10 Jan 2019 11:00:42 +0000
treeherdermozilla-inbound@3922da7f8c51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1518193
milestone66.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 1518193 - Add a new parallel task for freeing LIFO data r=sfink
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
js/src/vm/TraceLoggingTypes.h
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -956,16 +956,17 @@ GCRuntime::GCRuntime(JSRuntime* rt)
       gcCallbackDepth(0),
       alwaysPreserveCode(false),
 #ifdef DEBUG
       arenasEmptyAtShutdown(true),
 #endif
       lock(mutexid::GCLock),
       allocTask(rt, emptyChunks_.ref()),
       sweepTask(rt),
+      freeTask(rt),
       decommitTask(rt),
       nursery_(rt),
       storeBuffer_(rt, nursery()),
       blocksToFreeAfterMinorGC(
           (size_t)JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE) {
   setGCMode(JSGC_MODE_GLOBAL);
 }
 
@@ -1307,16 +1308,17 @@ void GCRuntime::finish() {
     nursery().waitBackgroundFreeEnd();
     nursery().disable();
   }
 
   // Wait until the background finalization and allocation stops and the
   // helper thread shuts down before we forcefully release any remaining GC
   // memory.
   sweepTask.join();
+  freeTask.join();
   allocTask.cancelAndWait();
   decommitTask.cancelAndWait();
 
 #ifdef JS_GC_ZEAL
   // Free memory associated with GC verification.
   finishVerifier();
 #endif
 
@@ -3548,38 +3550,27 @@ void GCRuntime::assertBackgroundSweeping
     for (auto i : AllAllocKinds()) {
       MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
       MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(i));
     }
   }
 #endif
 }
 
-void GCRuntime::queueZonesForBackgroundSweep(ZoneList& zones) {
-  AutoLockHelperThreadState lock;
-  backgroundSweepZones.ref().transferFrom(zones);
-  if (sweepOnBackgroundThread) {
-    sweepTask.startOrRunIfIdle(lock);
-  }
-}
-
-void GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo) {
-  MOZ_ASSERT(JS::RuntimeHeapIsBusy());
-  AutoLockHelperThreadState lock;
-  blocksToFreeAfterSweeping.ref().transferUnusedFrom(lifo);
-}
-
-void GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo) {
-  MOZ_ASSERT(JS::RuntimeHeapIsBusy());
-  AutoLockHelperThreadState lock;
-  blocksToFreeAfterSweeping.ref().transferFrom(lifo);
-}
-
-void GCRuntime::freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo) {
-  blocksToFreeAfterMinorGC.ref().transferFrom(lifo);
+void GCRuntime::queueZonesAndStartBackgroundSweep(ZoneList& zones) {
+  {
+    AutoLockHelperThreadState lock;
+    backgroundSweepZones.ref().transferFrom(zones);
+    if (sweepOnBackgroundThread) {
+      sweepTask.startOrRunIfIdle(lock);
+    }
+  }
+  if (!sweepOnBackgroundThread) {
+    sweepTask.joinAndRunFromMainThread(rt);
+  }
 }
 
 void BackgroundSweepTask::run() {
   AutoTraceLog logSweeping(TraceLoggerForCurrentThread(),
                            TraceLogger_GCSweeping);
 
   AutoLockHelperThreadState lock;
   AutoSetThreadIsSweeping threadIsSweeping;
@@ -3596,31 +3587,80 @@ void GCRuntime::sweepFromBackgroundThrea
     ZoneList zones;
     zones.transferFrom(backgroundSweepZones.ref());
     LifoAlloc freeLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     freeLifoAlloc.transferFrom(&blocksToFreeAfterSweeping.ref());
 
     AutoUnlockHelperThreadState unlock(lock);
     sweepBackgroundThings(zones, freeLifoAlloc);
 
-    // The main thread may call queueZonesForBackgroundSweep() while this is
+    // The main thread may call queueZonesAndStartBackgroundSweep() while this is
     // running so we must check there is no more work after releasing the
     // lock.
   } while (!backgroundSweepZones.ref().isEmpty());
 }
 
 void GCRuntime::waitBackgroundSweepEnd() {
   sweepTask.join();
 
   // TODO: Improve assertion to work in incremental GC?
   if (!isIncrementalGCInProgress()) {
     assertBackgroundSweepingFinished();
   }
 }
 
+void GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo) {
+  MOZ_ASSERT(JS::RuntimeHeapIsBusy());
+  AutoLockHelperThreadState lock;
+  blocksToFreeAfterSweeping.ref().transferUnusedFrom(lifo);
+}
+
+void GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo) {
+  MOZ_ASSERT(JS::RuntimeHeapIsBusy());
+  AutoLockHelperThreadState lock;
+  blocksToFreeAfterSweeping.ref().transferFrom(lifo);
+}
+
+void GCRuntime::freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo) {
+  blocksToFreeAfterMinorGC.ref().transferFrom(lifo);
+}
+
+void GCRuntime::startBackgroundFree() {
+  if (CanUseExtraThreads()) {
+    AutoLockHelperThreadState lock;
+    freeTask.startOrRunIfIdle(lock);
+  } else {
+    freeTask.joinAndRunFromMainThread(rt);
+  }
+}
+
+void BackgroundFreeTask::run() {
+  AutoTraceLog logFreeing(TraceLoggerForCurrentThread(),
+                           TraceLogger_GCFree);
+
+  AutoLockHelperThreadState lock;
+
+  runtime()->gc.freeFromBackgroundThread(lock);
+
+  // Signal to the main thread that we're about to finish, because we release
+  // the lock again before GCParallelTask's state is changed to finished.
+  setFinishing(lock);
+}
+
+void GCRuntime::freeFromBackgroundThread(AutoLockHelperThreadState& lock) {
+  do {
+    LifoAlloc lifoBlocks(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
+    lifoBlocks.transferFrom(&blocksToFreeAfterSweeping.ref());
+
+    AutoUnlockHelperThreadState unlock(lock);
+
+    lifoBlocks.freeAll();
+  } while (!blocksToFreeAfterSweeping.ref().isEmpty());
+}
+
 struct IsAboutToBeFinalizedFunctor {
   template <typename T>
   bool operator()(Cell** t) {
     mozilla::DebugOnly<const Cell*> prior = *t;
     bool result = IsAboutToBeFinalizedUnbarriered(reinterpret_cast<T**>(t));
     // Sweep should not have to deal with moved pointers, since moving GC
     // handles updating the UID table manually.
     MOZ_ASSERT(*t == prior);
@@ -5710,16 +5750,19 @@ bool GCRuntime::shouldYieldForZeal(ZealM
 IncrementalProgress GCRuntime::endSweepingSweepGroup(FreeOp* fop,
                                                      SliceBudget& budget) {
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::FINALIZE_END);
     FreeOp fop(rt);
     callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
   }
 
+  /* Free LIFO blocks on a background thread if possible. */
+  startBackgroundFree();
+
   /* Update the GC state for zones we have swept. */
   for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
     AutoLockGC lock(rt);
     zone->changeGCState(Zone::Sweep, Zone::Finished);
     zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind,
                                   tunables, schedulingState, lock);
     zone->updateAllGCMallocCountersOnGCEnd(lock);
     zone->arenas.unmarkPreMarkedFreeCells();
@@ -5737,21 +5780,17 @@ IncrementalProgress GCRuntime::endSweepi
     } else {
       zones.append(zone);
     }
   }
   if (sweepAtomsZone) {
     zones.append(atomsZone);
   }
 
-  queueZonesForBackgroundSweep(zones);
-
-  if (!sweepOnBackgroundThread) {
-    sweepTask.joinAndRunFromMainThread(rt);
-  }
+  queueZonesAndStartBackgroundSweep(zones);
 
   return Finished;
 }
 
 void GCRuntime::beginSweepPhase(JS::gcreason::Reason reason,
                                 AutoGCSession& session) {
   /*
    * Sweep phase.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -106,16 +106,22 @@ class ChunkPool {
 };
 
 class BackgroundSweepTask : public GCParallelTaskHelper<BackgroundSweepTask> {
  public:
   explicit BackgroundSweepTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
   void run();
 };
 
+class BackgroundFreeTask : public GCParallelTaskHelper<BackgroundFreeTask> {
+ public:
+  explicit BackgroundFreeTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
+  void run();
+};
+
 // 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 GCParallelTaskHelper<BackgroundAllocTask> {
   // Guarded by the GC lock.
   GCLockData<ChunkPool&> chunkPool_;
 
   const bool enabled_;
 
@@ -632,19 +638,20 @@ class GCRuntime {
                                         Zone* zone, AllocKind kind);
   IncrementalProgress sweepShapeTree(FreeOp* fop, SliceBudget& budget,
                                      Zone* zone);
   void endSweepPhase(bool lastGC);
   bool allCCVisibleZonesWereCollected() const;
   void sweepZones(FreeOp* fop, bool destroyingRuntime);
   void decommitAllWithoutUnlocking(const AutoLockGC& lock);
   void startDecommit();
-  void queueZonesForBackgroundSweep(ZoneList& zones);
-  void maybeStartBackgroundSweep(AutoLockHelperThreadState& lock);
+  void queueZonesAndStartBackgroundSweep(ZoneList& zones);
   void sweepFromBackgroundThread(AutoLockHelperThreadState& lock);
+  void startBackgroundFree();
+  void freeFromBackgroundThread(AutoLockHelperThreadState& lock);
   void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks);
   void assertBackgroundSweepingFinished();
   bool shouldCompact();
   void beginCompactPhase();
   IncrementalProgress compactPhase(JS::gcreason::Reason reason,
                                    SliceBudget& sliceBudget,
                                    AutoGCSession& session);
   void endCompactPhase();
@@ -989,19 +996,21 @@ class GCRuntime {
 #endif
 
   /* Synchronize GC heap access among GC helper threads and the main thread. */
   friend class js::AutoLockGC;
   friend class js::AutoLockGCBgAlloc;
   js::Mutex lock;
 
   friend class BackgroundSweepTask;
+  friend class BackgroundFreeTask;
 
   BackgroundAllocTask allocTask;
   BackgroundSweepTask sweepTask;
+  BackgroundFreeTask freeTask;
   BackgroundDecommitTask decommitTask;
 
   /*
    * During incremental sweeping, this field temporarily holds the arenas of
    * the current AllocKind being swept in order of increasing free space.
    */
   MainThreadData<SortedArenaList> incrementalSweepList;
 
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -15,16 +15,17 @@
 #define TRACELOGGER_TREE_ITEMS(_)              \
   _(AnnotateScripts)                           \
   _(Baseline)                                  \
   _(BaselineCompilation)                       \
   _(Engine)                                    \
   _(GC)                                        \
   _(GCAllocation)                              \
   _(GCSweeping)                                \
+  _(GCFree)                                    \
   _(Interpreter)                               \
   _(InlinedScripts)                            \
   _(IonAnalysis)                               \
   _(IonCompilation)                            \
   _(IonLinking)                                \
   _(IonMonkey)                                 \
   _(IrregexpCompile)                           \
   _(IrregexpExecute)                           \