Bug 1406328 - shut down the MediaCache thread in ShutdownThreads phase. r=gerald
authorJW Wang <jwwang@mozilla.com>
Fri, 06 Oct 2017 17:41:21 +0800
changeset 385086 2e858715590bdf0bb0b0d26da800609de59ae91b
parent 385085 b7429873f639fca4f386639804f6e40d4a704f20
child 385087 1636e8da864cf9ca99c11ff0d3aabad20d43b045
push id32646
push userarchaeopteryx@coole-files.de
push dateMon, 09 Oct 2017 21:47:29 +0000
treeherdermozilla-central@4afc55e7033c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1406328
milestone58.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
Bug 1406328 - shut down the MediaCache thread in ShutdownThreads phase. r=gerald To avoid leaks caused by Dispatch() failures. See comment 0 for the detail. MozReview-Commit-ID: 3lYxQNj1GPl
dom/media/MediaCache.cpp
--- 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();