Bug 1239873 - Use AsyncShutdown API to shut down MediaStreamGraph thread. draft
authorJan-Ivar Bruaroey <jib@mozilla.com>
Fri, 22 Jan 2016 13:49:54 -0500
changeset 324502 8bf79912cf8e718df37fbe975ce85a1ba63207c2
parent 324322 7104d650a97d895cbbc64d53462bf86a04658abe
child 513406 4f5cfd56d2f93a358f8b479f8de5ac1ba8dd4b31
push id9932
push userjbruaroey@mozilla.com
push dateSat, 23 Jan 2016 14:10:06 +0000
bugs1239873
milestone46.0a1
Bug 1239873 - Use AsyncShutdown API to shut down MediaStreamGraph thread.
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraphImpl.h
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioCaptureStream.h"
 #include "AudioChannelService.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
 #include "mozilla/dom/AudioContextBinding.h"
+#include "mozilla/media/MediaUtils.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 #include "mtransport/runnable_utils.h"
@@ -1400,27 +1401,30 @@ MediaStreamGraphImpl::ApplyStreamUpdate(
       stream->mWrapper->NotifyStreamFinished();
     }
 
     stream->NotifyMainThreadListeners();
   }
 }
 
 void
-MediaStreamGraphImpl::ForceShutDown()
+MediaStreamGraphImpl::ForceShutDown(ShutdownTicket* aShutdownTicket)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
   STREAM_LOG(LogLevel::Debug, ("MediaStreamGraph %p ForceShutdown", this));
   {
     MonitorAutoLock lock(mMonitor);
     mForceShutDown = true;
+    mForceShutdownTicket = aShutdownTicket;
     EnsureNextIterationLocked();
   }
 }
 
+/* static */ StaticRefPtr<nsIAsyncShutdownBlocker> gMediaStreamGraphShutdownBlocker;
+
 namespace {
 
 class MediaStreamGraphShutDownRunnable : public nsRunnable {
 public:
   explicit MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph)
     : mGraph(aGraph)
   {}
   NS_IMETHOD Run()
@@ -1438,16 +1442,23 @@ public:
     // handle it all in Shutdown()!
     if (mGraph->mDriver->AsAudioCallbackDriver()) {
       MOZ_ASSERT(!mGraph->mDriver->AsAudioCallbackDriver()->InCallback());
     }
 #endif
 
     mGraph->mDriver->Shutdown();
 
+    // We may be one of several graphs. Drop ticket to eventually unblock shutdown.
+    mGraph->mForceShutdownTicket = nullptr;
+
+    // We can't block past the final LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
+    // stage, since completion of that stage requires all streams to be freed,
+    // which requires shutdown to proceed.
+
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
       // mGraph is no longer needed, so delete it.
       mGraph->Destroy();
     } else {
       // The graph is not empty.  We must be in a forced shutdown, or a
       // non-realtime graph that has finished processing.  Some later
       // AppendMessage will detect that the manager has been emptied, and
@@ -1504,24 +1515,16 @@ public:
   {
     // Make sure to run this message during shutdown too, to make sure
     // that we balance the number of streams registered with the graph
     // as they're destroyed during shutdown.
     Run();
   }
 };
 
-class MediaStreamGraphShutdownObserver final : public nsIObserver
-{
-  ~MediaStreamGraphShutdownObserver() {}
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-};
-
 } // namespace
 
 void
 MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
 
   nsTArray<nsCOMPtr<nsIRunnable> > runnables;
@@ -2794,49 +2797,58 @@ MediaStreamGraphImpl::Destroy()
 {
   // First unregister from memory reporting.
   UnregisterWeakMemoryReporter(this);
 
   // Clear the self reference which will destroy this instance.
   mSelfRef = nullptr;
 }
 
