Bug 1319278 P4 Make ServiceWorkerPrivate use new WorkerControlEventTarget instead of TimeThreadEventTarget. r=baku
authorBen Kelly <ben@wanderview.com>
Thu, 26 Jan 2017 08:01:33 -0800
changeset 377896 6196560e59678490e7a993d13fc0610d57068f13
parent 377895 e881b33252def613f041a8671e04bcc94ae7f0c9
child 377897 d4f4afc096fd685ecc77e503ba70ec5b5dd52b13
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1319278
milestone54.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 1319278 P4 Make ServiceWorkerPrivate use new WorkerControlEventTarget instead of TimeThreadEventTarget. r=baku
dom/workers/ServiceWorkerPrivate.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1033,72 +1033,27 @@ ServiceWorkerPrivate::SendPushSubscripti
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 namespace {
 
-static void
-DummyNotificationTimerCallback(nsITimer* aTimer, void* aClosure)
-{
-  // Nothing.
-}
-
-class AllowWindowInteractionHandler;
-
-class ClearWindowAllowedRunnable final : public WorkerRunnable
+class AllowWindowInteractionHandler final : public ExtendableEventCallback
+                                          , public nsITimerCallback
+                                          , public WorkerHolder
 {
-public:
-  ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate,
-                             AllowWindowInteractionHandler* aHandler)
-  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-  , mHandler(aHandler)
-  { }
-
-private:
-  bool
-  PreDispatch(WorkerPrivate* aWorkerPrivate) override
-  {
-    // WorkerRunnable asserts that the dispatch is from parent thread if
-    // the busy count modification is WorkerThreadUnchangedBusyCount.
-    // Since this runnable will be dispatched from the timer thread, we override
-    // PreDispatch and PostDispatch to skip the check.
-    return true;
-  }
-
-  void
-  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
-  {
-    // Silence bad assertions.
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
-
-  nsresult
-  Cancel() override
-  {
-    // Always ensure the handler is released on the worker thread, even if we
-    // are cancelled.
-    mHandler = nullptr;
-    return WorkerRunnable::Cancel();
-  }
-
-  RefPtr<AllowWindowInteractionHandler> mHandler;
-};
-
-class AllowWindowInteractionHandler final : public ExtendableEventCallback
-{
-  friend class ClearWindowAllowedRunnable;
   nsCOMPtr<nsITimer> mTimer;
 
   ~AllowWindowInteractionHandler()
   {
+    // We must either fail to initialize or call ClearWindowAllowed.
+    MOZ_DIAGNOSTIC_ASSERT(!mTimer);
+    MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate);
   }
 
   void
   ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
@@ -1112,87 +1067,96 @@ class AllowWindowInteractionHandler fina
     WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
     if (!globalScope) {
       return;
     }
 
     globalScope->ConsumeWindowInteraction();
     mTimer->Cancel();
     mTimer = nullptr;
-    MOZ_ALWAYS_TRUE(aWorkerPrivate->ModifyBusyCountFromWorker(false));
+
+    ReleaseWorker();
   }
 
   void
   StartClearWindowTimer(WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(!mTimer);
 
     nsresult rv;
     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
-    RefPtr<ClearWindowAllowedRunnable> r =
-      new ClearWindowAllowedRunnable(aWorkerPrivate, this);
-
-    RefPtr<TimerThreadEventTarget> target =
-      new TimerThreadEventTarget(aWorkerPrivate, r);
-
-    rv = timer->SetTarget(target);
+    rv = timer->SetTarget(aWorkerPrivate->ControlEventTarget());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
-    // The important stuff that *has* to be reversed.
-    if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(true))) {
+    if (!HoldWorker(aWorkerPrivate, Closing)) {
       return;
     }
+
     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
     timer.swap(mTimer);
 
     // We swap first and then initialize the timer so that even if initializing
     // fails, we still clean the busy count and interaction count correctly.
     // The timer can't be initialized before modifying the busy count since the
     // timer thread could run and call the timeout but the worker may
     // already be terminating and modifying the busy count could fail.
-    rv = mTimer->InitWithFuncCallback(DummyNotificationTimerCallback, nullptr,
-                                      gDOMDisableOpenClickDelay,
-                                      nsITimer::TYPE_ONE_SHOT);
+    rv = mTimer->InitWithCallback(this,
+                                  gDOMDisableOpenClickDelay,
+                                  nsITimer::TYPE_ONE_SHOT);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       ClearWindowAllowed(aWorkerPrivate);
       return;
     }
   }
 
+  // nsITimerCallback virtual methods
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
+    ClearWindowAllowed(mWorkerPrivate);
+    return NS_OK;
+  }
+
+  // WorkerHolder virtual methods
+  bool
+  Notify(Status aStatus) override
+  {
+    // We could try to hold the worker alive until the timer fires, but other
+    // APIs are not likely to work in this partially shutdown state.  We might
+    // as well let the worker thread exit.
+    ClearWindowAllowed(mWorkerPrivate);
+    return true;
+  }
+
 public:
-  NS_INLINE_DECL_REFCOUNTING(AllowWindowInteractionHandler, override)
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   explicit AllowWindowInteractionHandler(WorkerPrivate* aWorkerPrivate)
   {
     StartClearWindowTimer(aWorkerPrivate);
   }
 
   void
   FinishedWithResult(ExtendableEventResult /* aResult */) override
   {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     ClearWindowAllowed(workerPrivate);
   }
 };
 
-bool
-ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  mHandler->ClearWindowAllowed(aWorkerPrivate);
-  mHandler = nullptr;
-  return true;
-}
+NS_IMPL_ISUPPORTS(AllowWindowInteractionHandler, nsITimerCallback)
 
 class SendNotificationEventRunnable final : public ExtendableEventWorkerRunnable
 {
   const nsString mEventName;
   const nsString mID;
   const nsString mTitle;
   const nsString mDir;
   const nsString mLang;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4899,16 +4899,22 @@ WorkerPrivate::DispatchToMainThread(alre
 {
   nsCOMPtr<nsIRunnable> runnable = aRunnable;
   if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
     named->SetName("WorkerRunnable");
   }
   return mMainThreadEventTarget->Dispatch(runnable.forget(), aFlags);
 }
 
+nsIEventTarget*
+WorkerPrivate::ControlEventTarget()
+{
+  return mWorkerControlEventTarget;
+}
+
 void
 WorkerPrivate::InitializeGCTimers()
 {
   AssertIsOnWorkerThread();
 
   // We need a timer for GC. The basic plan is to run a non-shrinking GC
   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1387,16 +1387,22 @@ public:
   nsresult
   DispatchToMainThread(nsIRunnable* aRunnable,
                        uint32_t aFlags = NS_DISPATCH_NORMAL);
 
   nsresult
   DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
                        uint32_t aFlags = NS_DISPATCH_NORMAL);
 
+  // Get an event target that will dispatch runnables as control runnables on
+  // the worker thread.  Implement nsICancelableRunnable if you wish to take
+  // action on cancelation.
+  nsIEventTarget*
+  ControlEventTarget();
+
 private:
   WorkerPrivate(WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
                 WorkerLoadInfo& aLoadInfo);
 
   bool
   MayContinueRunning()