Bug 1406328 - shut down the MediaCache thread in ShutdownThreads phase.
To avoid leaks caused by Dispatch() failures. See comment 0 for the detail.
MozReview-Commit-ID: 3lYxQNj1GPl
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -8,16 +8,17 @@
#include "ChannelMediaResource.h"
#include "FileBlockCache.h"
#include "MediaBlockCacheBase.h"
#include "MediaPrefs.h"
#include "MediaResource.h"
#include "MemoryBlockCache.h"
#include "mozilla/Attributes.h"
+#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/Telemetry.h"
#include "nsContentUtils.h"
@@ -146,17 +147,17 @@ public:
// Get an instance of a MediaCache (or nullptr if initialization failed).
// aContentLength is the content length if known already, otherwise -1.
// If the length is known and considered small enough, a discrete MediaCache
// with memory backing will be given. Otherwise the one MediaCache with
// file backing will be provided.
static RefPtr<MediaCache> GetMediaCache(int64_t aContentLength);
- nsIEventTarget* OwnerThread() const { return mThread; }
+ nsIEventTarget* OwnerThread() const { return sThread; }
// Brutally flush the cache contents. Main thread only.
void Flush();
// Close all streams associated with private browsing windows. This will
// also remove the blocks from the cache since we don't want to leave any
// traces when PB is done.
void CloseStreamsForPrivateBrowsing();
@@ -261,32 +262,49 @@ public:
return nullptr;
}
private:
MediaCache* mMediaCache;
int64_t mResourceID;
uint32_t mNext;
};
+ // Called during shutdown to clear sThread.
+ void operator=(std::nullptr_t)
+ {
+ nsCOMPtr<nsIThread> thread = sThread.forget();
+ if (thread) {
+ thread->Shutdown();
+ }
+ }
+
protected:
explicit MediaCache(MediaBlockCacheBase* aCache)
: mNextResourceID(1)
, mReentrantMonitor("MediaCache.mReentrantMonitor")
, mBlockCache(aCache)
, mUpdateQueued(false)
#ifdef DEBUG
, mInUpdate(false)
#endif
{
NS_ASSERTION(NS_IsMainThread(), "Only construct MediaCache on main thread");
MOZ_COUNT_CTOR(MediaCache);
MediaCacheFlusher::RegisterMediaCache(this);
- nsresult rv = NS_NewNamedThread("MediaCache", getter_AddRefs(mThread));
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to create a thread for MediaCache.");
+
+ if (!sThreadInit) {
+ sThreadInit = true;
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread("MediaCache", getter_AddRefs(thread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create a thread for MediaCache.");
+ return;
+ }
+ sThread = thread.forget();
+ ClearOnShutdown(this, ShutdownPhase::ShutdownThreads);
}
}
~MediaCache()
{
NS_ASSERTION(NS_IsMainThread(), "Only destroy MediaCache on main thread");
if (this == gMediaCache) {
LOG("~MediaCache(Global file-backed MediaCache)");
@@ -309,21 +327,16 @@ protected:
} else {
LOG("~MediaCache(Memory-backed MediaCache %p)", this);
}
MediaCacheFlusher::UnregisterMediaCache(this);
NS_ASSERTION(mStreams.IsEmpty(), "Stream(s) still open!");
Truncate();
NS_ASSERTION(mIndex.Length() == 0, "Blocks leaked?");
- nsCOMPtr<nsIThread> thread = mThread.forget();
- if (thread) {
- thread->Shutdown();
- }
-
MOZ_COUNT_DTOR(MediaCache);
}
// Find a free or reusable block and return its index. If there are no
// free blocks and no reusable blocks, add a new block to the cache
// and return it. Can return -1 on OOM.
int32_t FindBlockForIncomingData(TimeStamp aNow,
MediaCacheStream* aStream,
@@ -443,23 +456,30 @@ protected:
// True if an event to run Update() has been queued but not processed
bool mUpdateQueued;
#ifdef DEBUG
bool mInUpdate;
#endif
// A list of resource IDs to notify about the change in suspended status.
nsTArray<int64_t> mSuspendedStatusToNotify;
// The thread on which we will run data callbacks from the channels.
- // Could be null if failing to create the thread.
- nsCOMPtr<nsIThread> mThread;
+ // Could be null if failing to create the thread. Note this thread is shared
+ // among all MediaCache instances.
+ static StaticRefPtr<nsIThread> sThread;
+ // True if we've tried to init sThread. Note we try once only so it is safe
+ // to access sThread on all threads.
+ static bool sThreadInit;
};
// Initialized to nullptr by non-local static initialization.
/* static */ MediaCache* MediaCache::gMediaCache;
+/* static */ StaticRefPtr<nsIThread> MediaCache::sThread;
+/* static */ bool MediaCache::sThreadInit = false;
+
NS_IMETHODIMP
MediaCacheFlusher::Observe(nsISupports *aSubject, char const *aTopic, char16_t const *aData)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
if (strcmp(aTopic, "last-pb-context-exited") == 0) {
for (MediaCache* mc : mMediaCaches) {
mc->CloseStreamsForPrivateBrowsing();