Bug 1406328 - shut down the MediaCache thread in ShutdownThreads phase. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 06 Oct 2017 17:41:21 +0800
changeset 676680 dc9d604edfccdbf9f19cad057d85b1e191471e77
parent 676567 50846d9b4f2d98c9aa90bef615fde628aaf2925b
child 735016 682db4a58538a0e7abf701a2fed023765743c61e
push id83583
push userjwwang@mozilla.com
push dateMon, 09 Oct 2017 09:35:55 +0000
bugs1406328
milestone58.0a1
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
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();