Bug 1239873 - Use AsyncShutdown API to shut down MediaStreamGraph thread.
--- 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