Bug 1420798 - apply the 'ProofOfLock' pattern to MediaCache. draft
authorJW Wang <jwwang@mozilla.com>
Wed, 22 Nov 2017 17:14:02 +0800
changeset 703991 d956366c122d6b92e610aed357d139e987c5a4e4
parent 703990 e34f7a1be4d692275c3031477b16d90ee129a2e3
child 741953 2b79e2a813261ef4a210dd6063acee2e6d768b3e
push id91022
push userjwwang@mozilla.com
push dateTue, 28 Nov 2017 02:09:13 +0000
bugs1420798
milestone59.0a1
Bug 1420798 - apply the 'ProofOfLock' pattern to MediaCache. See comment 0 for the detail. We will replace ReentrantMonitor with Monitor in the future. MozReview-Commit-ID: 63ygEFWXHZd
dom/media/MediaCache.cpp
dom/media/MediaCache.h
--- 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);
 }
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -181,16 +181,18 @@ class MediaCache;
 
 /**
  * If the cache fails to initialize then Init will fail, so nonstatic
  * methods of this class can assume gMediaCache is non-null.
  *
  * This class can be directly embedded as a value.
  */
 class MediaCacheStream {
+  using AutoLock = ReentrantMonitorAutoEnter;
+
 public:
   // This needs to be a power of two
   static const int64_t BLOCK_SIZE = 32768;
 
   enum ReadMode {
     MODE_METADATA,
     MODE_PLAYBACK
   };
@@ -329,17 +331,17 @@ public:
   // This is the client's estimate of the playback rate assuming
   // the media plays continuously. The cache can't guess this itself
   // because it doesn't know when the decoder was paused, buffering, etc.
   // Do not pass zero.
   void SetPlaybackRate(uint32_t aBytesPerSecond);
 
   // Returns true when all streams for this resource are suspended or their
   // channel has ended.
-  bool AreAllStreamsForResourceSuspended();
+  bool AreAllStreamsForResourceSuspended(AutoLock&);
 
   // These methods must be called on a different thread from the main
   // thread. They should always be called on the same thread for a given
   // stream.
   // *aBytes gets the number of bytes that were actually read. This can
   // be less than aCount. If the first byte of data is not in the cache,
   // this will block until the data is available or the stream is
   // closed, otherwise it won't block.
@@ -419,41 +421,42 @@ private:
     int32_t mFirstBlock;
     // The number of blocks in the list.
     int32_t mCount;
   };
 
   // Read data from the partial block and return the number of bytes read
   // successfully. 0 if aOffset is not an offset in the partial block or there
   // is nothing to read.
-  uint32_t ReadPartialBlock(int64_t aOffset, Span<char> aBuffer);
+  uint32_t ReadPartialBlock(AutoLock&, int64_t aOffset, Span<char> aBuffer);
 
   // Read data from the cache block specified by aOffset. Return the number of
   // bytes read successfully or an error code if any failure.
-  Result<uint32_t, nsresult> ReadBlockFromCache(int64_t aOffset,
+  Result<uint32_t, nsresult> ReadBlockFromCache(AutoLock&,
+                                                int64_t aOffset,
                                                 Span<char> aBuffer,
                                                 bool aNoteBlockUsage = false);
 
   // Non-main thread only.
-  nsresult Seek(int64_t aOffset);
+  nsresult Seek(AutoLock&, int64_t aOffset);
 
   // Returns the end of the bytes starting at the given offset
   // which are in cache.
   // This method assumes that the cache monitor is held and can be called on
   // any thread.
-  int64_t GetCachedDataEndInternal(int64_t aOffset);
+  int64_t GetCachedDataEndInternal(AutoLock&, int64_t aOffset);
   // Returns the offset of the first byte of cached data at or after aOffset,
   // or -1 if there is no such cached data.
   // This method assumes that the cache monitor is held and can be called on
   // any thread.
-  int64_t GetNextCachedDataInternal(int64_t aOffset);
+  int64_t GetNextCachedDataInternal(AutoLock&, int64_t aOffset);
   // Used by |NotifyDataEnded| to write |mPartialBlock| to disk.
   // If |aNotifyAll| is true, this function will wake up readers who may be
   // waiting on the media cache monitor. Called on the main thread only.
-  void FlushPartialBlockInternal(bool aNotify, ReentrantMonitorAutoEnter& aReentrantMonitor);
+  void FlushPartialBlockInternal(AutoLock&, bool aNotify);
 
   void NotifyDataStartedInternal(uint32_t aLoadID,
                                  int64_t aOffset,
                                  bool aSeekable,
                                  int64_t aLength);
 
   void NotifyDataEndedInternal(uint32_t aLoadID,
                                nsresult aStatus,