Bug 968016 - Use a SharedThreadPool of size 1 for the Media State Machine thread. r=kinetik

This makes it easy to share the state machine thread, and for it to shut down
automatically when the last reference is dropped to it.
This commit is contained in:
Chris Pearce 2014-02-18 11:53:53 +13:00
Родитель 5a87fcc228
Коммит aa8aee7920
7 изменённых файлов: 38 добавлений и 302 удалений

Просмотреть файл

@ -8,7 +8,7 @@
#include "windows.h"
#include "mmsystem.h"
#endif
#include "mozilla/DebugOnly.h"
#include <stdint.h>
@ -30,6 +30,7 @@
#include "MediaShutdownManager.h"
#include "SharedThreadPool.h"
#include "MediaTaskQueue.h"
#include "nsIEventTarget.h"
#include "prenv.h"
#include "mozilla/Preferences.h"
#include "gfx2DGlue.h"
@ -147,109 +148,6 @@ static int64_t DurationToUsecs(TimeDuration aDuration) {
return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
}
class StateMachineTracker
{
private:
StateMachineTracker()
: mMonitor("media.statemachinetracker")
, mStateMachineCount(0)
{
MOZ_COUNT_CTOR(StateMachineTracker);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
}
~StateMachineTracker()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MOZ_COUNT_DTOR(StateMachineTracker);
}
public:
// Access singleton instance. This is initially called on the main
// thread in the MediaDecoderStateMachine constructor resulting
// in the global object being created lazily. Non-main thread
// access always occurs after this and uses the monitor to
// safely access the decode thread counts.
static StateMachineTracker& Instance();
// Instantiate the global state machine thread if required.
// Call on main thread only.
void EnsureGlobalStateMachine();
// Destroy global state machine thread if required.
// Call on main thread only.
void CleanupGlobalStateMachine();
// Return the global state machine thread. Call from any thread.
nsIThread* GetGlobalStateMachineThread()
{
ReentrantMonitorAutoEnter mon(mMonitor);
NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!");
return mStateMachineThread->GetThread();
}
private:
// Holds global instance of StateMachineTracker.
// Writable on main thread only.
static StateMachineTracker* sInstance;
// Reentrant monitor that must be obtained to access
// the decode thread count member and methods.
ReentrantMonitor mMonitor;
// Number of instances of MediaDecoderStateMachine
// that are currently instantiated. Access on the
// main thread only.
uint32_t mStateMachineCount;
// Global state machine thread. Write on the main thread
// only, read from the decoder threads. Synchronized via
// the mMonitor.
nsRefPtr<StateMachineThread> mStateMachineThread;
};
StateMachineTracker* StateMachineTracker::sInstance = nullptr;
StateMachineTracker& StateMachineTracker::Instance()
{
if (!sInstance) {
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
sInstance = new StateMachineTracker();
}
return *sInstance;
}
void StateMachineTracker::EnsureGlobalStateMachine()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mMonitor);
if (mStateMachineCount == 0) {
NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!");
mStateMachineThread = new StateMachineThread();
DebugOnly<nsresult> rv = mStateMachineThread->Init();
NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Can't create media state machine thread");
}
mStateMachineCount++;
}
void StateMachineTracker::CleanupGlobalStateMachine()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
NS_ABORT_IF_FALSE(mStateMachineCount > 0,
"State machine ref count must be > 0");
mStateMachineCount--;
if (mStateMachineCount == 0) {
DECODER_LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
{
ReentrantMonitorAutoEnter mon(mMonitor);
mStateMachineThread->Shutdown();
mStateMachineThread = nullptr;
sInstance = nullptr;
}
delete this;
}
}
MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader,
bool aRealTime) :
@ -295,8 +193,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
StateMachineTracker::Instance().EnsureGlobalStateMachine();
// only enable realtime mode when "media.realtime_decoder.enabled" is true.
if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
mRealTime = false;
@ -332,7 +228,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderStateMachine::~MediaDecoderStateMachine()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
MOZ_COUNT_DTOR(MediaDecoderStateMachine);
NS_ASSERTION(!mPendingWakeDecoder.get(),
"WakeDecoder should have been revoked already");
@ -348,7 +244,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
mTimer = nullptr;
mReader = nullptr;
StateMachineTracker::Instance().CleanupGlobalStateMachine();
#ifdef XP_WIN
timeEndPeriod(1);
#endif
@ -1189,6 +1084,10 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
Preferences::GetUint("media.num-decode-threads", 25)));
NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE);
RefPtr<SharedThreadPool> stateMachinePool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE);
mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget());
NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE);
@ -1197,6 +1096,8 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
cloneReader = static_cast<MediaDecoderStateMachine*>(aCloneDonor)->mReader;
}
mStateMachineThreadPool = stateMachinePool;
return mReader->Init(cloneReader);
}
@ -2043,7 +1944,8 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// finished and released its monitor/references. That event then will
// dispatch an event to the main thread to release the decoder and
// state machine.
NS_DispatchToCurrentThread(new nsDispatchDisposeEvent(mDecoder, this));
GetStateMachineThread()->Dispatch(
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
return NS_OK;
}
@ -2622,7 +2524,7 @@ nsresult MediaDecoderStateMachine::CallRunStateMachine()
if (mRunAgain && !mDispatchedRunEvent) {
mDispatchedRunEvent = true;
return NS_DispatchToCurrentThread(this);
return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL);
}
return res;
@ -2725,12 +2627,14 @@ bool MediaDecoderStateMachine::OnDecodeThread() const
bool MediaDecoderStateMachine::OnStateMachineThread() const
{
return IsCurrentThread(GetStateMachineThread());
bool rv = false;
mStateMachineThreadPool->IsOnCurrentThread(&rv);
return rv;
}
nsIThread* MediaDecoderStateMachine::GetStateMachineThread()
nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread()
{
return StateMachineTracker::Instance().GetGlobalStateMachineThread();
return mStateMachineThreadPool->GetEventTarget();
}
void MediaDecoderStateMachine::NotifyAudioAvailableListener()

