Bug 1672760 - Add an API to wait for parse tasks to complete without cancelling them r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 27 Oct 2020 14:10:27 +0000
changeset 554752 68e6b05b17b7e32496b5519f0b0fd9e99e10b3a0
parent 554751 b55c94de3ec736a26c5158f13a689a4be1cc127d
child 554753 424377419d0d660fd0353e93d35cbf88d8925629
push id37898
push userabutkovits@mozilla.com
push dateWed, 28 Oct 2020 09:24:21 +0000
treeherdermozilla-central@83bf4fd3b1fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1672760
milestone84.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 1672760 - Add an API to wait for parse tasks to complete without cancelling them r=sfink The problem here was cancelling parse tasks without the browser's knowledge (I didn't realise that the cancel method did anything beyond waiting). Differential Revision: https://phabricator.services.mozilla.com/D94819
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/MemoryMetrics.cpp
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -874,34 +874,22 @@ void MultiScriptsDecodeTask::parse(JSCon
       scripts.infallibleAppend(resultScript);
     } else {
       // If any decodes fail, don't process the rest. We likely are hitting OOM.
       break;
     }
   }
 }
 
-void js::CancelOffThreadParses(JSRuntime* rt) {
-  AutoLockHelperThreadState lock;
-
+static void WaitForOffThreadParses(JSRuntime* rt,
+                                   AutoLockHelperThreadState& lock) {
   if (HelperThreadState().threads(lock).empty()) {
     return;
   }
 
-#ifdef DEBUG
-  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(lock);
     for (const auto& task : worklist) {
       if (task->runtimeMatches(rt)) {
         pending = true;
       }
@@ -918,16 +906,36 @@ void js::CancelOffThreadParses(JSRuntime
         }
       }
       if (!inProgress) {
         break;
       }
     }
     HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
   }
+}
+
+void js::WaitForOffThreadParses(JSRuntime* rt) {
+  AutoLockHelperThreadState lock;
+  WaitForOffThreadParses(rt, lock);
+}
+
+void js::CancelOffThreadParses(JSRuntime* rt) {
+  AutoLockHelperThreadState lock;
+
+#ifdef DEBUG
+  for (const auto& task : HelperThreadState().parseWaitingOnGC(lock)) {
+    MOZ_ASSERT(!task->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.
+  WaitForOffThreadParses(rt, lock);
 
   // Clean up any parse tasks which haven't been finished by the main thread.
   auto& finished = HelperThreadState().parseFinishedList(lock);
   while (true) {
     bool found = false;
     ParseTask* next;
     ParseTask* task = finished.getFirst();
     while (task) {
@@ -940,19 +948,17 @@ void js::CancelOffThreadParses(JSRuntime
       task = next;
     }
     if (!found) {
       break;
     }
   }
 
 #ifdef DEBUG
-  GlobalHelperThreadState::ParseTaskVector& worklist =
-      HelperThreadState().parseWorklist(lock);
-  for (const auto& task : worklist) {
+  for (const auto& task : HelperThreadState().parseWorklist(lock)) {
     MOZ_ASSERT(!task->runtimeMatches(rt));
   }
 #endif
 }
 
 bool js::OffThreadParsingMustWaitForGC(JSRuntime* rt) {
   // Off thread parsing can't occur during incremental collections on the
   // atoms zone, to avoid triggering barriers. (Outside the atoms zone, the
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -172,16 +172,22 @@ inline void CancelOffThreadIonCompilesUs
   CancelOffThreadIonCompile(
       CompilationSelector(CompilationsUsingNursery{runtime}));
 }
 
 #ifdef DEBUG
 bool HasOffThreadIonCompile(JS::Realm* realm);
 #endif
 
+/*
+ * Wait for all scheduled, in progress or finished parse tasks for the runtime
+ * to complete.
+ */
+void WaitForOffThreadParses(JSRuntime* runtime);
+
 /* Cancel all scheduled, in progress or finished parses for runtime. */
 void CancelOffThreadParses(JSRuntime* runtime);
 
 /*
  * Start a parse/emit cycle for a stream of source. The characters must stay
  * alive until the compilation finishes.
  */
 bool StartOffThreadParseScript(JSContext* cx,
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -620,17 +620,17 @@ static bool FindNotableScriptSources(JS:
 }
 
 static bool CollectRuntimeStatsHelper(JSContext* cx, RuntimeStats* rtStats,
                                       ObjectPrivateVisitor* opv, bool anonymize,
                                       IterateCellCallback statsCellCallback) {
   // Wait for any off-thread parsing to finish, as that currently allocates GC
   // things.
   JSRuntime* rt = cx->runtime();
-  CancelOffThreadParses(rt);
+  WaitForOffThreadParses(rt);
 
   // Finish any ongoing incremental GC that may change the data we're gathering
   // and ensure that we don't do anything that could start another one.
   gc::FinishGC(cx);
   gc::WaitForBackgroundTasks(cx);
   JS::AutoAssertNoGC nogc(cx);
 
   if (!rtStats->realmStatsVector.reserve(rt->numRealms)) {