Bug 1450644 - Better shutdown approach for Workers - part 1 - CancelingRunnable, r=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 17 Apr 2018 20:51:03 +0200
changeset 414223 52a992c65070d7f996dd3bfd38bc1521f49ec277
parent 414222 0d3ab01ff51bc77b7f616beefd355e70c0d7de41
child 414224 05cb4efbcd693d8c64f4b4df8a3d055c518b9d76
push id33861
push userccoroiu@mozilla.com
push dateWed, 18 Apr 2018 10:50:38 +0000
treeherdermozilla-central@4af4ae0aee55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1450644
milestone61.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 1450644 - Better shutdown approach for Workers - part 1 - CancelingRunnable, r=asuth
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -971,16 +971,59 @@ class SimpleWorkerHolder final : public 
 public:
   SimpleWorkerHolder()
     : WorkerHolder("SimpleWorkerHolder")
   {}
 
   virtual bool Notify(WorkerStatus aStatus) override { return true; }
 };
 
+// A runnable to cancel the worker from the parent thread when self.close() is
+// called. This runnable is executed on the parent process in order to cancel
+// the current runnable. It uses a normal WorkerRunnable in order to be sure
+// that all the pending WorkerRunnables are executed before this.
+class CancelingOnParentRunnable final : public WorkerRunnable
+{
+public:
+  explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
+    : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
+  {}
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    aWorkerPrivate->Cancel();
+    return true;
+  }
+};
+
+// This runnable starts the canceling of a worker after a self.close().
+class CancelingRunnable final : public Runnable
+{
+public:
+  CancelingRunnable()
+    : Runnable("CancelingRunnable")
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+
+    // Now we can cancel the this worker from the parent process.
+    RefPtr<CancelingOnParentRunnable> r =
+      new CancelingOnParentRunnable(workerPrivate);
+    r->Dispatch();
+
+    return NS_OK;
+  }
+};
+
 } /* anonymous namespace */
 
 class WorkerPrivate::EventTarget final : public nsISerialEventTarget
 {
   // This mutex protects mWorkerPrivate and must be acquired *before* the
   // WorkerPrivate's mutex whenever they must both be held.
   mozilla::Mutex mMutex;
   WorkerPrivate* mWorkerPrivate;
@@ -3604,16 +3647,23 @@ WorkerPrivate::InterruptCallback(JSConte
   }
 
   // Make sure the periodic timer gets turned back on here.
   SetGCTimerMode(PeriodicTimer);
 
   return true;
 }
 
+void
+WorkerPrivate::CloseInternal()
+{
+  AssertIsOnWorkerThread();
+  NotifyInternal(Closing);
+}
+
 bool
 WorkerPrivate::IsOnCurrentThread()
 {
   // May be called on any thread!
 
   MOZ_ASSERT(mPRThread);
   return PR_GetCurrentThread() == mPRThread;
 }
@@ -4473,18 +4523,27 @@ WorkerPrivate::NotifyInternal(WorkerStat
   }
 
   // If the worker script never ran, or failed to compile, we don't need to do
   // anything else.
   if (!GlobalScope()) {
     return true;
   }
 
-  // Don't abort the script.
+  // Don't abort the script now, but we dispatch a runnable to do it when the
+  // current JS frame is executed.
   if (aStatus == Closing) {
+    if (mSyncLoopStack.IsEmpty()) {
+      // Here we use a normal runnable to know when the current JS chunk of code
+      // is finished. We cannot use a WorkerRunnable because they are not
+      // accepted any more by the worker, and we do not want to use a
+      // WorkerControlRunnable because they are immediately executed.
+      RefPtr<CancelingRunnable> r = new CancelingRunnable();
+      mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+    }
     return true;
   }
 
   MOZ_ASSERT(aStatus == Terminating ||
              aStatus == Canceling ||
              aStatus == Killing);
 
   // Always abort the script.
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -258,22 +258,18 @@ public:
   DoRunLoop(JSContext* aCx);
 
   bool
   InterruptCallback(JSContext* aCx);
 
   bool
   IsOnCurrentThread();
 
-  bool
-  CloseInternal()
-  {
-    AssertIsOnWorkerThread();
-    return NotifyInternal(Closing);
-  }
+  void
+  CloseInternal();
 
   bool
   FreezeInternal();
 
   bool
   ThawInternal();
 
   void