--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -137,16 +137,19 @@ MediaCacheFlusher::UnregisterMediaCache(
if (gMediaCacheFlusher->mMediaCaches.Length() == 0) {
gMediaCacheFlusher = nullptr;
}
}
class MediaCache
{
+ using AutoLock = ReentrantMonitorAutoEnter;
+ using AutoUnlock = ReentrantMonitorAutoExit;
+
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaCache)
friend class MediaCacheStream::BlockList;
typedef MediaCacheStream::BlockList BlockList;
static const int64_t BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE;
// Get an instance of a MediaCache (or nullptr if initialization failed).
@@ -166,86 +169,91 @@ public:
// traces when PB is done.
void CloseStreamsForPrivateBrowsing();
// Cache-file access methods. These are the lowest-level cache methods.
// mReentrantMonitor must be held; these can be called on any thread.
// This can return partial reads.
// Note mReentrantMonitor will be dropped while doing IO. The caller need
// to handle changes happening when the monitor is not held.
- nsresult ReadCacheFile(int64_t aOffset, void* aData, int32_t aLength,
+ nsresult ReadCacheFile(AutoLock&,
+ int64_t aOffset,
+ void* aData,
+ int32_t aLength,
int32_t* aBytes);
// The generated IDs are always positive.
- int64_t AllocateResourceID()
- {
- mReentrantMonitor.AssertCurrentThreadIn();
- return ++mNextResourceID;
- }
+ int64_t AllocateResourceID(AutoLock&) { return ++mNextResourceID; }
// mReentrantMonitor must be held, called on main thread.
// These methods are used by the stream to set up and tear down streams,
// and to handle reads and writes.
// Add aStream to the list of streams.
- void OpenStream(MediaCacheStream* aStream, bool aIsClone = false);
+ void OpenStream(AutoLock&, MediaCacheStream* aStream, bool aIsClone = false);
// Remove aStream from the list of streams.
void ReleaseStream(MediaCacheStream* aStream);
// Free all blocks belonging to aStream.
- void ReleaseStreamBlocks(MediaCacheStream* aStream);
+ void ReleaseStreamBlocks(AutoLock&, MediaCacheStream* aStream);
// Find a cache entry for this data, and write the data into it
void AllocateAndWriteBlock(
+ AutoLock&,
MediaCacheStream* aStream,
int32_t aStreamBlockIndex,
MediaCacheStream::ReadMode aMode,
Span<const uint8_t> aData1,
Span<const uint8_t> aData2 = Span<const uint8_t>());
// mReentrantMonitor must be held; can be called on any thread
// Notify the cache that a seek has been requested. Some blocks may
// need to change their class between PLAYED_BLOCK and READAHEAD_BLOCK.
// This does not trigger channel seeks directly, the next Update()
// will do that if necessary. The caller will call QueueUpdate().
- void NoteSeek(MediaCacheStream* aStream, int64_t aOldOffset);
+ void NoteSeek(AutoLock&, MediaCacheStream* aStream, int64_t aOldOffset);
// Notify the cache that a block has been read from. This is used
// to update last-use times. The block may not actually have a
// cache entry yet since Read can read data from a stream's
// in-memory mPartialBlockBuffer while the block is only partly full,
// and thus hasn't yet been committed to the cache. The caller will
// call QueueUpdate().
- void NoteBlockUsage(MediaCacheStream* aStream, int32_t aBlockIndex,
+ void NoteBlockUsage(AutoLock&,
+ MediaCacheStream* aStream,
+ int32_t aBlockIndex,
int64_t aStreamOffset,
- MediaCacheStream::ReadMode aMode, TimeStamp aNow);
+ MediaCacheStream::ReadMode aMode,
+ TimeStamp aNow);
// Mark aStream as having the block, adding it as an owner.
- void AddBlockOwnerAsReadahead(int32_t aBlockIndex, MediaCacheStream* aStream,
+ void AddBlockOwnerAsReadahead(AutoLock&,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream,
int32_t aStreamBlockIndex);
// This queues a call to Update() on the main thread.
- void QueueUpdate();
+ void QueueUpdate(AutoLock&);
// Notify all streams for the resource ID that the suspended status changed
// at the end of MediaCache::Update.
- void QueueSuspendedStatusUpdate(int64_t aResourceID);
+ void QueueSuspendedStatusUpdate(AutoLock&, int64_t aResourceID);
// Updates the cache state asynchronously on the main thread:
// -- try to trim the cache back to its desired size, if necessary
// -- suspend channels that are going to read data that's lower priority
// than anything currently cached
// -- resume channels that are going to read data that's higher priority
// than something currently cached
// -- seek channels that need to seek to a new location
void Update();
#ifdef DEBUG_VERIFY_CACHE
// Verify invariants, especially block list invariants
- void Verify();
+ void Verify(AutoLock&);
#else
- void Verify() {}
+ void Verify(AutoLock&) {}
#endif
- ReentrantMonitor& GetReentrantMonitor() { return mReentrantMonitor; }
+ ReentrantMonitor& Monitor() { return mMonitor; }
/**
* An iterator that makes it easy to iterate through all streams that
* have a given resource ID and are not closed.
* Can be used on the main thread or while holding the media cache lock.
*/
class ResourceStreamIterator
{
@@ -269,17 +277,17 @@ public:
private:
MediaCache* mMediaCache;
int64_t mResourceID;
uint32_t mNext;
};
protected:
explicit MediaCache(MediaBlockCacheBase* aCache)
- : mReentrantMonitor("MediaCache.mReentrantMonitor")
+ : mMonitor("MediaCache.mMonitor")
, mBlockCache(aCache)
, mUpdateQueued(false)
#ifdef DEBUG
, mInUpdate(false)
#endif
{
NS_ASSERTION(NS_IsMainThread(), "Only construct MediaCache on main thread");
MOZ_COUNT_CTOR(MediaCache);
@@ -316,36 +324,39 @@ protected:
NS_ASSERTION(mIndex.Length() == 0, "Blocks leaked?");
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,
+ int32_t FindBlockForIncomingData(AutoLock&,
+ TimeStamp aNow,
MediaCacheStream* aStream,
int32_t aStreamBlockIndex);
// Find a reusable block --- a free block, if there is one, otherwise
// the reusable block with the latest predicted-next-use, or -1 if
// there aren't any freeable blocks. Only block indices less than
// aMaxSearchBlockIndex are considered. If aForStream is non-null,
// then aForStream and aForStreamBlock indicate what media data will
// be placed; FindReusableBlock will favour returning free blocks
// near other blocks for that point in the stream.
- int32_t FindReusableBlock(TimeStamp aNow,
+ int32_t FindReusableBlock(AutoLock&,
+ TimeStamp aNow,
MediaCacheStream* aForStream,
int32_t aForStreamBlock,
int32_t aMaxSearchBlockIndex);
- bool BlockIsReusable(int32_t aBlockIndex);
+ bool BlockIsReusable(AutoLock&, int32_t aBlockIndex);
// Given a list of blocks sorted with the most reusable blocks at the
// end, find the last block whose stream is not pinned (if any)
// and whose cache entry index is less than aBlockIndexLimit
// and append it to aResult.
- void AppendMostReusableBlock(BlockList* aBlockList,
+ void AppendMostReusableBlock(AutoLock&,
+ BlockList* aBlockList,
nsTArray<uint32_t>* aResult,
int32_t aBlockIndexLimit);
enum BlockClass {
// block belongs to mMetadataBlockList because data has been consumed
// from it in "metadata mode" --- in particular blocks read during
// Ogg seeks go into this class. These blocks may have played data
// in them too.
@@ -375,40 +386,47 @@ protected:
struct Block {
// Free blocks have an empty mOwners array
nsTArray<BlockOwner> mOwners;
};
// Get the BlockList that the block should belong to given its
// current owner
- BlockList* GetListForBlock(BlockOwner* aBlock);
+ BlockList* GetListForBlock(AutoLock&, BlockOwner* aBlock);
// Get the BlockOwner for the given block index and owning stream
// (returns null if the stream does not own the block)
- BlockOwner* GetBlockOwner(int32_t aBlockIndex, MediaCacheStream* aStream);
+ BlockOwner* GetBlockOwner(AutoLock&,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream);
// Returns true iff the block is free
bool IsBlockFree(int32_t aBlockIndex)
{ return mIndex[aBlockIndex].mOwners.IsEmpty(); }
// Add the block to the free list and mark its streams as not having
// the block in cache
- void FreeBlock(int32_t aBlock);
+ void FreeBlock(AutoLock&, int32_t aBlock);
// Mark aStream as not having the block, removing it as an owner. If
// the block has no more owners it's added to the free list.
- void RemoveBlockOwner(int32_t aBlockIndex, MediaCacheStream* aStream);
+ void RemoveBlockOwner(AutoLock&,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream);
// Swap all metadata associated with the two blocks. The caller
// is responsible for swapping up any cache file state.
- void SwapBlocks(int32_t aBlockIndex1, int32_t aBlockIndex2);
+ void SwapBlocks(AutoLock&, int32_t aBlockIndex1, int32_t aBlockIndex2);
// Insert the block into the readahead block list for the stream
// at the right point in the list.
- void InsertReadaheadBlock(BlockOwner* aBlockOwner, int32_t aBlockIndex);
+ void InsertReadaheadBlock(AutoLock&,
+ BlockOwner* aBlockOwner,
+ int32_t aBlockIndex);
// Guess the duration until block aBlock will be next used
- TimeDuration PredictNextUse(TimeStamp aNow, int32_t aBlock);
+ TimeDuration PredictNextUse(AutoLock&, TimeStamp aNow, int32_t aBlock);
// Guess the duration until the next incoming data on aStream will be used
- TimeDuration PredictNextUseForIncomingData(MediaCacheStream* aStream);
+ TimeDuration PredictNextUseForIncomingData(AutoLock&,
+ MediaCacheStream* aStream);
// Truncate the file and index array if there are free blocks at the
// end
void Truncate();
// There is at most one file-backed media cache.
// It is owned by all MediaCacheStreams that use it.
// This is a raw pointer set by GetMediaCache(), and reset by ~MediaCache(),
@@ -417,17 +435,17 @@ protected:
// This member is main-thread only. It's used to allocate unique
// resource IDs to streams.
int64_t mNextResourceID = 0;
// The monitor protects all the data members here. Also, off-main-thread
// readers that need to block will Wait() on this monitor. When new
// data becomes available in the cache, we NotifyAll() on this monitor.
- ReentrantMonitor mReentrantMonitor;
+ ReentrantMonitor mMonitor;
// This is only written while on the main thread and the monitor is held.
// Thus, it can be safely read from the main thread or while holding the monitor.
nsTArray<MediaCacheStream*> mStreams;
// The Blocks describing the cache entries.
nsTArray<Block> mIndex;
// Keep track for highest number of blocks used, for telemetry purposes.
int32_t mIndexWatermark = 0;
// Keep track for highest number of blocks owners, for telemetry purposes.
@@ -675,20 +693,20 @@ MediaCacheStream::BlockList::NotifyBlock
e2->mPrevBlock = e2Prev;
}
}
void
MediaCache::Flush()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ AutoLock lock(mMonitor);
for (uint32_t blockIndex = 0; blockIndex < mIndex.Length(); ++blockIndex) {
- FreeBlock(blockIndex);
+ FreeBlock(lock, blockIndex);
}
// Truncate index array.
Truncate();
NS_ASSERTION(mIndex.Length() == 0, "Blocks leaked?");
// Reset block cache to its pristine state.
mBlockCache->Flush();
}
@@ -768,28 +786,30 @@ MediaCache::GetMediaCache(int64_t aConte
LOG("GetMediaCache(%" PRIi64 ") -> Failed to create file-backed MediaCache",
aContentLength);
}
return gMediaCache;
}
nsresult
-MediaCache::ReadCacheFile(
- int64_t aOffset, void* aData, int32_t aLength, int32_t* aBytes)
+MediaCache::ReadCacheFile(AutoLock&,
+ int64_t aOffset,
+ void* aData,
+ int32_t aLength,
+ int32_t* aBytes)
{
- mReentrantMonitor.AssertCurrentThreadIn();
RefPtr<MediaBlockCacheBase> blockCache = mBlockCache;
if (!blockCache) {
return NS_ERROR_FAILURE;
}
{
// Since the monitor might be acquired on the main thread, we need to drop
// the monitor while doing IO in order not to block the main thread.
- ReentrantMonitorAutoExit unlock(mReentrantMonitor);
+ AutoUnlock unlock(mMonitor);
return blockCache->Read(
aOffset, reinterpret_cast<uint8_t*>(aData), aLength, aBytes);
}
}
// Allowed range is whatever can be accessed with an int32_t block index.
static bool
IsOffsetAllowed(int64_t aOffset)
@@ -824,96 +844,93 @@ OffsetInBlock(int64_t aOffset)
{
// Still check for allowed range in debug builds, to catch out-of-range
// issues early during development.
MOZ_ASSERT(IsOffsetAllowed(aOffset));
return int32_t(aOffset % MediaCache::BLOCK_SIZE);
}
int32_t
-MediaCache::FindBlockForIncomingData(TimeStamp aNow,
+MediaCache::FindBlockForIncomingData(AutoLock& aLock,
+ TimeStamp aNow,
MediaCacheStream* aStream,
int32_t aStreamBlockIndex)
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- mReentrantMonitor.AssertCurrentThreadIn();
int32_t blockIndex =
- FindReusableBlock(aNow, aStream, aStreamBlockIndex, INT32_MAX);
+ FindReusableBlock(aLock, aNow, aStream, aStreamBlockIndex, INT32_MAX);
if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
// The block returned is already allocated.
// Don't reuse it if a) there's room to expand the cache or
// b) the data we're going to store in the free block is not higher
// priority than the data already stored in the free block.
// The latter can lead us to go over the cache limit a bit.
if ((mIndex.Length() < uint32_t(mBlockCache->GetMaxBlocks()) ||
blockIndex < 0 ||
- PredictNextUseForIncomingData(aStream) >=
- PredictNextUse(aNow, blockIndex))) {
+ PredictNextUseForIncomingData(aLock, aStream) >=
+ PredictNextUse(aLock, aNow, blockIndex))) {
blockIndex = mIndex.Length();
if (!mIndex.AppendElement())
return -1;
mIndexWatermark = std::max(mIndexWatermark, blockIndex + 1);
mFreeBlocks.AddFirstBlock(blockIndex);
return blockIndex;
}
}
return blockIndex;
}
bool
-MediaCache::BlockIsReusable(int32_t aBlockIndex)
+MediaCache::BlockIsReusable(AutoLock&, int32_t aBlockIndex)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block = &mIndex[aBlockIndex];
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
MediaCacheStream* stream = block->mOwners[i].mStream;
if (stream->mPinCount > 0 ||
uint32_t(OffsetToBlockIndex(stream->mStreamOffset)) ==
block->mOwners[i].mStreamBlock) {
return false;
}
}
return true;
}
void
-MediaCache::AppendMostReusableBlock(BlockList* aBlockList,
- nsTArray<uint32_t>* aResult,
- int32_t aBlockIndexLimit)
+MediaCache::AppendMostReusableBlock(AutoLock& aLock,
+ BlockList* aBlockList,
+ nsTArray<uint32_t>* aResult,
+ int32_t aBlockIndexLimit)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
int32_t blockIndex = aBlockList->GetLastBlock();
if (blockIndex < 0)
return;
do {
// Don't consider blocks for pinned streams, or blocks that are
// beyond the specified limit, or a block that contains a stream's
// current read position (such a block contains both played data
// and readahead data)
- if (blockIndex < aBlockIndexLimit && BlockIsReusable(blockIndex)) {
+ if (blockIndex < aBlockIndexLimit && BlockIsReusable(aLock, blockIndex)) {
aResult->AppendElement(blockIndex);
return;
}
blockIndex = aBlockList->GetPrevBlock(blockIndex);
} while (blockIndex >= 0);
}
int32_t
-MediaCache::FindReusableBlock(TimeStamp aNow,
- MediaCacheStream* aForStream,
- int32_t aForStreamBlock,
- int32_t aMaxSearchBlockIndex)
+MediaCache::FindReusableBlock(AutoLock& aLock,
+ TimeStamp aNow,
+ MediaCacheStream* aForStream,
+ int32_t aForStreamBlock,
+ int32_t aMaxSearchBlockIndex)
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- mReentrantMonitor.AssertCurrentThreadIn();
uint32_t length = std::min(uint32_t(aMaxSearchBlockIndex), uint32_t(mIndex.Length()));
if (aForStream && aForStreamBlock > 0 &&
uint32_t(aForStreamBlock) <= aForStream->mBlocks.Length()) {
int32_t prevCacheBlock = aForStream->mBlocks[aForStreamBlock - 1];
if (prevCacheBlock >= 0) {
uint32_t freeBlockScanEnd =
@@ -941,44 +958,44 @@ MediaCache::FindReusableBlock(TimeStamp
AutoTArray<uint32_t,8> candidates;
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
MediaCacheStream* stream = mStreams[i];
if (stream->mPinCount > 0) {
// No point in even looking at this stream's blocks
continue;
}
- AppendMostReusableBlock(&stream->mMetadataBlocks, &candidates, length);
- AppendMostReusableBlock(&stream->mPlayedBlocks, &candidates, length);
+ AppendMostReusableBlock(
+ aLock, &stream->mMetadataBlocks, &candidates, length);
+ AppendMostReusableBlock(aLock, &stream->mPlayedBlocks, &candidates, length);
// Don't consider readahead blocks in non-seekable streams. If we
// remove the block we won't be able to seek back to read it later.
if (stream->mIsTransportSeekable) {
- AppendMostReusableBlock(&stream->mReadaheadBlocks, &candidates, length);
+ AppendMostReusableBlock(
+ aLock, &stream->mReadaheadBlocks, &candidates, length);
}
}
TimeDuration latestUse;
int32_t latestUseBlock = -1;
for (uint32_t i = 0; i < candidates.Length(); ++i) {
- TimeDuration nextUse = PredictNextUse(aNow, candidates[i]);
+ TimeDuration nextUse = PredictNextUse(aLock, aNow, candidates[i]);
if (nextUse > latestUse) {
latestUse = nextUse;
latestUseBlock = candidates[i];
}
}
return latestUseBlock;
}
MediaCache::BlockList*
-MediaCache::GetListForBlock(BlockOwner* aBlock)
+MediaCache::GetListForBlock(AutoLock&, BlockOwner* aBlock)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
switch (aBlock->mClass) {
case METADATA_BLOCK:
NS_ASSERTION(aBlock->mStream, "Metadata block has no stream?");
return &aBlock->mStream->mMetadataBlocks;
case PLAYED_BLOCK:
NS_ASSERTION(aBlock->mStream, "Metadata block has no stream?");
return &aBlock->mStream->mPlayedBlocks;
case READAHEAD_BLOCK:
@@ -986,33 +1003,33 @@ MediaCache::GetListForBlock(BlockOwner*
return &aBlock->mStream->mReadaheadBlocks;
default:
NS_ERROR("Invalid block class");
return nullptr;
}
}
MediaCache::BlockOwner*
-MediaCache::GetBlockOwner(int32_t aBlockIndex, MediaCacheStream* aStream)
+MediaCache::GetBlockOwner(AutoLock&,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block = &mIndex[aBlockIndex];
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
if (block->mOwners[i].mStream == aStream)
return &block->mOwners[i];
}
return nullptr;
}
void
-MediaCache::SwapBlocks(int32_t aBlockIndex1, int32_t aBlockIndex2)
+MediaCache::SwapBlocks(AutoLock& aLock,
+ int32_t aBlockIndex1,
+ int32_t aBlockIndex2)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block1 = &mIndex[aBlockIndex1];
Block* block2 = &mIndex[aBlockIndex2];
block1->mOwners.SwapElements(block2->mOwners);
// Now all references to block1 have to be replaced with block2 and
// vice versa.
// First update stream references to blocks via mBlocks.
@@ -1039,88 +1056,84 @@ MediaCache::SwapBlocks(int32_t aBlockInd
continue;
visitedStreams.PutEntry(stream);
stream->mReadaheadBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
stream->mPlayedBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
stream->mMetadataBlocks.NotifyBlockSwapped(aBlockIndex1, aBlockIndex2);
}
}
- Verify();
+ Verify(aLock);
}
void
-MediaCache::RemoveBlockOwner(int32_t aBlockIndex, MediaCacheStream* aStream)
+MediaCache::RemoveBlockOwner(AutoLock& aLock,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block = &mIndex[aBlockIndex];
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
if (bo->mStream == aStream) {
- GetListForBlock(bo)->RemoveBlock(aBlockIndex);
+ GetListForBlock(aLock, bo)->RemoveBlock(aBlockIndex);
bo->mStream->mBlocks[bo->mStreamBlock] = -1;
block->mOwners.RemoveElementAt(i);
if (block->mOwners.IsEmpty()) {
mFreeBlocks.AddFirstBlock(aBlockIndex);
}
return;
}
}
}
void
-MediaCache::AddBlockOwnerAsReadahead(int32_t aBlockIndex,
- MediaCacheStream* aStream,
- int32_t aStreamBlockIndex)
+MediaCache::AddBlockOwnerAsReadahead(AutoLock& aLock,
+ int32_t aBlockIndex,
+ MediaCacheStream* aStream,
+ int32_t aStreamBlockIndex)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block = &mIndex[aBlockIndex];
if (block->mOwners.IsEmpty()) {
mFreeBlocks.RemoveBlock(aBlockIndex);
}
BlockOwner* bo = block->mOwners.AppendElement();
mBlockOwnersWatermark =
std::max(mBlockOwnersWatermark, uint32_t(block->mOwners.Length()));
bo->mStream = aStream;
bo->mStreamBlock = aStreamBlockIndex;
aStream->mBlocks[aStreamBlockIndex] = aBlockIndex;
bo->mClass = READAHEAD_BLOCK;
- InsertReadaheadBlock(bo, aBlockIndex);
+ InsertReadaheadBlock(aLock, bo, aBlockIndex);
}
void
-MediaCache::FreeBlock(int32_t aBlock)
+MediaCache::FreeBlock(AutoLock& aLock, int32_t aBlock)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
Block* block = &mIndex[aBlock];
if (block->mOwners.IsEmpty()) {
// already free
return;
}
LOG("Released block %d", aBlock);
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
- GetListForBlock(bo)->RemoveBlock(aBlock);
+ GetListForBlock(aLock, bo)->RemoveBlock(aBlock);
bo->mStream->mBlocks[bo->mStreamBlock] = -1;
}
block->mOwners.Clear();
mFreeBlocks.AddFirstBlock(aBlock);
- Verify();
+ Verify(aLock);
}
TimeDuration
-MediaCache::PredictNextUse(TimeStamp aNow, int32_t aBlock)
+MediaCache::PredictNextUse(AutoLock&, TimeStamp aNow, int32_t aBlock)
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- mReentrantMonitor.AssertCurrentThreadIn();
NS_ASSERTION(!IsBlockFree(aBlock), "aBlock is free");
Block* block = &mIndex[aBlock];
// Blocks can be belong to multiple streams. The predicted next use
// time is the earliest time predicted by any of the streams.
TimeDuration result;
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
BlockOwner* bo = &block->mOwners[i];
@@ -1163,20 +1176,19 @@ MediaCache::PredictNextUse(TimeStamp aNo
if (i == 0 || prediction < result) {
result = prediction;
}
}
return result;
}
TimeDuration
-MediaCache::PredictNextUseForIncomingData(MediaCacheStream* aStream)
+MediaCache::PredictNextUseForIncomingData(AutoLock&, MediaCacheStream* aStream)
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- mReentrantMonitor.AssertCurrentThreadIn();
int64_t bytesAhead = aStream->mChannelOffset - aStream->mStreamOffset;
if (bytesAhead <= -BLOCK_SIZE) {
// Hmm, no idea when data behind us will be used. Guess 24 hours.
return TimeDuration::FromSeconds(24*60*60);
}
if (bytesAhead <= 0)
return TimeDuration(0);
@@ -1185,17 +1197,17 @@ MediaCache::PredictNextUseForIncomingDat
std::min<int64_t>(millisecondsAhead, INT32_MAX));
}
void
MediaCache::Update()
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ AutoLock lock(mMonitor);
struct StreamAction
{
enum
{
NONE,
SEEK,
RESUME,
@@ -1239,17 +1251,17 @@ MediaCache::Update()
// freeBlockCount.
for (int32_t blockIndex = mIndex.Length() - 1; blockIndex >= maxBlocks;
--blockIndex) {
if (IsBlockFree(blockIndex)) {
// Don't count overflowing free blocks in our free block count
--freeBlockCount;
continue;
}
- TimeDuration predictedUse = PredictNextUse(now, blockIndex);
+ TimeDuration predictedUse = PredictNextUse(lock, now, blockIndex);
latestPredictedUseForOverflow = std::max(latestPredictedUseForOverflow, predictedUse);
}
} else {
freeBlockCount += maxBlocks - mIndex.Length();
}
// Now try to move overflowing blocks to the main part of the cache.
for (int32_t blockIndex = mIndex.Length() - 1; blockIndex >= maxBlocks;
@@ -1257,64 +1269,69 @@ MediaCache::Update()
if (IsBlockFree(blockIndex))
continue;
Block* block = &mIndex[blockIndex];
// Try to relocate the block close to other blocks for the first stream.
// There is no point in trying to make it close to other blocks in
// *all* the streams it might belong to.
int32_t destinationBlockIndex =
- FindReusableBlock(now, block->mOwners[0].mStream,
- block->mOwners[0].mStreamBlock, maxBlocks);
+ FindReusableBlock(lock,
+ now,
+ block->mOwners[0].mStream,
+ block->mOwners[0].mStreamBlock,
+ maxBlocks);
if (destinationBlockIndex < 0) {
// Nowhere to place this overflow block. We won't be able to
// place any more overflow blocks.
break;
}
// Don't evict |destinationBlockIndex| if it is within [cur, end) otherwise
// a new channel will be opened to download this block again which is bad.
bool inCurrentCachedRange = false;
for (BlockOwner& owner : mIndex[destinationBlockIndex].mOwners) {
MediaCacheStream* stream = owner.mStream;
int64_t end = OffsetToBlockIndexUnchecked(
- stream->GetCachedDataEndInternal(stream->mStreamOffset));
+ stream->GetCachedDataEndInternal(lock, stream->mStreamOffset));
int64_t cur = OffsetToBlockIndexUnchecked(stream->mStreamOffset);
if (cur <= owner.mStreamBlock && owner.mStreamBlock < end) {
inCurrentCachedRange = true;
break;
}
}
if (inCurrentCachedRange) {
continue;
}
if (IsBlockFree(destinationBlockIndex) ||
- PredictNextUse(now, destinationBlockIndex) > latestPredictedUseForOverflow) {
+ PredictNextUse(lock, now, destinationBlockIndex) >
+ latestPredictedUseForOverflow) {
// Reuse blocks in the main part of the cache that are less useful than
// the least useful overflow blocks
nsresult rv = mBlockCache->MoveBlock(blockIndex, destinationBlockIndex);
if (NS_SUCCEEDED(rv)) {
// We successfully copied the file data.
LOG("Swapping blocks %d and %d (trimming cache)",
blockIndex, destinationBlockIndex);
// Swapping the block metadata here lets us maintain the
// correct positions in the linked lists
- SwapBlocks(blockIndex, destinationBlockIndex);
+ SwapBlocks(lock, blockIndex, destinationBlockIndex);
//Free the overflowing block even if the copy failed.
LOG("Released block %d (trimming cache)", blockIndex);
- FreeBlock(blockIndex);
+ FreeBlock(lock, blockIndex);
}
} else {
LOG("Could not trim cache block %d (destination %d, "
"predicted next use %f, latest predicted use for overflow %f",
- blockIndex, destinationBlockIndex,
- PredictNextUse(now, destinationBlockIndex).ToSeconds(),
+ blockIndex,
+ destinationBlockIndex,
+ PredictNextUse(lock, now, destinationBlockIndex).ToSeconds(),
latestPredictedUseForOverflow.ToSeconds());
}
}
// Try chopping back the array of cache entries and the cache file.
Truncate();
// Count the blocks allocated for readahead of non-seekable streams
// (these blocks can't be freed but we don't want them to monopolize the
@@ -1326,19 +1343,19 @@ MediaCache::Update()
nonSeekableReadaheadBlockCount += stream->mReadaheadBlocks.GetCount();
}
}
// If freeBlockCount is zero, then compute the latest of
// the predicted next-uses for all blocks
TimeDuration latestNextUse;
if (freeBlockCount == 0) {
- int32_t reusableBlock = FindReusableBlock(now, nullptr, 0, maxBlocks);
+ int32_t reusableBlock = FindReusableBlock(lock, now, nullptr, 0, maxBlocks);
if (reusableBlock >= 0) {
- latestNextUse = PredictNextUse(now, reusableBlock);
+ latestNextUse = PredictNextUse(lock, now, reusableBlock);
}
}
int32_t resumeThreshold = MediaPrefs::MediaCacheResumeThreshold();
int32_t readaheadLimit = MediaPrefs::MediaCacheReadaheadLimit();
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
actions.AppendElement(StreamAction{});
@@ -1352,17 +1369,18 @@ MediaCache::Update()
// We make decisions based on mSeekTarget when there is a pending seek.
// Otherwise we will keep issuing seek requests until mChannelOffset
// is changed by NotifyDataStarted() which is bad.
int64_t channelOffset =
stream->mSeekTarget != -1 ? stream->mSeekTarget : stream->mChannelOffset;
// Figure out where we should be reading from. It's the first
// uncached byte after the current mStreamOffset.
- int64_t dataOffset = stream->GetCachedDataEndInternal(stream->mStreamOffset);
+ int64_t dataOffset =
+ stream->GetCachedDataEndInternal(lock, stream->mStreamOffset);
MOZ_ASSERT(dataOffset >= 0);
// Compute where we'd actually seek to to read at readOffset
int64_t desiredOffset = dataOffset;
if (stream->mIsTransportSeekable) {
if (desiredOffset > channelOffset &&
desiredOffset <= channelOffset + SEEK_VS_READ_THRESHOLD) {
// Assume it's more efficient to just keep reading up to the
@@ -1376,17 +1394,17 @@ MediaCache::Update()
// Seek back to 0 (the client will reopen the stream) and then
// read forward.
NS_WARNING("Can't seek backwards, so seeking to 0");
desiredOffset = 0;
// Flush cached blocks out, since if this is a live stream
// the cached data may be completely different next time we
// read it. We have to assume that live streams don't
// advertise themselves as being seekable...
- ReleaseStreamBlocks(stream);
+ ReleaseStreamBlocks(lock, stream);
} else {
// otherwise reading forward is looking good, so just stay where we
// are and don't trigger a channel seek!
desiredOffset = channelOffset;
}
}
// Figure out if we should be reading data now or not. It's amazing
@@ -1422,17 +1440,18 @@ MediaCache::Update()
LOG("Stream %p throttling non-seekable readahead", stream);
enableReading = false;
} else if (mIndex.Length() > uint32_t(maxBlocks)) {
// We're in the process of bringing the cache size back to the
// desired limit, so don't bring in more data yet
LOG("Stream %p throttling to reduce cache size", stream);
enableReading = false;
} else {
- TimeDuration predictedNewDataUse = PredictNextUseForIncomingData(stream);
+ TimeDuration predictedNewDataUse =
+ PredictNextUseForIncomingData(lock, stream);
if (stream->mThrottleReadahead &&
stream->mCacheSuspended &&
predictedNewDataUse.ToSeconds() > resumeThreshold) {
// Don't need data for a while, so don't bother waking up the stream
LOG("Stream %p avoiding wakeup since more data is not needed", stream);
enableReading = false;
} else if (stream->mThrottleReadahead &&
@@ -1530,34 +1549,34 @@ MediaCache::Update()
actions[i].mSeekTarget,
actions[i].mResume);
stream->mClient->CacheClientSeek(actions[i].mSeekTarget,
actions[i].mResume);
break;
case StreamAction::RESUME:
LOG("Stream %p Resumed", stream);
stream->mClient->CacheClientResume();
- QueueSuspendedStatusUpdate(stream->mResourceID);
+ QueueSuspendedStatusUpdate(lock, stream->mResourceID);
break;
case StreamAction::SUSPEND:
LOG("Stream %p Suspended", stream);
stream->mClient->CacheClientSuspend();
- QueueSuspendedStatusUpdate(stream->mResourceID);
+ QueueSuspendedStatusUpdate(lock, stream->mResourceID);
break;
default:
break;
}
}
// Notify streams about the suspended status changes.
for (uint32_t i = 0; i < mSuspendedStatusToNotify.Length(); ++i) {
MediaCache::ResourceStreamIterator iter(this, mSuspendedStatusToNotify[i]);
while (MediaCacheStream* stream = iter.Next()) {
stream->mClient->CacheClientNotifySuspendedStatusChanged(
- stream->AreAllStreamsForResourceSuspended());
+ stream->AreAllStreamsForResourceSuspended(lock));
}
}
mSuspendedStatusToNotify.Clear();
}
class UpdateEvent : public Runnable
{
public:
@@ -1577,49 +1596,44 @@ public:
return NS_OK;
}
private:
RefPtr<MediaCache> mMediaCache;
};
void
-MediaCache::QueueUpdate()
+MediaCache::QueueUpdate(AutoLock&)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
// Queuing an update while we're in an update raises a high risk of
// triggering endless events
NS_ASSERTION(!mInUpdate,
"Queuing an update while we're in an update");
if (mUpdateQueued)
return;
mUpdateQueued = true;
// XXX MediaCache does updates when decoders are still running at
// shutdown and get freed in the final cycle-collector cleanup. So
// don't leak a runnable in that case.
nsCOMPtr<nsIRunnable> event = new UpdateEvent(this);
sThread->Dispatch(event.forget());
}
void
-MediaCache::QueueSuspendedStatusUpdate(int64_t aResourceID)
+MediaCache::QueueSuspendedStatusUpdate(AutoLock&, int64_t aResourceID)
{
- mReentrantMonitor.AssertCurrentThreadIn();
if (!mSuspendedStatusToNotify.Contains(aResourceID)) {
mSuspendedStatusToNotify.AppendElement(aResourceID);
}
}
#ifdef DEBUG_VERIFY_CACHE
void
-MediaCache::Verify()
+MediaCache::Verify(AutoLock&)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
mFreeBlocks.Verify();
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
MediaCacheStream* stream = mStreams[i];
stream->mReadaheadBlocks.Verify();
stream->mPlayedBlocks.Verify();
stream->mMetadataBlocks.Verify();
// Verify that the readahead blocks are listed in stream block order
@@ -1637,75 +1651,75 @@ MediaCache::Verify()
lastStreamBlock = nextStreamBlock;
block = stream->mReadaheadBlocks.GetNextBlock(block);
}
}
}
#endif
void
-MediaCache::InsertReadaheadBlock(BlockOwner* aBlockOwner, int32_t aBlockIndex)
+MediaCache::InsertReadaheadBlock(AutoLock& aLock,
+ BlockOwner* aBlockOwner,
+ int32_t aBlockIndex)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
// Find the last block whose stream block is before aBlockIndex's
// stream block, and insert after it
MediaCacheStream* stream = aBlockOwner->mStream;
int32_t readaheadIndex = stream->mReadaheadBlocks.GetLastBlock();
while (readaheadIndex >= 0) {
- BlockOwner* bo = GetBlockOwner(readaheadIndex, stream);
+ BlockOwner* bo = GetBlockOwner(aLock, readaheadIndex, stream);
NS_ASSERTION(bo, "stream must own its blocks");
if (bo->mStreamBlock < aBlockOwner->mStreamBlock) {
stream->mReadaheadBlocks.AddAfter(aBlockIndex, readaheadIndex);
return;
}
NS_ASSERTION(bo->mStreamBlock > aBlockOwner->mStreamBlock,
"Duplicated blocks??");
readaheadIndex = stream->mReadaheadBlocks.GetPrevBlock(readaheadIndex);
}
stream->mReadaheadBlocks.AddFirstBlock(aBlockIndex);
- Verify();
+ Verify(aLock);
}
void
-MediaCache::AllocateAndWriteBlock(MediaCacheStream* aStream,
+MediaCache::AllocateAndWriteBlock(AutoLock& aLock,
+ MediaCacheStream* aStream,
int32_t aStreamBlockIndex,
MediaCacheStream::ReadMode aMode,
Span<const uint8_t> aData1,
Span<const uint8_t> aData2)
{
MOZ_ASSERT(sThread->IsOnCurrentThread());
- mReentrantMonitor.AssertCurrentThreadIn();
// Remove all cached copies of this block
ResourceStreamIterator iter(this, aStream->mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
while (aStreamBlockIndex >= int32_t(stream->mBlocks.Length())) {
stream->mBlocks.AppendElement(-1);
}
if (stream->mBlocks[aStreamBlockIndex] >= 0) {
// We no longer want to own this block
int32_t globalBlockIndex = stream->mBlocks[aStreamBlockIndex];
LOG("Released block %d from stream %p block %d(%" PRId64 ")",
globalBlockIndex,
stream,
aStreamBlockIndex,
aStreamBlockIndex * BLOCK_SIZE);
- RemoveBlockOwner(globalBlockIndex, stream);
+ RemoveBlockOwner(aLock, globalBlockIndex, stream);
}
}
// Extend the mBlocks array as necessary
TimeStamp now = TimeStamp::Now();
int32_t blockIndex =
- FindBlockForIncomingData(now, aStream, aStreamBlockIndex);
+ FindBlockForIncomingData(aLock, now, aStream, aStreamBlockIndex);
if (blockIndex >= 0) {
- FreeBlock(blockIndex);
+ FreeBlock(aLock, blockIndex);
Block* block = &mIndex[blockIndex];
LOG("Allocated block %d to stream %p block %d(%" PRId64 ")",
blockIndex,
aStream,
aStreamBlockIndex,
aStreamBlockIndex * BLOCK_SIZE);
@@ -1734,100 +1748,99 @@ MediaCache::AllocateAndWriteBlock(MediaC
bo.mLastUseTime = now;
bo.mStream->mBlocks[aStreamBlockIndex] = blockIndex;
if (aStreamBlockIndex * BLOCK_SIZE < bo.mStream->mStreamOffset) {
bo.mClass = aMode == MediaCacheStream::MODE_PLAYBACK ? PLAYED_BLOCK
: METADATA_BLOCK;
// This must be the most-recently-used block, since we
// marked it as used now (which may be slightly bogus, but we'll
// treat it as used for simplicity).
- GetListForBlock(&bo)->AddFirstBlock(blockIndex);
- Verify();
+ GetListForBlock(aLock, &bo)->AddFirstBlock(blockIndex);
+ Verify(aLock);
} else {
// This may not be the latest readahead block, although it usually
// will be. We may have to scan for the right place to insert
// the block in the list.
bo.mClass = READAHEAD_BLOCK;
- InsertReadaheadBlock(&bo, blockIndex);
+ InsertReadaheadBlock(aLock, &bo, blockIndex);
}
}
// Invariant: block->mOwners.IsEmpty() iff we can find an entry
// in mFreeBlocks for a given blockIndex.
MOZ_DIAGNOSTIC_ASSERT(!block->mOwners.IsEmpty());
mFreeBlocks.RemoveBlock(blockIndex);
nsresult rv = mBlockCache->WriteBlock(blockIndex, aData1, aData2);
if (NS_FAILED(rv)) {
LOG("Released block %d from stream %p block %d(%" PRId64 ")",
blockIndex,
aStream,
aStreamBlockIndex,
aStreamBlockIndex * BLOCK_SIZE);
- FreeBlock(blockIndex);
+ FreeBlock(aLock, blockIndex);
}
}
// Queue an Update since the cache state has changed (for example
// we might want to stop loading because the cache is full)
- QueueUpdate();
+ QueueUpdate(aLock);
}
void
-MediaCache::OpenStream(MediaCacheStream* aStream, bool aIsClone)
+MediaCache::OpenStream(AutoLock& aLock,
+ MediaCacheStream* aStream,
+ bool aIsClone)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- ReentrantMonitorAutoEnter mon(mReentrantMonitor);
LOG("Stream %p opened", aStream);
mStreams.AppendElement(aStream);
// A cloned stream should've got the ID from its original.
if (!aIsClone) {
MOZ_ASSERT(aStream->mResourceID == 0, "mResourceID has been initialized.");
- aStream->mResourceID = AllocateResourceID();
+ aStream->mResourceID = AllocateResourceID(aLock);
}
// We should have a valid ID now no matter it is cloned or not.
MOZ_ASSERT(aStream->mResourceID > 0, "mResourceID is invalid");
// Queue an update since a new stream has been opened.
- QueueUpdate();
+ QueueUpdate(aLock);
}
void
MediaCache::ReleaseStream(MediaCacheStream* aStream)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ AutoLock lock(mMonitor);
LOG("Stream %p closed", aStream);
mStreams.RemoveElement(aStream);
// Update MediaCache again for |mStreams| is changed.
// We need to re-run Update() to ensure streams reading from the same resource
// as the removed stream get a chance to continue reading.
- QueueUpdate();
+ QueueUpdate(lock);
}
void
-MediaCache::ReleaseStreamBlocks(MediaCacheStream* aStream)
+MediaCache::ReleaseStreamBlocks(AutoLock& aLock, MediaCacheStream* aStream)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
// XXX scanning the entire stream doesn't seem great, if not much of it
// is cached, but the only easy alternative is to scan the entire cache
// which isn't better
uint32_t length = aStream->mBlocks.Length();
for (uint32_t i = 0; i < length; ++i) {
int32_t blockIndex = aStream->mBlocks[i];
if (blockIndex >= 0) {
LOG("Released block %d from stream %p block %d(%" PRId64 ")",
blockIndex, aStream, i, i*BLOCK_SIZE);
- RemoveBlockOwner(blockIndex, aStream);
+ RemoveBlockOwner(aLock, blockIndex, aStream);
}
}
}
void
MediaCache::Truncate()
{
uint32_t end;
@@ -1842,55 +1855,56 @@ MediaCache::Truncate()
// XXX We could truncate the cache file here, but we don't seem
// to have a cross-platform API for doing that. At least when all
// streams are closed we shut down the cache, which erases the
// file at that point.
}
}
void
-MediaCache::NoteBlockUsage(MediaCacheStream* aStream, int32_t aBlockIndex,
+MediaCache::NoteBlockUsage(AutoLock& aLock,
+ MediaCacheStream* aStream,
+ int32_t aBlockIndex,
int64_t aStreamOffset,
- MediaCacheStream::ReadMode aMode, TimeStamp aNow)
+ MediaCacheStream::ReadMode aMode,
+ TimeStamp aNow)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
if (aBlockIndex < 0) {
// this block is not in the cache yet
return;
}
- BlockOwner* bo = GetBlockOwner(aBlockIndex, aStream);
+ BlockOwner* bo = GetBlockOwner(aLock, aBlockIndex, aStream);
if (!bo) {
// this block is not in the cache yet
return;
}
// The following check has to be <= because the stream offset has
// not yet been updated for the data read from this block
NS_ASSERTION(bo->mStreamBlock*BLOCK_SIZE <= aStreamOffset,
"Using a block that's behind the read position?");
- GetListForBlock(bo)->RemoveBlock(aBlockIndex);
+ GetListForBlock(aLock, bo)->RemoveBlock(aBlockIndex);
bo->mClass =
(aMode == MediaCacheStream::MODE_METADATA || bo->mClass == METADATA_BLOCK)
? METADATA_BLOCK
: PLAYED_BLOCK;
// Since this is just being used now, it can definitely be at the front
// of mMetadataBlocks or mPlayedBlocks
- GetListForBlock(bo)->AddFirstBlock(aBlockIndex);
+ GetListForBlock(aLock, bo)->AddFirstBlock(aBlockIndex);
bo->mLastUseTime = aNow;
- Verify();
+ Verify(aLock);
}
void
-MediaCache::NoteSeek(MediaCacheStream* aStream, int64_t aOldOffset)
+MediaCache::NoteSeek(AutoLock& aLock,
+ MediaCacheStream* aStream,
+ int64_t aOldOffset)
{
- mReentrantMonitor.AssertCurrentThreadIn();
-
if (aOldOffset < aStream->mStreamOffset) {
// We seeked forward. Convert blocks from readahead to played.
// Any readahead block that intersects the seeked-over range must
// be converted.
int32_t blockIndex = OffsetToBlockIndex(aOldOffset);
if (blockIndex < 0) {
return;
}
@@ -1901,18 +1915,22 @@ MediaCache::NoteSeek(MediaCacheStream* a
return;
}
TimeStamp now = TimeStamp::Now();
while (blockIndex < endIndex) {
int32_t cacheBlockIndex = aStream->mBlocks[blockIndex];
if (cacheBlockIndex >= 0) {
// Marking the block used may not be exactly what we want but
// it's simple
- NoteBlockUsage(aStream, cacheBlockIndex, aStream->mStreamOffset,
- MediaCacheStream::MODE_PLAYBACK, now);
+ NoteBlockUsage(aLock,
+ aStream,
+ cacheBlockIndex,
+ aStream->mStreamOffset,
+ MediaCacheStream::MODE_PLAYBACK,
+ now);
}
++blockIndex;
}
} else {
// We seeked backward. Convert from played to readahead.
// Any played block that is entirely after the start of the seeked-over
// range must be converted.
int32_t blockIndex =
@@ -1925,43 +1943,43 @@ MediaCache::NoteSeek(MediaCacheStream* a
int32_t(aStream->mBlocks.Length()));
if (endIndex < 0) {
return;
}
while (blockIndex < endIndex) {
MOZ_ASSERT(endIndex > 0);
int32_t cacheBlockIndex = aStream->mBlocks[endIndex - 1];
if (cacheBlockIndex >= 0) {
- BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
+ BlockOwner* bo = GetBlockOwner(aLock, cacheBlockIndex, aStream);
NS_ASSERTION(bo, "Stream doesn't own its blocks?");
if (bo->mClass == PLAYED_BLOCK) {
aStream->mPlayedBlocks.RemoveBlock(cacheBlockIndex);
bo->mClass = READAHEAD_BLOCK;
// Adding this as the first block is sure to be OK since
// this must currently be the earliest readahead block
// (that's why we're proceeding backwards from the end of
// the seeked range to the start)
aStream->mReadaheadBlocks.AddFirstBlock(cacheBlockIndex);
- Verify();
+ Verify(aLock);
}
}
--endIndex;
}
}
}
void
MediaCacheStream::NotifyLoadID(uint32_t aLoadID)
{
MOZ_ASSERT(aLoadID > 0);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"MediaCacheStream::NotifyLoadID",
[ client = RefPtr<ChannelMediaResource>(mClient), this, aLoadID ]() {
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
mLoadID = aLoadID;
});
OwnerThread()->Dispatch(r.forget());
}
void
MediaCacheStream::NotifyDataStartedInternal(uint32_t aLoadID,
int64_t aOffset,
@@ -1971,17 +1989,17 @@ MediaCacheStream::NotifyDataStartedInter
MOZ_ASSERT(OwnerThread()->IsOnCurrentThread());
MOZ_ASSERT(aLoadID > 0);
LOG("Stream %p DataStarted: %" PRId64 " aLoadID=%u aLength=%" PRId64,
this,
aOffset,
aLoadID,
aLength);
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
NS_WARNING_ASSERTION(aOffset == mSeekTarget || aOffset == mChannelOffset,
"Server is giving us unexpected offset");
MOZ_ASSERT(aOffset >= 0);
if (aLength >= 0) {
mStreamLength = aLength;
}
mChannelOffset = aOffset;
if (mStreamLength >= 0) {
@@ -1991,17 +2009,17 @@ MediaCacheStream::NotifyDataStartedInter
}
mLoadID = aLoadID;
MOZ_ASSERT(aOffset == 0 || aSeekable,
"channel offset must be zero when we become non-seekable");
mIsTransportSeekable = aSeekable;
// Queue an Update since we may change our strategy for dealing
// with this stream
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
// Reset mSeekTarget since the seek is completed so MediaCache::Update() will
// make decisions based on mChannelOffset instead of mSeekTarget.
mSeekTarget = -1;
// Reset these flags since a new load has begun.
mChannelEnded = false;
mDidNotifyDataEnded = false;
@@ -2040,17 +2058,17 @@ MediaCacheStream::NotifyDataStarted(uint
void
MediaCacheStream::NotifyDataReceived(uint32_t aLoadID,
uint32_t aCount,
const uint8_t* aData)
{
MOZ_ASSERT(OwnerThread()->IsOnCurrentThread());
MOZ_ASSERT(aLoadID > 0);
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (mClosed) {
// Nothing to do if the stream is closed.
return;
}
LOG("Stream %p DataReceived at %" PRId64 " count=%u aLoadID=%u",
this,
mChannelOffset,
@@ -2079,16 +2097,17 @@ MediaCacheStream::NotifyDataReceived(uin
}
// The number of bytes needed to complete the partial block.
size_t remaining = BLOCK_SIZE - partial.Length();
if (source.Length() >= remaining) {
// We have a whole block now to write it out.
mMediaCache->AllocateAndWriteBlock(
+ lock,
this,
OffsetToBlockIndexUnchecked(mChannelOffset),
mMetadataInPartialBlockBuffer ? MODE_METADATA : MODE_PLAYBACK,
partial,
source.First(remaining));
source = source.From(remaining);
mChannelOffset += remaining;
} else {
@@ -2108,60 +2127,60 @@ MediaCacheStream::NotifyDataReceived(uin
stream->mStreamLength = std::max(stream->mStreamLength, mChannelOffset);
}
stream->mClient->CacheClientNotifyDataReceived();
}
// Notify in case there's a waiting reader
// XXX it would be fairly easy to optimize things a lot more to
// avoid waking up reader threads unnecessarily
- mon.NotifyAll();
+ lock.NotifyAll();
}
void
-MediaCacheStream::FlushPartialBlockInternal(bool aNotifyAll,
- ReentrantMonitorAutoEnter& aReentrantMonitor)
+MediaCacheStream::FlushPartialBlockInternal(AutoLock& aLock, bool aNotifyAll)
{
MOZ_ASSERT(OwnerThread()->IsOnCurrentThread());
int32_t blockIndex = OffsetToBlockIndexUnchecked(mChannelOffset);
int32_t blockOffset = OffsetInBlock(mChannelOffset);
if (blockOffset > 0) {
LOG("Stream %p writing partial block: [%d] bytes; "
"mStreamOffset [%" PRId64 "] mChannelOffset[%"
PRId64 "] mStreamLength [%" PRId64 "] notifying: [%s]",
this, blockOffset, mStreamOffset, mChannelOffset, mStreamLength,
aNotifyAll ? "yes" : "no");
// Write back the partial block
memset(mPartialBlockBuffer.get() + blockOffset, 0, BLOCK_SIZE - blockOffset);
auto data = MakeSpan<const uint8_t>(mPartialBlockBuffer.get(), BLOCK_SIZE);
mMediaCache->AllocateAndWriteBlock(
+ aLock,
this,
blockIndex,
mMetadataInPartialBlockBuffer ? MODE_METADATA : MODE_PLAYBACK,
data);
}
// |mChannelOffset == 0| means download ends with no bytes received.
// We should also wake up those readers who are waiting for data
// that will never come.
if ((blockOffset > 0 || mChannelOffset == 0) && aNotifyAll) {
// Wake up readers who may be waiting for this data
- aReentrantMonitor.NotifyAll();
+ aLock.NotifyAll();
}
}
void
MediaCacheStream::NotifyDataEndedInternal(uint32_t aLoadID,
nsresult aStatus,
bool aReopenOnError)
{
MOZ_ASSERT(OwnerThread()->IsOnCurrentThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (mClosed || aLoadID != mLoadID) {
// Nothing to do if the stream is closed or a new load has begun.
return;
}
// Note that aStatus might have succeeded --- this might be a normal close
// --- even in situations where the server cut us off because we were
@@ -2200,31 +2219,31 @@ MediaCacheStream::NotifyDataEndedInterna
// and close the stream. This is OK for it is not an error we can recover
// from and we have a consistent behavior with that where CacheClientSeek()
// is called from MediaCache::Update().
}
// It is prudent to update channel/cache status before calling
// CacheClientNotifyDataEnded() which will read |mChannelEnded|.
mChannelEnded = true;
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
if (NS_FAILED(aStatus)) {
// Notify the client about this network error.
mDidNotifyDataEnded = true;
mNotifyDataEndedStatus = aStatus;
mClient->CacheClientNotifyDataEnded(aStatus);
// Wake up the readers so they can fail gracefully.
- mon.NotifyAll();
+ lock.NotifyAll();
return;
}
// Note we don't flush the partial block when download ends abnormally for
// the padding zeros will give wrong data to other streams.
- FlushPartialBlockInternal(true, mon);
+ FlushPartialBlockInternal(lock, true);
MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
// We read the whole stream, so remember the true length
stream->mStreamLength = mChannelOffset;
if (!stream->mDidNotifyDataEnded) {
stream->mDidNotifyDataEnded = true;
stream->mNotifyDataEndedStatus = aStatus;
@@ -2253,21 +2272,21 @@ MediaCacheStream::NotifyDataEnded(uint32
void
MediaCacheStream::NotifyClientSuspended(bool aSuspended)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<ChannelMediaResource> client = mClient;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"MediaCacheStream::NotifyClientSuspended", [client, this, aSuspended]() {
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (!mClosed && mClientSuspended != aSuspended) {
mClientSuspended = aSuspended;
// mClientSuspended changes the decision of reading streams.
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
});
OwnerThread()->Dispatch(r.forget());
}
MediaCacheStream::~MediaCacheStream()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
@@ -2284,30 +2303,29 @@ MediaCacheStream::~MediaCacheStream()
"MEDIACACHESTREAM_LENGTH_KB=%" PRIu32,
this,
lengthKb);
Telemetry::Accumulate(Telemetry::HistogramID::MEDIACACHESTREAM_LENGTH_KB,
lengthKb);
}
bool
-MediaCacheStream::AreAllStreamsForResourceSuspended()
+MediaCacheStream::AreAllStreamsForResourceSuspended(AutoLock& aLock)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
// Look for a stream that's able to read the data we need
int64_t dataOffset = -1;
while (MediaCacheStream* stream = iter.Next()) {
if (stream->mCacheSuspended || stream->mChannelEnded || stream->mClosed) {
continue;
}
if (dataOffset < 0) {
- dataOffset = GetCachedDataEndInternal(mStreamOffset);
+ dataOffset = GetCachedDataEndInternal(aLock, mStreamOffset);
}
// Ignore streams that are reading beyond the data we need
if (stream->mChannelOffset > dataOffset) {
continue;
}
return false;
}
@@ -2318,103 +2336,102 @@ void
MediaCacheStream::Close()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
if (!mMediaCache || mClosed) {
return;
}
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
// Closing a stream will change the return value of
// MediaCacheStream::AreAllStreamsForResourceSuspended as well as
// ChannelMediaResource::IsSuspendedByCache. Let's notify it.
- mMediaCache->QueueSuspendedStatusUpdate(mResourceID);
+ mMediaCache->QueueSuspendedStatusUpdate(lock, mResourceID);
mClosed = true;
- mMediaCache->ReleaseStreamBlocks(this);
+ mMediaCache->ReleaseStreamBlocks(lock, this);
// Wake up any blocked readers
- mon.NotifyAll();
+ lock.NotifyAll();
// Queue an Update since we may have created more free space. Don't do
// it from CloseInternal since that gets called by Update() itself
// sometimes, and we try to not to queue updates from Update().
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
void
MediaCacheStream::Pin()
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
++mPinCount;
// Queue an Update since we may no longer want to read more into the
// cache, if this stream's block have become non-evictable
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
void
MediaCacheStream::Unpin()
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
NS_ASSERTION(mPinCount > 0, "Unbalanced Unpin");
--mPinCount;
// Queue an Update since we may be able to read more into the
// cache, if this stream's block have become evictable
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
int64_t
MediaCacheStream::GetLength()
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
return mStreamLength;
}
int64_t
MediaCacheStream::GetOffset() const
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
return mChannelOffset;
}
int64_t
MediaCacheStream::GetNextCachedData(int64_t aOffset)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
- return GetNextCachedDataInternal(aOffset);
+ AutoLock lock(mMediaCache->Monitor());
+ return GetNextCachedDataInternal(lock, aOffset);
}
int64_t
MediaCacheStream::GetCachedDataEnd(int64_t aOffset)
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
- return GetCachedDataEndInternal(aOffset);
+ AutoLock lock(mMediaCache->Monitor());
+ return GetCachedDataEndInternal(lock, aOffset);
}
bool
MediaCacheStream::IsDataCachedToEndOfStream(int64_t aOffset)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (mStreamLength < 0)
return false;
- return GetCachedDataEndInternal(aOffset) >= mStreamLength;
+ return GetCachedDataEndInternal(lock, aOffset) >= mStreamLength;
}
int64_t
-MediaCacheStream::GetCachedDataEndInternal(int64_t aOffset)
+MediaCacheStream::GetCachedDataEndInternal(AutoLock&, int64_t aOffset)
{
- mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
int32_t blockIndex = OffsetToBlockIndex(aOffset);
if (blockIndex < 0) {
return aOffset;
}
while (size_t(blockIndex) < mBlocks.Length() && mBlocks[blockIndex] != -1) {
++blockIndex;
}
int64_t result = blockIndex*BLOCK_SIZE;
@@ -2427,19 +2444,18 @@ MediaCacheStream::GetCachedDataEndIntern
// The last block in the cache may only be partially valid, so limit
// the cached range to the stream length
result = std::min(result, mStreamLength);
}
return std::max(result, aOffset);
}
int64_t
-MediaCacheStream::GetNextCachedDataInternal(int64_t aOffset)
+MediaCacheStream::GetNextCachedDataInternal(AutoLock&, int64_t aOffset)
{
- mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
if (aOffset == mStreamLength)
return -1;
int32_t startBlockIndex = OffsetToBlockIndex(aOffset);
if (startBlockIndex < 0) {
return -1;
}
int32_t channelBlockIndex = OffsetToBlockIndexUnchecked(mChannelOffset);
@@ -2480,83 +2496,83 @@ MediaCacheStream::GetNextCachedDataInter
NS_NOTREACHED("Should return in loop");
return -1;
}
void
MediaCacheStream::SetReadMode(ReadMode aMode)
{
// TODO: Assert non-main thread.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (aMode == mCurrentMode)
return;
mCurrentMode = aMode;
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
void
MediaCacheStream::SetPlaybackRate(uint32_t aBytesPerSecond)
{
MOZ_ASSERT(aBytesPerSecond > 0, "Zero playback rate not allowed");
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"MediaCacheStream::SetPlaybackRate",
[ =, client = RefPtr<ChannelMediaResource>(mClient) ]() {
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (!mClosed && mPlaybackBytesPerSecond != aBytesPerSecond) {
mPlaybackBytesPerSecond = aBytesPerSecond;
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
});
OwnerThread()->Dispatch(r.forget());
}
nsresult
-MediaCacheStream::Seek(int64_t aOffset)
+MediaCacheStream::Seek(AutoLock& aLock, int64_t aOffset)
{
MOZ_ASSERT(!NS_IsMainThread());
- mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
if (!IsOffsetAllowed(aOffset)) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (mClosed) {
return NS_ERROR_ABORT;
}
int64_t oldOffset = mStreamOffset;
mStreamOffset = aOffset;
LOG("Stream %p Seek to %" PRId64, this, mStreamOffset);
- mMediaCache->NoteSeek(this, oldOffset);
- mMediaCache->QueueUpdate();
+ mMediaCache->NoteSeek(aLock, this, oldOffset);
+ mMediaCache->QueueUpdate(aLock);
return NS_OK;
}
void
MediaCacheStream::ThrottleReadahead(bool bThrottle)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"MediaCacheStream::ThrottleReadahead",
[ client = RefPtr<ChannelMediaResource>(mClient), this, bThrottle ]() {
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
if (!mClosed && mThrottleReadahead != bThrottle) {
LOGI("Stream %p ThrottleReadahead %d", this, bThrottle);
mThrottleReadahead = bThrottle;
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
});
OwnerThread()->Dispatch(r.forget());
}
uint32_t
-MediaCacheStream::ReadPartialBlock(int64_t aOffset, Span<char> aBuffer)
+MediaCacheStream::ReadPartialBlock(AutoLock&,
+ int64_t aOffset,
+ Span<char> aBuffer)
{
- mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(IsOffsetAllowed(aOffset));
if (OffsetToBlockIndexUnchecked(mChannelOffset) !=
OffsetToBlockIndexUnchecked(aOffset) ||
aOffset >= mChannelOffset) {
// Not in the partial block or no data to read.
return 0;
}
@@ -2567,21 +2583,21 @@ MediaCacheStream::ReadPartialBlock(int64
// We have |source.Length() <= BLOCK_SIZE < INT32_MAX| to guarantee
// that |bytesToRead| can fit into a uint32_t.
uint32_t bytesToRead = std::min(aBuffer.Length(), source.Length());
memcpy(aBuffer.Elements(), source.Elements(), bytesToRead);
return bytesToRead;
}
Result<uint32_t, nsresult>
-MediaCacheStream::ReadBlockFromCache(int64_t aOffset,
+MediaCacheStream::ReadBlockFromCache(AutoLock& aLock,
+ int64_t aOffset,
Span<char> aBuffer,
bool aNoteBlockUsage)
{
- mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(IsOffsetAllowed(aOffset));
// OffsetToBlockIndexUnchecked() is always non-negative.
uint32_t index = OffsetToBlockIndexUnchecked(aOffset);
int32_t cacheBlock = index < mBlocks.Length() ? mBlocks[index] : -1;
if (cacheBlock < 0) {
// Not in the cache.
return 0;
@@ -2592,17 +2608,18 @@ MediaCacheStream::ReadBlockFromCache(int
// BLOCK_SIZE bytes.
aBuffer = aBuffer.First(BLOCK_SIZE);
}
// |BLOCK_SIZE - OffsetInBlock(aOffset)| <= BLOCK_SIZE
int32_t bytesToRead =
std::min<int32_t>(BLOCK_SIZE - OffsetInBlock(aOffset), aBuffer.Length());
int32_t bytesRead = 0;
nsresult rv =
- mMediaCache->ReadCacheFile(cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset),
+ mMediaCache->ReadCacheFile(aLock,
+ cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset),
aBuffer.Elements(),
bytesToRead,
&bytesRead);
// Ensure |cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset)| won't overflow.
static_assert(INT64_MAX >= BLOCK_SIZE * (uint32_t(INT32_MAX) + 1),
"BLOCK_SIZE too large!");
@@ -2610,27 +2627,27 @@ MediaCacheStream::ReadBlockFromCache(int
nsCString name;
GetErrorName(rv, name);
LOGE("Stream %p ReadCacheFile failed, rv=%s", this, name.Data());
return mozilla::Err(rv);
}
if (aNoteBlockUsage) {
mMediaCache->NoteBlockUsage(
- this, cacheBlock, aOffset, mCurrentMode, TimeStamp::Now());
+ aLock, this, cacheBlock, aOffset, mCurrentMode, TimeStamp::Now());
}
return bytesRead;
}
nsresult
MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
// Cache the offset in case it is changed again when we are waiting for the
// monitor to be notified to avoid reading at the wrong position.
auto streamOffset = mStreamOffset;
// The buffer we are about to fill.
auto buffer = MakeSpan<char>(aBuffer, aCount);
@@ -2650,18 +2667,18 @@ MediaCacheStream::Read(char* aBuffer, ui
return NS_ERROR_ILLEGAL_VALUE;
}
if (mStreamLength >= 0 && streamOffset >= mStreamLength) {
// Don't try to read beyond the end of the stream
break;
}
- Result<uint32_t, nsresult> rv =
- ReadBlockFromCache(streamOffset, buffer, true /* aNoteBlockUsage */);
+ Result<uint32_t, nsresult> rv = ReadBlockFromCache(
+ lock, streamOffset, buffer, true /* aNoteBlockUsage */);
if (rv.isErr()) {
return rv.unwrapErr();
}
uint32_t bytes = rv.unwrap();
if (bytes > 0) {
// Got data from the cache successfully. Read next block.
streamOffset += bytes;
@@ -2673,71 +2690,71 @@ MediaCacheStream::Read(char* aBuffer, ui
// this resource. Note we use the partial block only when it is completed,
// that is reaching EOS.
bool foundDataInPartialBlock = false;
MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
if (OffsetToBlockIndexUnchecked(stream->mChannelOffset) ==
OffsetToBlockIndexUnchecked(streamOffset) &&
stream->mChannelOffset == stream->mStreamLength) {
- uint32_t bytes = stream->ReadPartialBlock(streamOffset, buffer);
+ uint32_t bytes = stream->ReadPartialBlock(lock, streamOffset, buffer);
streamOffset += bytes;
buffer = buffer.From(bytes);
foundDataInPartialBlock = true;
break;
}
}
if (foundDataInPartialBlock) {
// Break for we've reached EOS.
break;
}
if (mStreamOffset != streamOffset) {
// Update mStreamOffset before we drop the lock. We need to run
// Update() again since stream reading strategy might have changed.
mStreamOffset = streamOffset;
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
}
// No data to read, so block
- mon.Wait();
+ lock.Wait();
continue;
}
uint32_t count = buffer.Elements() - aBuffer;
*aBytes = count;
if (count == 0) {
return NS_OK;
}
// Some data was read, so queue an update since block priorities may
// have changed
- mMediaCache->QueueUpdate();
+ mMediaCache->QueueUpdate(lock);
LOG("Stream %p Read at %" PRId64 " count=%d", this, streamOffset-count, count);
mStreamOffset = streamOffset;
return NS_OK;
}
nsresult
MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
uint32_t aCount, uint32_t* aBytes)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
- nsresult rv = Seek(aOffset);
+ AutoLock lock(mMediaCache->Monitor());
+ nsresult rv = Seek(lock, aOffset);
if (NS_FAILED(rv)) return rv;
return Read(aBuffer, aCount, aBytes);
}
nsresult
MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
{
MOZ_ASSERT(!NS_IsMainThread());
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
// The buffer we are about to fill.
auto buffer = MakeSpan<char>(aBuffer, aCount);
// Read one block (or part of a block) at a time
int64_t streamOffset = aOffset;
while (!buffer.IsEmpty()) {
if (mClosed) {
@@ -2746,31 +2763,32 @@ MediaCacheStream::ReadFromCache(char* aB
return NS_ERROR_FAILURE;
}
if (!IsOffsetAllowed(streamOffset)) {
LOGE("Stream %p invalid offset=%" PRId64, this, streamOffset);
return NS_ERROR_ILLEGAL_VALUE;
}
- Result<uint32_t, nsresult> rv = ReadBlockFromCache(streamOffset, buffer);
+ Result<uint32_t, nsresult> rv =
+ ReadBlockFromCache(lock, streamOffset, buffer);
if (rv.isErr()) {
return rv.unwrapErr();
}
uint32_t bytes = rv.unwrap();
if (bytes > 0) {
// Read data from the cache successfully. Let's try next block.
streamOffset += bytes;
buffer = buffer.From(bytes);
continue;
}
// The partial block is our last chance to get data.
- bytes = ReadPartialBlock(streamOffset, buffer);
+ bytes = ReadPartialBlock(lock, streamOffset, buffer);
if (bytes < buffer.Length()) {
// Not enough data to read.
return NS_ERROR_FAILURE;
}
// Return for we've got all the requested bytes.
return NS_OK;
}
@@ -2795,28 +2813,30 @@ MediaCacheStream::Init(int64_t aContentL
mStreamLength = aContentLength;
}
mMediaCache = MediaCache::GetMediaCache(aContentLength);
if (!mMediaCache) {
return NS_ERROR_FAILURE;
}
- mMediaCache->OpenStream(this);
+
+ AutoLock lock(mMediaCache->Monitor());
+ mMediaCache->OpenStream(lock, this);
return NS_OK;
}
nsresult
MediaCacheStream::InitAsClone(MediaCacheStream* aOriginal)
{
MOZ_ASSERT(aOriginal->IsAvailableForSharing());
MOZ_ASSERT(!mMediaCache, "Has been initialized.");
MOZ_ASSERT(aOriginal->mMediaCache, "Don't clone an uninitialized stream.");
- ReentrantMonitorAutoEnter mon(aOriginal->mMediaCache->GetReentrantMonitor());
+ AutoLock lock(aOriginal->mMediaCache->Monitor());
if (aOriginal->mDidNotifyDataEnded &&
NS_FAILED(aOriginal->mNotifyDataEndedStatus)) {
// Streams that ended abnormally are ineligible for cloning.
return NS_ERROR_FAILURE;
}
// This needs to be done before OpenStream() to avoid data race.
@@ -2848,58 +2868,58 @@ MediaCacheStream::InitAsClone(MediaCache
if (cacheBlockIndex < 0)
continue;
while (i >= mBlocks.Length()) {
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);
+ mMediaCache->AddBlockOwnerAsReadahead(lock, cacheBlockIndex, this, i);
}
- mMediaCache->OpenStream(this, true /* aIsClone */);
+ mMediaCache->OpenStream(lock, this, true /* aIsClone */);
return NS_OK;
}
nsIEventTarget*
MediaCacheStream::OwnerThread() const
{
return mMediaCache->OwnerThread();
}
nsresult MediaCacheStream::GetCachedRanges(MediaByteRangeSet& aRanges)
{
MOZ_ASSERT(!NS_IsMainThread());
// Take the monitor, so that the cached data ranges can't grow while we're
// trying to loop over them.
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
// We must be pinned while running this, otherwise the cached data ranges may
// shrink while we're trying to loop over them.
NS_ASSERTION(mPinCount > 0, "Must be pinned");
- int64_t startOffset = GetNextCachedDataInternal(0);
+ int64_t startOffset = GetNextCachedDataInternal(lock, 0);
while (startOffset >= 0) {
- int64_t endOffset = GetCachedDataEndInternal(startOffset);
+ int64_t endOffset = GetCachedDataEndInternal(lock, startOffset);
NS_ASSERTION(startOffset < endOffset, "Buffered range must end after its start");
// Bytes [startOffset..endOffset] are cached.
aRanges += MediaByteRange(startOffset, endOffset);
- startOffset = GetNextCachedDataInternal(endOffset);
+ startOffset = GetNextCachedDataInternal(lock, endOffset);
NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
"Must have advanced to start of next range, or hit end of stream");
}
return NS_OK;
}
nsCString
MediaCacheStream::GetDebugInfo()
{
- ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
+ AutoLock lock(mMediaCache->Monitor());
return nsPrintfCString("mStreamLength=%" PRId64 " mChannelOffset=%" PRId64
" mCacheSuspended=%d mChannelEnded=%d mLoadID=%u",
mStreamLength,
mChannelOffset,
mCacheSuspended,
mChannelEnded,
mLoadID);
}