Bug 1208371 - Count the users of a MediaStream to ease Destroy() responsibility. r=roc
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 05 Jan 2016 10:16:22 +0800
changeset 348412 da958105982700b4ab6390f33ca954169b939127
parent 348411 d602e5f8c5dfa34473c1577c102c1c8e724963b6
child 348413 49c2839c3cf99e50de1e124178e643dc467f00b2
push id14828
push userpehrsons@gmail.com
push dateThu, 07 Apr 2016 12:57:27 +0000
reviewersroc
bugs1208371
milestone48.0a1
Bug 1208371 - Count the users of a MediaStream to ease Destroy() responsibility. r=roc MozReview-Commit-ID: FdcR4ChTND4
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraph.h
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1834,16 +1834,17 @@ MediaStream::MediaStream(DOMMediaStream*
   , mNotifiedBlocked(false)
   , mHasCurrentData(false)
   , mNotifiedHasCurrentData(false)
   , mWrapper(aWrapper)
   , mMainThreadCurrentTime(0)
   , mMainThreadFinished(false)
   , mFinishedNotificationSent(false)
   , mMainThreadDestroyed(false)
+  , mNrOfMainThreadUsers(0)
   , mGraph(nullptr)
   , mAudioChannelType(dom::AudioChannel::Normal)
 {
   MOZ_COUNT_CTOR(MediaStream);
   // aWrapper should not already be connected to a MediaStream! It needs
   // to be hooked up to this stream, and since this stream is only just
   // being created now, aWrapper must not be connected to anything.
   NS_ASSERTION(!aWrapper || !aWrapper->GetPlaybackStream(),
@@ -1977,16 +1978,18 @@ MediaStream::DestroyImpl()
     mConsumers[i]->Disconnect();
   }
   mGraph = nullptr;
 }
 
 void
 MediaStream::Destroy()
 {
+  NS_ASSERTION(mNrOfMainThreadUsers == 0,
+               "Do not mix Destroy() and RegisterUser()/UnregisterUser()");
   // Keep this stream alive until we leave this method
   RefPtr<MediaStream> kungFuDeathGrip = this;
 
   class Message : public ControlMessage {
   public:
     explicit Message(MediaStream* aStream) : ControlMessage(aStream) {}
     void Run() override
     {
@@ -2002,16 +2005,36 @@ MediaStream::Destroy()
   GraphImpl()->AppendMessage(MakeUnique<Message>(this));
   // Message::RunDuringShutdown may have removed this stream from the graph,
   // but our kungFuDeathGrip above will have kept this stream alive if
   // necessary.
   mMainThreadDestroyed = true;
 }
 
 void
+MediaStream::RegisterUser()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ++mNrOfMainThreadUsers;
+}
+
+void
+MediaStream::UnregisterUser()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  --mNrOfMainThreadUsers;
+  NS_ASSERTION(mNrOfMainThreadUsers >= 0, "Double-removal of main thread user");
+  NS_ASSERTION(!IsDestroyed(), "Do not mix Destroy() and RegisterUser()/UnregisterUser()");
+  if (mNrOfMainThreadUsers == 0) {
+    Destroy();
+  }
+}
+
+void
 MediaStream::AddAudioOutput(void* aKey)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, void* aKey) : ControlMessage(aStream), mKey(aKey) {}
     void Run() override
     {
       mStream->AddAudioOutputImpl(mKey);
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -426,18 +426,29 @@ public:
    * and has not started, then the runnable will be run
    * synchronously/immediately.  (There are no pending updates in these
    * situations.)
    *
    * Main thread only.
    */
   void RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable);
 
-  // Signal that the client is done with this MediaStream. It will be deleted later.
+  // Signal that the client is done with this MediaStream. It will be deleted
+  // later. Do not mix usage of Destroy() with RegisterUser()/UnregisterUser().
+  // That will cause the MediaStream to be destroyed twice, which will cause
+  // some assertions to fail.
   virtual void Destroy();
+  // Signal that a client is using this MediaStream. Useful to not have to
+  // explicitly manage ownership (responsibility to Destroy()) when there are
+  // multiple clients using a MediaStream.
+  void RegisterUser();
+  // Signal that a client no longer needs this MediaStream. When the number of
+  // clients using this MediaStream reaches 0, it will be destroyed.
+  void UnregisterUser();
+
   // Returns the main-thread's view of how much data has been processed by
   // this stream.
   StreamTime GetCurrentTime()
   {
     NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
     return mMainThreadCurrentTime;
   }
   // Return the main thread's view of whether this stream has finished.
@@ -701,16 +712,17 @@ protected:
 
   // This state is only used on the main thread.
   DOMMediaStream* mWrapper;
   // Main-thread views of state
   StreamTime mMainThreadCurrentTime;
   bool mMainThreadFinished;
   bool mFinishedNotificationSent;
   bool mMainThreadDestroyed;
+  int mNrOfMainThreadUsers;
 
   // Our media stream graph.  null if destroyed on the graph thread.
   MediaStreamGraphImpl* mGraph;
 
   dom::AudioChannel mAudioChannelType;
 };
 
 /**