Bug 1405962. P1 - give MediaCache a thread on which we will run data callbacks from the HTTP channel.
MozReview-Commit-ID: Av7bFGx9SW
--- 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.