Просмотреть файл

@ -122,7 +122,6 @@ public:
bool aRealTime = false);
~MediaDecoderStateMachine();
// nsDecoderStateMachine interface
nsresult Init(MediaDecoderStateMachine* aCloneDonor);
// Enumeration for the valid decoding states
@ -303,7 +302,7 @@ public:
void SetFrameBufferLength(uint32_t aLength);
// Returns the shared state machine thread.
static nsIThread* GetStateMachineThread();
nsIEventTarget* GetStateMachineThread();
// Calls ScheduleStateMachine() after taking the decoder lock. Also
// notifies the decoder thread in case it's waiting on the decoder lock.
@ -616,6 +615,8 @@ private:
// thread every time they're called.
RefPtr<MediaTaskQueue> mDecodeTaskQueue;
RefPtr<SharedThreadPool> mStateMachineThreadPool;
// Timer to call the state machine Run() method. Used by
// ScheduleStateMachine(). Access protected by decoder monitor.
nsCOMPtr<nsITimer> mTimer;

Просмотреть файл

@ -12,71 +12,6 @@
namespace mozilla {
StateMachineThread::StateMachineThread()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(StateMachineThread);
}
StateMachineThread::~StateMachineThread()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_DTOR(StateMachineThread);
}
void
StateMachineThread::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mThread);
if (mThread) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &StateMachineThread::ShutdownThread);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
}
void
StateMachineThread::ShutdownThread()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mThread);
mThread->Shutdown();
mThread = nullptr;
MediaShutdownManager::Instance().Unregister(this);
}
nsresult
StateMachineThread::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = NS_NewNamedThread("Media State", getter_AddRefs(mThread));
NS_ENSURE_SUCCESS(rv, rv);
MediaShutdownManager::Instance().Register(this);
return NS_OK;
}
nsIThread*
StateMachineThread::GetThread()
{
MOZ_ASSERT(mThread);
return mThread;
}
void
StateMachineThread::SpinUntilShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
while (mThread) {
bool processed = false;
nsresult rv = NS_GetCurrentThread()->ProcessNextEvent(true, &processed);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to spin main thread while awaiting media shutdown");
break;
}
}
}
NS_IMPL_ISUPPORTS1(MediaShutdownManager, nsIObserver)
MediaShutdownManager::MediaShutdownManager()
@ -111,8 +46,7 @@ void
MediaShutdownManager::EnsureCorrectShutdownObserverState()
{
MOZ_ASSERT(!mIsDoingXPCOMShutDown);
bool needShutdownObserver = (mDecoders.Count() > 0) ||
(mStateMachineThreads.Count() > 0);
bool needShutdownObserver = mDecoders.Count() > 0;
if (needShutdownObserver != mIsObservingShutdown) {
mIsObservingShutdown = needShutdownObserver;
if (mIsObservingShutdown) {
@ -162,28 +96,6 @@ MediaShutdownManager::Observe(nsISupports *aSubjet,
return NS_OK;
}
void
MediaShutdownManager::Register(StateMachineThread* aThread)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mStateMachineThreads.Contains(aThread));
mStateMachineThreads.PutEntry(aThread);
MOZ_ASSERT(mStateMachineThreads.Contains(aThread));
MOZ_ASSERT(mStateMachineThreads.Count() > 0);
EnsureCorrectShutdownObserverState();
}
void
MediaShutdownManager::Unregister(StateMachineThread* aThread)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mStateMachineThreads.Contains(aThread));
if (!mIsDoingXPCOMShutDown) {
mStateMachineThreads.RemoveEntry(aThread);
EnsureCorrectShutdownObserverState();
}
}
static PLDHashOperator
ShutdownMediaDecoder(nsRefPtrHashKey<MediaDecoder>* aEntry, void*)
{
@ -191,17 +103,6 @@ ShutdownMediaDecoder(nsRefPtrHashKey<MediaDecoder>* aEntry, void*)
return PL_DHASH_REMOVE;
}
static PLDHashOperator
JoinStateMachineThreads(nsRefPtrHashKey<StateMachineThread>* aEntry, void*)
{
// Hold a strong reference to the StateMachineThread, so that if it
// is Unregistered() and the hashtable's owning reference is cleared,
// it won't be destroyed while we're spinning here.
RefPtr<StateMachineThread> thread = aEntry->GetKey();
thread->SpinUntilShutdownComplete();
return PL_DHASH_REMOVE;
}
void
MediaShutdownManager::Shutdown()
{
@ -217,13 +118,6 @@ MediaShutdownManager::Shutdown()
// hashtable.
mDecoders.EnumerateEntries(ShutdownMediaDecoder, nullptr);
// Iterate over the StateMachineThreads and wait for them to have finished
// shutting down, and remove them from the hashtable. Once all the decoders
// have shutdown the active state machine thread will naturally shutdown
// asynchronously. We may also have another state machine thread active,
// if construction and shutdown of the state machine thread has interleaved.
mStateMachineThreads.EnumerateEntries(JoinStateMachineThreads, nullptr);
// Remove the MediaShutdownManager instance from the shutdown observer
// list.
nsContentUtils::UnregisterShutdownObserver(this);

Просмотреть файл

@ -32,21 +32,19 @@ class StateMachineThread;
// waiting for its threads to shutdown it will block other xpcom-shutdown
// notifications from firing, and shutdown of one part of the media pipeline
// (say the State Machine thread) may depend another part to be shutdown
// first (the MediaDecoder threads). Additionally we need to not interfere
// with shutdown in the the non-xpcom-shutdown case, where we need to be able
// to recreate the State Machine thread after it's been destroyed without
// affecting the shutdown of the old State Machine thread. The
// MediaShutdownManager encapsulates all these dependencies, and provides
// a single xpcom-shutdown listener for the MediaDecoder infrastructure, to
// ensure that no shutdown order dependencies leak out of the MediaDecoder
// stack. The MediaShutdownManager is a singleton.
// first (the MediaDecoder threads). The MediaShutdownManager encapsulates
// all these dependencies, and provides a single xpcom-shutdown listener
// for the MediaDecoder infrastructure, to ensure that no shutdown order
// dependencies leak out of the MediaDecoder stack. The MediaShutdownManager
// is a singleton.
//
// The MediaShutdownManager ensures that the MediaDecoder stack is shutdown
// before returning from its xpcom-shutdown observer by keeping track of all
// the active MediaDecoders, and upon xpcom-shutdown calling Shutdown() on
// every MediaDecoder and then spinning the main thread event loop until the
// State Machine thread has shutdown. Once the State Machine thread has been
// shutdown, the xpcom-shutdown observer returns.
// every MediaDecoder and then spinning the main thread event loop until all
// SharedThreadPools have shutdown. Once the SharedThreadPools are shutdown,
// all the state machines and their threads have been shutdown, the
// xpcom-shutdown observer returns.
//
// Note that calling the Unregister() functions may result in the singleton
// being deleted, so don't store references to the singleton, always use the
@ -78,18 +76,6 @@ public:
// xpcom-shutdown listener.
void Unregister(MediaDecoder* aDecoder);
// Notifies the MediaShutdownManager of a state machine thread that
// must be tracked. Note that we track State Machine threads individually
// as their shutdown and the construction of a new state machine thread
// can interleave. This stores a strong ref to the state machine.
void Register(StateMachineThread* aThread);
// Notifies the MediaShutdownManager that a StateMachineThread that it was
// tracking has shutdown, and it no longer needs to be shutdown in the
// xpcom-shutdown listener. This drops the strong reference to the
// StateMachineThread, which may destroy it.
void Unregister(StateMachineThread* aThread);
private:
MediaShutdownManager();
@ -108,62 +94,12 @@ private:
// we're shutting down (in the non xpcom-shutdown case).
nsTHashtable<nsRefPtrHashKey<MediaDecoder>> mDecoders;
// References to the state machine threads that we're tracking shutdown
// of. Note that although there is supposed to be a single state machine,
// the construction and shutdown of these can interleave, so we must track
// individual instances of the state machine threads.
// These are strong references.
nsTHashtable<nsRefPtrHashKey<StateMachineThread>> mStateMachineThreads;
// True if we have an XPCOM shutdown observer.
bool mIsObservingShutdown;
bool mIsDoingXPCOMShutDown;
};
// A wrapper for the state machine thread. We must wrap this so that the
// state machine threads can shutdown independently from the
// StateMachineTracker, under the control of the MediaShutdownManager.
// The state machine thread is shutdown naturally when all decoders
// complete their shutdown. So if a new decoder is created just as the
// old state machine thread has shutdown, we need to be able to shutdown
// the old state machine thread independently of the StateMachineTracker
// creating a new state machine thread. Also if this happens we need to
// be able to force both state machine threads to shutdown in the
// MediaShutdownManager, which is why we maintain a set of state machine
// threads, even though there's supposed to only be one alive at once.
// This class does not enforce its own thread safety, the StateMachineTracker
// ensures thread safety when it uses the StateMachineThread.
class StateMachineThread {
public:
StateMachineThread();
~StateMachineThread();
NS_INLINE_DECL_REFCOUNTING(StateMachineThread);
// Creates the wrapped thread.
nsresult Init();
// Returns a reference to the underlying thread. Don't shut this down
// directly, use StateMachineThread::Shutdown() instead.
nsIThread* GetThread();
// Dispatches an event to the main thread to shutdown the wrapped thread.
// The owner's (StateMachineTracker's) reference to the StateMachineThread
// can be dropped, the StateMachineThread will shutdown itself
// asynchronously.
void Shutdown();
// Processes events on the main thread event loop until this thread
// has been shutdown. Use this to block until the asynchronous shutdown
// has complete.
void SpinUntilShutdownComplete();
private:
void ShutdownThread();
nsCOMPtr<nsIThread> mThread;
};
} // namespace mozilla
#endif

