Bug 1160064 - Give WatchManager an owner thread and make sure everything happens there. r=jww
authorBobby Holley <bobbyholley@gmail.com>
Thu, 30 Apr 2015 16:21:14 -0700
changeset 273329 924a8ed0b89cf23e388e2a673866354ff67f7d80
parent 273328 6979a9e3e60a8b10c986d45cda2ed90b3d7efbab
child 273330 2e212218ba40e9d842fcee6b64364b4ddde2b6b2
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjww
bugs1160064
milestone40.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 1160064 - Give WatchManager an owner thread and make sure everything happens there. r=jww
dom/html/HTMLMediaElement.cpp
dom/media/MediaDecoder.cpp
dom/media/MediaDecoderStateMachine.cpp
dom/media/StateWatching.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2037,17 +2037,17 @@ HTMLMediaElement::LookupMediaElementURIT
       }
     }
   }
   return nullptr;
 }
 
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
-    mWatchManager(this),
+    mWatchManager(this, AbstractThread::MainThread()),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -590,17 +590,17 @@ void MediaDecoder::SetInfinite(bool aInf
 
 bool MediaDecoder::IsInfinite()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mInfiniteStream;
 }
 
 MediaDecoder::MediaDecoder() :
-  mWatchManager(this),
+  mWatchManager(this, AbstractThread::MainThread()),
   mNextFrameStatus(AbstractThread::MainThread(),
                    MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoder::mNextFrameStatus (Mirror)"),
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mCurrentTime(0.0),
   mInitialVolume(0.0),
   mInitialPlaybackRate(1.0),
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -198,17 +198,17 @@ static const uint32_t MAX_VIDEO_QUEUE_SI
 static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
 static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE;
 
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader,
                                                    bool aRealTime) :
   mDecoder(aDecoder),
   mTaskQueue(new MediaTaskQueue(GetMediaThreadPool(), /* aAssertTailDispatch = */ true)),
-  mWatchManager(this),
+  mWatchManager(this, mTaskQueue),
   mRealTime(aRealTime),
   mDispatchedStateMachine(false),
   mDelayedScheduler(this),
   mState(DECODER_STATE_DECODING_NONE, "MediaDecoderStateMachine::mState"),
   mPlayDuration(0),
   mStartTime(-1),
   mEndTime(-1),
   mDurationSet(false),
@@ -2524,16 +2524,19 @@ MediaDecoderStateMachine::FinishShutdown
   // mPendingWakeDecoder being needed again. Revoke it.
   mPendingWakeDecoder = nullptr;
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
   mNextFrameStatus.DisconnectAll();
 
+  // Shut down the watch manager before shutting down our task queue.
+  mWatchManager.Shutdown();
+
   MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
              "How did we escape from the shutdown state?");
   // We must daisy-chain these events to destroy the decoder. We must
   // destroy the decoder on the main thread, but we can't destroy the
   // decoder while this thread holds the decoder monitor. We can't
   // dispatch an event to the main thread to destroy the decoder from
   // here, as the event may run before the dispatch returns, and we
   // hold the decoder monitor here. We also want to guarantee that the
