author | Chris Pearce <cpearce@mozilla.com> |
Thu, 23 Jan 2014 17:21:05 +1300 | |
changeset 164784 | b61f756f9f54d23499ad1302bfb99bd0b4c8eef6 |
parent 164783 | de7009282021ba162819ccdac7de557cca0ad929 |
child 164785 | a90eb14cad0b541b9ee7c762ab1d9a216b71ac9b |
push id | 26060 |
push user | cbook@mozilla.com |
push date | Thu, 23 Jan 2014 09:18:25 +0000 |
treeherder | mozilla-central@d418cc97bacb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kinetik |
bugs | 962354 |
milestone | 29.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
|
new file mode 100644 --- /dev/null +++ b/content/media/SharedThreadPool.cpp @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SharedThreadPool.h" +#include "mozilla/Monitor.h" +#include "mozilla/StaticPtr.h" +#include "nsDataHashtable.h" +#include "VideoUtils.h" +#include "nsXPCOMCIDInternal.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/Preferences.h" + +#ifdef XP_WIN +// Required to init MSCOM by MSCOMInitThreadPoolListener. +#include <Objbase.h> +#endif + +namespace mozilla { + +// Created and destroyed on the main thread. +static StaticAutoPtr<ReentrantMonitor> sMonitor; + +// Hashtable, maps thread pool name to SharedThreadPool instance. +// Modified only on the main thread. +static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools; + +static already_AddRefed<nsIThreadPool> +CreateThreadPool(const nsCString& aName); + +static void +DestroySharedThreadPoolHashTable(); + +void +SharedThreadPool::EnsureInitialized() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (sMonitor || sPools) { + // Already initalized. + return; + } + sMonitor = new ReentrantMonitor("SharedThreadPool"); + sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>(); +} + +class ShutdownPoolsEvent : public nsRunnable { +public: + NS_IMETHODIMP Run() { + MOZ_ASSERT(NS_IsMainThread()); + DestroySharedThreadPoolHashTable(); + return NS_OK; + } +}; + +static void +DestroySharedThreadPoolHashTable() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(sMonitor && sPools); + if (!sPools->Count()) { + // No more SharedThreadPool singletons. Delete the hash table. + // Note we don't need to lock sMonitor, since we only modify the + // hash table on the main thread, and if the hash table is empty + // there are no external references into its contents. + sPools = nullptr; + sMonitor = nullptr; + } +} + +TemporaryRef<SharedThreadPool> +SharedThreadPool::Get(const nsCString& aName) +{ + MOZ_ASSERT(NS_IsMainThread()); + EnsureInitialized(); + MOZ_ASSERT(sMonitor); + ReentrantMonitorAutoEnter mon(*sMonitor); + SharedThreadPool* pool = nullptr; + if (!sPools->Get(aName, &pool)) { + nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName)); + NS_ENSURE_TRUE(threadPool, nullptr); + pool = new SharedThreadPool(aName, threadPool); + sPools->Put(aName, pool); + } + MOZ_ASSERT(pool); + RefPtr<SharedThreadPool> instance(pool); + return instance.forget(); +} + +NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::AddRef(void) +{ + MOZ_ASSERT(sMonitor); + ReentrantMonitorAutoEnter mon(*sMonitor); + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt count = ++mRefCnt; + NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this)); + return count; +} + +NS_IMETHODIMP_(nsrefcnt) SharedThreadPool::Release(void) +{ + MOZ_ASSERT(sMonitor); + bool dispatchShutdownEvent; + { + ReentrantMonitorAutoEnter mon(*sMonitor); + nsrefcnt count = --mRefCnt; + NS_LOG_RELEASE(this, count, "SharedThreadPool"); + if (count) { + return count; + } + + // Zero refcount. Must shutdown and then delete the thread pool. + + // First, dispatch an event to the main thread to call Shutdown() on + // the nsIThreadPool. The Runnable here will add a refcount to the pool, + // and when the Runnable releases the nsIThreadPool it will be deleted. + RefPtr<nsIRunnable> r = NS_NewRunnableMethod(mPool, &nsIThreadPool::Shutdown); + NS_DispatchToMainThread(r); + + // Remove SharedThreadPool from table of pools. + sPools->Remove(mName); + MOZ_ASSERT(!sPools->Get(mName)); + + // Stabilize refcount, so that if something in the dtor QIs, + // it won't explode. + mRefCnt = 1; + + delete this; + + dispatchShutdownEvent = sPools->Count() == 0; + } + if (dispatchShutdownEvent) { + // No more SharedThreadPools alive. Destroy the hash table. + // Ensure that we only run on the main thread. + // Do this in an event so that if something holds the monitor we won't + // be deleting the monitor while it's held. + NS_DispatchToMainThread(new ShutdownPoolsEvent(), NS_DISPATCH_NORMAL); + } + return 0; +} + +NS_IMPL_QUERY_INTERFACE1(SharedThreadPool, nsIThreadPool) + +SharedThreadPool::SharedThreadPool(const nsCString& aName, + nsIThreadPool* aPool) + : mName(aName) + , mPool(aPool) + , mRefCnt(0) +{ + mEventTarget = do_QueryInterface(aPool); +} + +SharedThreadPool::~SharedThreadPool() +{ +} + +#ifdef XP_WIN + +// Thread pool listener which ensures that MSCOM is initialized and +// deinitialized on the thread pool thread. We may call into WMF or +// DirectShow on this thread, so we need MSCOM working. +class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener { +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITHREADPOOLLISTENER +}; + +NS_IMPL_ISUPPORTS1(MSCOMInitThreadPoolListener, nsIThreadPoolListener) + +NS_IMETHODIMP +MSCOMInitThreadPoolListener::OnThreadCreated() +{ + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hr)) { + NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread."); + } + return NS_OK; +} + +NS_IMETHODIMP +MSCOMInitThreadPoolListener::OnThreadShuttingDown() +{ + CoUninitialize(); + return NS_OK; +} + +#endif // XP_WIN + +static already_AddRefed<nsIThreadPool> +CreateThreadPool(const nsCString& aName) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv; + nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = pool->SetName(aName); + NS_ENSURE_SUCCESS(rv, nullptr); + + // We limit the number of threads that we use for media. Note that the + // default thread limit is the same as the idle limit so that we're not + // constantly creating and destroying threads (see Bug 881954). When the + // thread pool threads shutdown they dispatch an event to the main thread + // to call nsIThread::Shutdown(), and if we're very busy that can take a + // while to run, and we end up with dozens of extra threads. Note that + // threads that are idle for 60 seconds are shutdown naturally. + rv = pool->SetThreadLimit( + Preferences::GetUint("media.thread-pool.thread-limit", 4)); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = pool->SetIdleThreadLimit( + Preferences::GetUint("media.thread-pool.idle-thread-limit", 4)); + NS_ENSURE_SUCCESS(rv, nullptr); + +#ifdef XP_WIN + // Ensure MSCOM is initialized on the thread pools threads. + nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener(); + rv = pool->SetListener(listener); + NS_ENSURE_SUCCESS(rv, nullptr); +#endif + + return pool.forget(); +} + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/content/media/SharedThreadPool.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SharedThreadPool_h_ +#define SharedThreadPool_h_ + +#include <queue> +#include "mozilla/RefPtr.h" +#include "nsThreadUtils.h" +#include "nsIThreadPool.h" +#include "nsISupports.h" +#include "nsISupportsImpl.h" +#include "nsCOMPtr.h" + +namespace mozilla { + +// Wrapper that makes an nsIThreadPool a singleton, and provides a +// consistent threadsafe interface to get instances. Callers simply get a +// SharedThreadPool by the name of its nsIThreadPool. All get requests of +// the same name get the same SharedThreadPool. Users must store a reference +// to the pool, and when the last reference to a SharedThreadPool is dropped +// 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 { +public: + + // Gets (possibly creating) the shared thread pool singleton instance with + // thread pool named aName. + // *Must* be called on the main thread. + static TemporaryRef<SharedThreadPool> Get(const nsCString& aName); + + // We implement custom threadsafe AddRef/Release pair, that destroys the + // the shared pool singleton when the refcount drops to 0. The addref/release + // are implemented using locking, so it's not recommended that you use them + // in a tight loop. + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr); + NS_IMETHOD_(nsrefcnt) AddRef(void); + NS_IMETHOD_(nsrefcnt) Release(void); + + // Forward behaviour to wrapped thread pool implementation. + NS_FORWARD_SAFE_NSITHREADPOOL(mPool); + NS_FORWARD_SAFE_NSIEVENTTARGET(mEventTarget); + +private: + + // Creates necessary statics. + // Main thread only. + static void EnsureInitialized(); + + // Creates a singleton SharedThreadPool wrapper around aPool. + // aName is the name of the aPool, and is used to lookup the + // SharedThreadPool in the hash table of all created pools. + SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool); + virtual ~SharedThreadPool(); + + // Name of mPool. + const nsCString mName; + + // Thread pool being wrapped. + nsCOMPtr<nsIThreadPool> mPool; + + // Refcount. We implement custom ref counting so that the thread pool is + // shutdown in a threadsafe manner and singletonness is preserved. + nsrefcnt mRefCnt; + + // mPool QI'd to nsIEventTarget. We cache this, so that we can use + // NS_FORWARD_SAFE_NSIEVENTTARGET above. + nsCOMPtr<nsIEventTarget> mEventTarget; +}; + +} // namespace mozilla + +#endif // SharedThreadPool_h_ \ No newline at end of file
--- a/content/media/moz.build +++ b/content/media/moz.build @@ -80,16 +80,17 @@ EXPORTS += [ 'MediaMetadataManager.h', 'MediaRecorder.h', 'MediaResource.h', 'MediaSegment.h', 'MediaStreamGraph.h', 'MP3FrameParser.h', 'RtspMediaResource.h', 'SharedBuffer.h', + 'SharedThreadPool.h', 'StreamBuffer.h', 'TimeVarying.h', 'TrackUnionStream.h', 'VideoFrameContainer.h', 'VideoSegment.h', 'VideoUtils.h', 'VorbisUtils.h', ] @@ -126,16 +127,17 @@ UNIFIED_SOURCES += [ 'MediaDecoderStateMachine.cpp', 'MediaRecorder.cpp', 'MediaResource.cpp', 'MediaShutdownManager.cpp', 'MediaStreamGraph.cpp', 'MediaStreamTrack.cpp', 'MP3FrameParser.cpp', 'RtspMediaResource.cpp', + 'SharedThreadPool.cpp', 'StreamBuffer.cpp', 'TextTrack.cpp', 'TextTrackCue.cpp', 'TextTrackCueList.cpp', 'TextTrackList.cpp', 'TextTrackRegion.cpp', 'TextTrackRegionList.cpp', 'VideoFrameContainer.cpp',
--- a/content/media/wmf/WMFByteStream.cpp +++ b/content/media/wmf/WMFByteStream.cpp @@ -14,97 +14,29 @@ #include "WMFUtils.h" #include "MediaResource.h" #include "nsISeekableStream.h" #include "mozilla/RefPtr.h" #include "nsIThreadPool.h" #include "nsXPCOMCIDInternal.h" #include "nsComponentManagerUtils.h" #include "mozilla/DebugOnly.h" +#include "SharedThreadPool.h" #include <algorithm> #include <cassert> namespace mozilla { #ifdef PR_LOGGING PRLogModuleInfo* gWMFByteStreamLog = nullptr; #define WMF_BS_LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__)) #else #define WMF_BS_LOG(...) #endif -// Limit the number of threads that we use for IO. -static const uint32_t NumWMFIoThreads = 4; - -// Thread pool listener which ensures that MSCOM is initialized and -// deinitialized on the thread pool thread. We can call back into WMF -// on this thread, so we need MSCOM working. -class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener { -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSITHREADPOOLLISTENER -}; - -NS_IMPL_ISUPPORTS1(ThreadPoolListener, nsIThreadPoolListener) - -NS_IMETHODIMP -ThreadPoolListener::OnThreadCreated() -{ - HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); - if (FAILED(hr)) { - NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread."); - } - return NS_OK; -} - -NS_IMETHODIMP -ThreadPoolListener::OnThreadShuttingDown() -{ - CoUninitialize(); - return NS_OK; -} - -// Thread pool on which read requests are processed. -// This is created and destroyed on the main thread only. -static nsIThreadPool* sThreadPool = nullptr; - -// Counter of the number of WMFByteStreams that are instantiated and that need -// the thread pool. This is read/write on the main thread only. -static int32_t sThreadPoolRefCnt = 0; - -class ReleaseWMFByteStreamResourcesEvent MOZ_FINAL : public nsRunnable { -public: - ReleaseWMFByteStreamResourcesEvent(already_AddRefed<MediaResource> aResource) - : mResource(aResource) {} - virtual ~ReleaseWMFByteStreamResourcesEvent() {} - NS_IMETHOD Run() { - NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); - // Explicitly release the MediaResource reference. We *must* do this on - // the main thread, so we must explicitly release it here, we can't rely - // on the destructor to release it, since if this event runs before its - // dispatch call returns the destructor may run on the non-main thread. - mResource = nullptr; - NS_ASSERTION(sThreadPoolRefCnt > 0, "sThreadPoolRefCnt Should be non-negative"); - sThreadPoolRefCnt--; - if (sThreadPoolRefCnt == 0) { - NS_ASSERTION(sThreadPool != nullptr, "Should have thread pool ref if sThreadPoolRefCnt==0."); - // Note: store ref to thread pool, then clear global ref, then - // Shutdown() using the stored ref. Events can run during the Shutdown() - // call, so if we release after calling Shutdown(), another event may - // have incremented the refcnt in the meantime, and have a dangling - // pointer to the now destroyed threadpool! - nsCOMPtr<nsIThreadPool> pool = sThreadPool; - NS_IF_RELEASE(sThreadPool); - pool->Shutdown(); - } - return NS_OK; - } - nsRefPtr<MediaResource> mResource; -}; - WMFByteStream::WMFByteStream(MediaResource* aResource, WMFSourceReaderCallback* aSourceReaderCallback) : mSourceReaderCallback(aSourceReaderCallback), mResource(aResource), mReentrantMonitor("WMFByteStream.Data"), mOffset(0), mIsShutdown(false) { @@ -118,63 +50,26 @@ WMFByteStream::WMFByteStream(MediaResour #endif WMF_BS_LOG("[%p] WMFByteStream CTOR", this); MOZ_COUNT_CTOR(WMFByteStream); } WMFByteStream::~WMFByteStream() { MOZ_COUNT_DTOR(WMFByteStream); - // The WMFByteStream can be deleted from a thread pool thread, so we - // dispatch an event to the main thread to deref the thread pool and - // deref the MediaResource. - nsCOMPtr<nsIRunnable> event = - new ReleaseWMFByteStreamResourcesEvent(mResource.forget()); - NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); WMF_BS_LOG("[%p] WMFByteStream DTOR", this); } nsresult WMFByteStream::Init() { NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); - if (!sThreadPool) { - nsresult rv; - nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - sThreadPool = pool; - NS_ADDREF(sThreadPool); - - rv = sThreadPool->SetName(NS_LITERAL_CSTRING("WMFByteStream Async Read Pool")); - NS_ENSURE_SUCCESS(rv, rv); - - // We limit the number of threads that we use for IO. Note that the thread - // limit is the same as the idle limit so that we're not constantly creating - // and destroying threads. When the thread pool threads shutdown they - // dispatch an event to the main thread to call nsIThread::Shutdown(), - // and if we're very busy that can take a while to run, and we end up with - // dozens of extra threads. Note that threads that are idle for 60 seconds - // are shutdown naturally. - rv = sThreadPool->SetThreadLimit(NumWMFIoThreads); - NS_ENSURE_SUCCESS(rv, rv); - - rv = sThreadPool->SetIdleThreadLimit(NumWMFIoThreads); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIThreadPoolListener> listener = new ThreadPoolListener(); - rv = sThreadPool->SetListener(listener); - NS_ENSURE_SUCCESS(rv, rv); - } - sThreadPoolRefCnt++; - - // Store a ref to the thread pool, so that we keep the pool alive as long as - // we're alive. - mThreadPool = sThreadPool; + mThreadPool = SharedThreadPool::Get(NS_LITERAL_CSTRING("WMFByteStream IO")); + NS_ENSURE_TRUE(mThreadPool, NS_ERROR_FAILURE); NS_ConvertUTF8toUTF16 contentTypeUTF16(mResource->GetContentType()); if (!contentTypeUTF16.IsEmpty()) { HRESULT hr = wmf::MFCreateAttributes(byRef(mAttributes), 1); NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); hr = mAttributes->SetString(MF_BYTESTREAM_CONTENT_TYPE, contentTypeUTF16.get());
--- a/content/media/wmf/WMFByteStream.h +++ b/content/media/wmf/WMFByteStream.h @@ -10,23 +10,22 @@ #include "nsISupportsImpl.h" #include "nsCOMPtr.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/Attributes.h" #include "nsAutoPtr.h" #include "mozilla/RefPtr.h" -class nsIThreadPool; - namespace mozilla { class MediaResource; class ReadRequest; class WMFSourceReaderCallback; +class SharedThreadPool; // Wraps a MediaResource around an IMFByteStream interface, so that it can // be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue // on which blocking I/O is performed. The SourceReader requests reads // asynchronously using {Begin,End}Read(), and more rarely synchronously // using Read(). // // Note: This implementation attempts to be bug-compatible with Windows Media @@ -118,17 +117,17 @@ private: // call this function. nsresult Read(ReadRequest* aRequestState); // Returns true if the current position of the stream is at end of stream. bool IsEOS(); // Reference to the thread pool in which we perform the reads asynchronously. // Note this is pool is shared amongst all active WMFByteStreams. - nsCOMPtr<nsIThreadPool> mThreadPool; + RefPtr<SharedThreadPool> mThreadPool; // Reference to the source reader's callback. We use this reference to // notify threads waiting on a ReadSample() callback to stop waiting // if the stream is closed, which happens when the media element is // shutdown. RefPtr<WMFSourceReaderCallback> mSourceReaderCallback; // Resource we're wrapping.