Просмотреть файл

@ -240,7 +240,7 @@ MediaStreamGraphImpl::UpdateBufferSufficiencyState(SourceMediaStream* aStream)
}
for (uint32_t i = 0; i < runnables.Length(); ++i) {
runnables[i].mThread->Dispatch(runnables[i].mRunnable, 0);
runnables[i].mTarget->Dispatch(runnables[i].mRunnable, 0);
}
}
@ -2190,7 +2190,7 @@ SourceMediaStream::HaveEnoughBuffered(TrackID aID)
void
SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
nsIThread* aSignalThread, nsIRunnable* aSignalRunnable)
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable)
{
MutexAutoLock lock(mMutex);
TrackData* data = FindDataForTrack(aID);

Просмотреть файл

@ -675,7 +675,7 @@ public:
* does not exist. No op if a runnable is already present for this track.
*/
void DispatchWhenNotEnoughBuffered(TrackID aID,
nsIThread* aSignalThread, nsIRunnable* aSignalRunnable);
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable);
/**
* Indicate that a track has ended. Do not do any more API calls
* affecting this track.
@ -728,13 +728,13 @@ public:
friend class MediaStreamGraphImpl;
struct ThreadAndRunnable {
void Init(nsIThread* aThread, nsIRunnable* aRunnable)
void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
{
mThread = aThread;
mTarget = aTarget;
mRunnable = aRunnable;
}
nsCOMPtr<nsIThread> mThread;
nsCOMPtr<nsIEventTarget> mTarget;
nsCOMPtr<nsIRunnable> mRunnable;
};
enum TrackCommands {

Просмотреть файл

@ -25,7 +25,8 @@ namespace mozilla {
// the pool is shutdown and deleted. Users aren't required to manually
// shutdown the pool, and can release references on any thread. On Windows
// all threads in the pool have MSCOM initialized with COINIT_MULTITHREADED.
class SharedThreadPool : public nsIThreadPool {
class SharedThreadPool : public nsIThreadPool
{
public:
// Gets (possibly creating) the shared thread pool singleton instance with