-NS_IMPL_ISUPPORTS(MediaStreamGraphShutdownObserver, nsIObserver)
-
-static bool gShutdownObserverRegistered = false;
-
-NS_IMETHODIMP
-MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
-                                          const char *aTopic,
-                                          const char16_t *aData)
-{
-  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    for (auto iter = gGraphs.Iter(); !iter.Done(); iter.Next()) {
-      MediaStreamGraphImpl* graph = iter.UserData();
-      graph->ForceShutDown();
-    }
-    nsContentUtils::UnregisterShutdownObserver(this);
-    gShutdownObserverRegistered = false;
-  }
-  return NS_OK;
-}
-
 MediaStreamGraph*
 MediaStreamGraph::GetInstance(MediaStreamGraph::GraphDriverType aGraphDriverRequested,
                               dom::AudioChannel aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   uint32_t channel = static_cast<uint32_t>(aChannel);
   MediaStreamGraphImpl* graph = nullptr;
 
   if (!gGraphs.Get(channel, &graph)) {
-    if (!gShutdownObserverRegistered) {
-      gShutdownObserverRegistered = true;
-      nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
+    if (!gMediaStreamGraphShutdownBlocker) {
+
+      class Blocker : public media::ShutdownBlocker
+      {
+      public:
+        Blocker()
+        : media::ShutdownBlocker(NS_LITERAL_STRING(
+            "MediaStreamGraph shutdown: blocking on msg thread")) {}
+
+        NS_IMETHOD
+        BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override
+        {
+          // Distribute the global async shutdown blocker in a ticket. If there
+          // are zero graphs then shutdown is unblocked when we go out of scope.
+          RefPtr<MediaStreamGraphImpl::ShutdownTicket> ticket =
+              new MediaStreamGraphImpl::ShutdownTicket(gMediaStreamGraphShutdownBlocker.get());
+          gMediaStreamGraphShutdownBlocker = nullptr;
+
+          for (auto iter = gGraphs.Iter(); !iter.Done(); iter.Next()) {
+            iter.UserData()->ForceShutDown(ticket);
+          }
+          return NS_OK;
+        }
+      };
+
+      gMediaStreamGraphShutdownBlocker = new Blocker();
+      nsCOMPtr<nsIAsyncShutdownClient> barrier = MediaStreamGraphImpl::GetShutdownBarrier();
+      nsresult rv = barrier->
+          AddBlocker(gMediaStreamGraphShutdownBlocker,
+                     NS_LITERAL_STRING(__FILE__), __LINE__,
+                     NS_LITERAL_STRING("MediaStreamGraph shutdown"));
+      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     }
 
     CubebUtils::InitPreferredSampleRate();
 
     graph = new MediaStreamGraphImpl(aGraphDriverRequested,
                                      CubebUtils::PreferredSampleRate(),
                                      aChannel);
 
@@ -2874,17 +2886,17 @@ MediaStreamGraph::DestroyNonRealtimeInst
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
   if (graph->mForceShutDown)
     return; // already done
 
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(0);
   }
-  graph->ForceShutDown();
+  graph->ForceShutDown(nullptr);
 }
 
 NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter)
 
 struct ArrayClearer
 {
   explicit ArrayClearer(nsTArray<AudioNodeSizes>& aArray) : mArray(aArray) {}
   ~ArrayClearer() { mArray.Clear(); }
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -8,16 +8,17 @@
 
 #include "MediaStreamGraph.h"
 
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
+#include "nsIAsyncShutdown.h"
 #include "Latency.h"
 #include "mozilla/WeakPtr.h"
 #include "GraphDriver.h"
 #include "AudioMixer.h"
 
 namespace mozilla {
 
 template <typename T>
@@ -134,23 +135,58 @@ public:
    * Called to apply a StreamUpdate to its stream.
    */
   void ApplyStreamUpdate(StreamUpdate* aUpdate);
   /**
    * Append a ControlMessage to the message queue. This queue is drained
    * during RunInStableState; the messages will run on the graph thread.
    */
   void AppendMessage(ControlMessage* aMessage);
+
+  // Shutdown helpers.
+
+  static already_AddRefed<nsIAsyncShutdownClient>
+  GetShutdownBarrier()
+  {
+    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
+    MOZ_RELEASE_ASSERT(svc);
+
+    nsCOMPtr<nsIAsyncShutdownClient> barrier;
+    nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
+    if (!barrier) {
+      // We are probably in a content process.
+      rv = svc->GetContentChildShutdown(getter_AddRefs(barrier));
+    }
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+    MOZ_RELEASE_ASSERT(barrier);
+    return barrier.forget();
+  }
+
+  class ShutdownTicket final
+  {
+  public:
+    explicit ShutdownTicket(nsIAsyncShutdownBlocker* aBlocker) : mBlocker(aBlocker) {}
+    NS_INLINE_DECL_REFCOUNTING(ShutdownTicket)
+  private:
+    ~ShutdownTicket()
+    {
+      nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
+      barrier->RemoveBlocker(mBlocker);
+    }
+
+    nsCOMPtr<nsIAsyncShutdownBlocker> mBlocker;
+  };
+
   /**
    * Make this MediaStreamGraph enter forced-shutdown state. This state
    * will be noticed by the media graph thread, which will shut down all streams
    * and other state controlled by the media graph thread.
    * This is called during application shutdown.
    */
-  void ForceShutDown();
+  void ForceShutDown(ShutdownTicket* aShutdownTicket);
   /**
    * Shutdown() this MediaStreamGraph's threads and return when they've shut down.
    */
   void ShutdownThreads();
 
   /**
    * Called before the thread runs.
    */
@@ -687,16 +723,22 @@ public:
    * The graph should stop processing at or after this time.
    */
   GraphTime mEndTime;
 
   /**
    * True when we need to do a forced shutdown during application shutdown.
    */
   bool mForceShutDown;
+
+  /**
+   * Drop this reference during shutdown to unblock shutdown.
+   **/
+  RefPtr<ShutdownTicket> mForceShutdownTicket;
+
   /**
    * True when we have posted an event to the main thread to run
    * RunInStableState() and the event hasn't run yet.
    */
   bool mPostedRunInStableStateEvent;
 
   // Main thread only