--- a/dom/media/StateWatching.h
+++ b/dom/media/StateWatching.h
@@ -178,114 +178,138 @@ private:
 // the owner, since that would keep the owner alive indefinitely. Instead, it
 // _only_ holds strong refs while waiting for Direct Tasks to fire. This ensures
 // that everything is kept alive just long enough.
 template <typename OwnerType>
 class WatchManager
 {
 public:
   typedef void(OwnerType::*CallbackMethod)();
-  explicit WatchManager(OwnerType* aOwner)
-    : mOwner(aOwner) {}
+  explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
+    : mOwner(aOwner), mOwnerThread(aOwnerThread) {}
 
   ~WatchManager()
   {
+    if (!IsShutdown()) {
+      Shutdown();
+    }
+  }
+
+  bool IsShutdown() const { return !mOwner; }
+
+  // Shutdown needs to happen on mOwnerThread. If the WatchManager will be
+  // destroyed on a different thread, Shutdown() must be called manually.
+  void Shutdown()
+  {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     for (size_t i = 0; i < mWatchers.Length(); ++i) {
       mWatchers[i]->Destroy();
     }
+    mWatchers.Clear();
+    mOwner = nullptr;
   }
 
   void Watch(WatchTarget& aTarget, CallbackMethod aMethod)
   {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     aTarget.AddWatcher(&EnsureWatcher(aMethod));
   }
 
   void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod)
   {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     PerCallbackWatcher* watcher = GetWatcher(aMethod);
     MOZ_ASSERT(watcher);
     aTarget.RemoveWatcher(watcher);
   }
 
   void ManualNotify(CallbackMethod aMethod)
   {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     PerCallbackWatcher* watcher = GetWatcher(aMethod);
     MOZ_ASSERT(watcher);
     watcher->Notify();
   }
 
 private:
   class PerCallbackWatcher : public AbstractWatcher
   {
   public:
-    PerCallbackWatcher(OwnerType* aOwner, CallbackMethod aMethod)
-      : mOwner(aOwner), mCallbackMethod(aMethod) {}
+    PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread, CallbackMethod aMethod)
+      : mOwner(aOwner), mOwnerThread(aOwnerThread), mCallbackMethod(aMethod) {}
 
     void Destroy()
     {
+      MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
       mDestroyed = true;
       mOwner = nullptr;
     }
 
     void Notify() override
     {
+      MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
       MOZ_DIAGNOSTIC_ASSERT(mOwner, "mOwner is only null after destruction, "
                                     "at which point we shouldn't be notified");
       if (mStrongRef) {
         // We've already got a notification job in the pipe.
         return;
       }
       mStrongRef = mOwner; // Hold the owner alive while notifying.
 
       // Queue up our notification jobs to run in a stable state.
       nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &PerCallbackWatcher::DoNotify);
-      AbstractThread::GetCurrent()->TailDispatcher().AddDirectTask(r.forget());
+      mOwnerThread->TailDispatcher().AddDirectTask(r.forget());
     }
 
     bool CallbackMethodIs(CallbackMethod aMethod) const
     {
       return mCallbackMethod == aMethod;
     }
 
   private:
     ~PerCallbackWatcher() {}
 
     void DoNotify()
     {
+      MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
       MOZ_ASSERT(mStrongRef);
       nsRefPtr<OwnerType> ref = mStrongRef.forget();
       ((*ref).*mCallbackMethod)();
     }
 
     OwnerType* mOwner; // Never null.
     nsRefPtr<OwnerType> mStrongRef; // Only non-null when notifying.
+    nsRefPtr<AbstractThread> mOwnerThread;
     CallbackMethod mCallbackMethod;
   };
 
   PerCallbackWatcher* GetWatcher(CallbackMethod aMethod)
   {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     for (size_t i = 0; i < mWatchers.Length(); ++i) {
       if (mWatchers[i]->CallbackMethodIs(aMethod)) {
         return mWatchers[i];
       }
     }
     return nullptr;
   }
 
   PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod)
   {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
     PerCallbackWatcher* watcher = GetWatcher(aMethod);
     if (watcher) {
       return *watcher;
     }
-    watcher = mWatchers.AppendElement(new PerCallbackWatcher(mOwner, aMethod))->get();
+    watcher = mWatchers.AppendElement(new PerCallbackWatcher(mOwner, mOwnerThread, aMethod))->get();
     return *watcher;
   }
 
   nsTArray<nsRefPtr<PerCallbackWatcher>> mWatchers;
   OwnerType* mOwner;
+  nsRefPtr<AbstractThread> mOwnerThread;
 };
 
 #undef WATCH_LOG
 
 } // namespace mozilla
 
 #endif