Bug 1379243 P4 Make worker EventTargetFor() return a "hybrid" WorkerEventTarget. r=baku
authorBen Kelly <ben@wanderview.com>
Fri, 21 Jul 2017 08:16:24 -0700
changeset 419040 9d7a312cb4fc8d233ec7dd31e03fa0de54ae0dad
parent 419039 739dffee0f16ef692307f60b84ca4bdbe5d37d2e
child 419041 2197cb6f7889a86c5024b2cf8fa554b0817da236
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1379243
milestone56.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 1379243 P4 Make worker EventTargetFor() return a "hybrid" WorkerEventTarget. r=baku
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1667,17 +1667,17 @@ public:
   {
     MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
   }
 
   void
   ForgetWorkerPrivate(WorkerPrivate* aWorkerPrivate)
   {
     MutexAutoLock lock(mMutex);
-    MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate == aWorkerPrivate);
+    MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate || mWorkerPrivate == aWorkerPrivate);
     mWorkerPrivate = nullptr;
   }
 
   NS_IMETHOD
   DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override
   {
     nsCOMPtr<nsIRunnable> runnable(aRunnable);
     return Dispatch(runnable.forget(), aFlags);
@@ -4460,16 +4460,18 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   , mDebugger(nullptr)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
   , mNumHoldersPreventingShutdownStart(0)
   , mDebuggerEventLoopLevel(0)
   , mMainThreadEventTarget(GetMainThreadEventTarget())
   , mWorkerControlEventTarget(new WorkerEventTarget(this,
                                                     WorkerEventTarget::Behavior::ControlOnly))
+  , mWorkerHybridEventTarget(new WorkerEventTarget(this,
+                                                   WorkerEventTarget::Behavior::Hybrid))
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
   , mPendingEventQueueClearing(false)
   , mCancelAllPendingRunnables(false)
@@ -4522,16 +4524,22 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   } else {
     mMainThreadEventTarget = target.forget();
   }
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
   mWorkerControlEventTarget->ForgetWorkerPrivate(this);
+
+  // We force the hybrid event target to forget the thread when we
+  // enter the Killing state, but we do it again here to be safe.
+  // Its possible that we may be created and destroyed without progressing
+  // to Killing via some obscure code path.
+  mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
 }
 
 // static
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aScriptURL,
                            const WorkerOptions& aOptions,
                            ErrorResult& aRv)
@@ -5229,16 +5237,22 @@ WorkerPrivate::DispatchToMainThread(alre
 }
 
 nsISerialEventTarget*
 WorkerPrivate::ControlEventTarget()
 {
   return mWorkerControlEventTarget;
 }
 
+nsISerialEventTarget*
+WorkerPrivate::HybridEventTarget()
+{
+  return mWorkerHybridEventTarget;
+}
+
 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
@@ -6192,16 +6206,22 @@ WorkerPrivate::NotifyInternal(JSContext*
     mStatus = aStatus;
 
     // Mark parent status as closing immediately to avoid new events being
     // dispatched after we clear the queue below.
     if (aStatus == Closing) {
       Close();
     }
 
+    // Make sure the hybrid event target stops dispatching runnables
+    // once we reaching the killing state.
+    if (aStatus == Killing) {
+      mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
+    }
+
     eventTarget = mEventTarget;
   }
 
   // Disable the event target, if it exists.
   if (eventTarget) {
     // Since we'll no longer process events, make sure we no longer allow anyone
     // to post them. We have to do this without mMutex held, since our mutex
     // must be acquired *after* the WorkerEventTarget's mutex when they're both
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1000,16 +1000,17 @@ class WorkerPrivate : public WorkerPriva
   nsTArray<ParentType*> mChildWorkers;
   nsTObserverArray<WorkerHolder*> mHolders;
   uint32_t mNumHoldersPreventingShutdownStart;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
   uint32_t mDebuggerEventLoopLevel;
   RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;
   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
   RefPtr<WorkerEventTarget> mWorkerControlEventTarget;
+  RefPtr<WorkerEventTarget> mWorkerHybridEventTarget;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
     RefPtr<EventTarget> mEventTarget;
     bool mCompleted;
     bool mResult;
@@ -1459,16 +1460,21 @@ public:
                        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.
   nsISerialEventTarget*
   ControlEventTarget();
 
+  // Get an event target that will attempt to dispatch a normal WorkerRunnable,
+  // but if that fails will then fall back to a control runnable.
+  nsISerialEventTarget*
+  HybridEventTarget();
+
 private:
   WorkerPrivate(WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsAString& aWorkerName,
                 const nsACString& aServiceWorkerScope,
                 WorkerLoadInfo& aLoadInfo);
 
   bool
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -67,17 +67,17 @@ NS_CreateJSTimeoutHandler(JSContext* aCx
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::cache::CacheStorage;
 using mozilla::ipc::PrincipalInfo;
 
 WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
-: mSerialEventTarget(aWorkerPrivate->GetEventTarget())
+: mSerialEventTarget(aWorkerPrivate->HybridEventTarget())
 , mWindowInteractionsAllowed(0)
 , mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   // We should always have an event target when the global is created.
   MOZ_DIAGNOSTIC_ASSERT(mSerialEventTarget);
 }
@@ -855,17 +855,17 @@ ServiceWorkerGlobalScope::OpenWindowEnab
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
   return worker->OpenWindowEnabled();
 }
 
 WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
                                                   WorkerPrivate* aWorkerPrivate)
 : mWorkerPrivate(aWorkerPrivate)
-, mSerialEventTarget(aWorkerPrivate->GetEventTarget())
+, mSerialEventTarget(aWorkerPrivate->HybridEventTarget())
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   // We should always have an event target when the global is created.
   MOZ_DIAGNOSTIC_ASSERT(mSerialEventTarget);
 }
 
 WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()