Bug 1405962. P1 - give MediaCache a thread on which we will run data callbacks from the HTTP channel. draft
authorJW Wang <jwwang@mozilla.com>
Wed, 13 Sep 2017 16:51:12 +0800
changeset 675835 228b08c8c93437a938ca3e3b6f24bf597690e65d
parent 675465 b5c21a91313941f2219fa5dadbfede60aadaa150
child 675836 7dc29f0c976a5d0b606321a3c9ed64d6f4bf6324
push id83260
push userjwwang@mozilla.com
push dateThu, 05 Oct 2017 23:40:33 +0000
bugs1405962
milestone58.0a1
Bug 1405962. P1 - give MediaCache a thread on which we will run data callbacks from the HTTP channel. MozReview-Commit-ID: Av7bFGx9SW
dom/media/ChannelMediaResource.cpp
dom/media/MediaCache.cpp
dom/media/MediaCache.h
modules/libpref/init/all.js
--- a/dom/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -5,16 +5,17 @@
 
 #include "ChannelMediaResource.h"
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICachingChannel.h"
 #include "nsIClassOfService.h"
 #include "nsIInputStream.h"
+#include "nsIThreadRetargetableRequest.h"
 #include "nsNetUtil.h"
 
 static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
 static const uint32_t HTTP_OK_CODE = 200;
 
 mozilla::LazyLogModule gMediaResourceLog("MediaResource");
 // Debug logging macro with object pointer and class name.
 #define LOG(msg, ...) MOZ_LOG(gMediaResourceLog, mozilla::LogLevel::Debug, \
@@ -293,16 +294,26 @@ ChannelMediaResource::OnStartRequest(nsI
   mChannelStatistics.Start();
   mReopenOnError = false;
 
   mSuspendAgent.UpdateSuspendedStatusIfNeeded();
 
   // Fires an initial progress event.
   owner->DownloadProgressed();
 
+  // TODO: Don't turn this on until we fix all data races.
+  nsCOMPtr<nsIThreadRetargetableRequest> retarget;
+  if (Preferences::GetBool("media.omt_data_delivery.enabled", false) &&
+      (retarget = do_QueryInterface(aRequest)) && mCacheStream.OwnerThread()) {
+    // Note this will not always succeed. We need to handle the case where
+    // all resources sharing the same cache might run their data callbacks
+    // on different threads.
+    retarget->RetargetDeliveryTo(mCacheStream.OwnerThread());
+  }
+
   return NS_OK;
 }
 
 bool
 ChannelMediaResource::IsTransportSeekable()
 {
   return mCacheStream.IsTransportSeekable();
 }
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -138,16 +138,18 @@ 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; }
+
   // 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();
 
@@ -264,16 +266,20 @@ protected:
     , 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.");
+    }
   }
 
   ~MediaCache()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only destroy MediaCache on main thread");
     if (this == gMediaCache) {
       LOG("~MediaCache(Global file-backed MediaCache)");
       // This is the file-backed MediaCache, reset the global pointer.
@@ -295,16 +301,21 @@ 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,
@@ -423,16 +434,19 @@ protected:
   BlockList       mFreeBlocks;
   // 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;
 };
 
 // Initialized to nullptr by non-local static initialization.
 /* static */ MediaCache* MediaCache::gMediaCache;
 
 NS_IMETHODIMP
 MediaCacheFlusher::Observe(nsISupports *aSubject, char const *aTopic, char16_t const *aData)
 {
@@ -1926,21 +1940,16 @@ MediaCacheStream::NotifyDataReceived(uin
   }
 
   LOG("Stream %p DataReceived at %" PRId64 " count=%" PRId64 " aLoadID=%u",
       this,
       mChannelOffset,
       aSize,
       aLoadID);
 
-  // TODO: For now NotifyDataReceived() always runs on the main thread. This
-  // assertion is to make sure our load ID algorithm doesn't go wrong. Remove it
-  // when OMT data delievery is enabled.
-  MOZ_DIAGNOSTIC_ASSERT(mLoadID == aLoadID);
-
   if (mLoadID != aLoadID) {
     // mChannelOffset is updated to a new position when loading a new channel.
     // We should discard the data coming from the old channel so it won't be
     // stored to the wrong positoin.
     return;
   }
   int64_t size = aSize;
   const char* data = aData;
@@ -2649,16 +2658,22 @@ MediaCacheStream::InitAsClone(MediaCache
       mBlocks.AppendElement(-1);
     }
     // Every block is a readahead block for the clone because the clone's initial
     // stream offset is zero
     mMediaCache->AddBlockOwnerAsReadahead(cacheBlockIndex, this, i);
   }
 }
 
+nsIEventTarget*
+MediaCacheStream::OwnerThread() const
+{
+  return mMediaCache->OwnerThread();
+}
+
 nsresult MediaCacheStream::GetCachedRanges(MediaByteRangeSet& aRanges)
 {
   // Take the monitor, so that the cached data ranges can't grow while we're
   // trying to loop over them.
   ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
 
   // We must be pinned while running this, otherwise the cached data ranges may
   // shrink while we're trying to loop over them.
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -9,16 +9,17 @@
 
 #include "Intervals.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 
+class nsIEventTarget;
 class nsIPrincipal;
 
 namespace mozilla {
 // defined in MediaResource.h
 class ChannelMediaResource;
 typedef media::IntervalSet<int64_t> MediaByteRangeSet;
 class MediaResource;
 class ReentrantMonitorAutoEnter;
@@ -205,16 +206,18 @@ public:
   nsresult Init(int64_t aContentLength);
 
   // Set up this stream with the cache, assuming it's for the same data
   // as the aOriginal stream.
   // Exactly one of InitAsClone or Init must be called before any other method
   // on this class.
   void InitAsClone(MediaCacheStream* aOriginal);
 
+  nsIEventTarget* OwnerThread() const;
+
   // These are called on the main thread.
   // Tell us whether the stream is seekable or not. Non-seekable streams
   // will always pass 0 for aOffset to CacheClientSeek. This should only
   // be called while the stream is at channel offset 0. Seekability can
   // change during the lifetime of the MediaCacheStream --- every time
   // we do an HTTP load the seekability may be different (and sometimes
   // is, in practice, due to the effects of caching proxies).
   void SetTransportSeekable(bool aIsTransportSeekable);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -306,16 +306,20 @@ pref("print.use_simplify_page", false);
 // Disable support for MathML
 pref("mathml.disabled",    false);
 
 // Enable scale transform for stretchy MathML operators. See bug 414277.
 pref("mathml.scale_stretchy_operators.enabled", true);
 
 pref("media.dormant-on-pause-timeout-ms", 5000);
 
+// Used by ChannelMediaResource to run data callbacks from HTTP channel
+// off the main thread.
+pref("media.omt_data_delivery.enabled", false);
+
 // File-backed MediaCache size in kilobytes
 pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 30);
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.