Bug 1371882 - Virtualize FileBlockCache's API into MediaBlockCacheBase - r=cpearce
MozReview-Commit-ID: Eya0RWiyiEP
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -8,17 +8,17 @@
#define FILE_BLOCK_CACHE_H_
#include "mozilla/Attributes.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/AbstractThread.h"
#include "nsTArray.h"
-#include "MediaCache.h"
+#include "MediaBlockCacheBase.h"
#include "nsDeque.h"
#include "nsThreadUtils.h"
#include <deque>
struct PRFileDesc;
namespace mozilla {
@@ -47,51 +47,47 @@ namespace mozilla {
// When WriteBlock() or MoveBlock() are called, data about how to complete
// the block change is added to mBlockChanges, indexed by block index, and
// the block index is appended to the mChangeIndexList. This enables
// us to quickly tell if a block has been changed, and ensures we can perform
// the changes in the correct order. An event is dispatched to perform the
// changes listed in mBlockChanges to file. Read() checks mBlockChanges and
// determines the current data to return, reading from file or from
// mBlockChanges as necessary.
-class FileBlockCache
+class FileBlockCache : public MediaBlockCacheBase
{
public:
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileBlockCache)
-
- enum {
- BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE
- };
-
FileBlockCache();
protected:
- ~FileBlockCache();
+ virtual ~FileBlockCache();
public:
- nsresult Init();
+ nsresult Init() override;
// Closes writer, shuts down thread.
- void Close();
+ void Close() override;
// Can be called on any thread. This defers to a non-main thread.
nsresult WriteBlock(uint32_t aBlockIndex,
- Span<const uint8_t> aData1, Span<const uint8_t> aData2);
+ Span<const uint8_t> aData1,
+ Span<const uint8_t> aData2) override;
// Synchronously reads data from file. May read from file or memory
// depending on whether written blocks have been flushed to file yet.
// Not recommended to be called from the main thread, as can cause jank.
nsresult Read(int64_t aOffset,
uint8_t* aData,
int32_t aLength,
- int32_t* aBytes);
+ int32_t* aBytes) override;
// Moves a block asynchronously. Can be called on any thread.
// This defers file I/O to a non-main thread.
- nsresult MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex);
+ nsresult MoveBlock(int32_t aSourceBlockIndex,
+ int32_t aDestBlockIndex) override;
// Represents a change yet to be made to a block in the file. The change
// is either a write (and the data to be written is stored in this struct)
// or a move (and the index of the source block is stored instead).
struct BlockChange final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
copy from dom/media/FileBlockCache.h
copy to dom/media/MediaBlockCacheBase.h
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/MediaBlockCacheBase.h
@@ -1,208 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef FILE_BLOCK_CACHE_H_
-#define FILE_BLOCK_CACHE_H_
+#ifndef MEDIA_BLOCK_CACHE_BASE_H_
+#define MEDIA_BLOCK_CACHE_BASE_H_
-#include "mozilla/Attributes.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/UniquePtr.h"
-#include "mozilla/AbstractThread.h"
-#include "nsTArray.h"
#include "MediaCache.h"
-#include "nsDeque.h"
-#include "nsThreadUtils.h"
-#include <deque>
-
-struct PRFileDesc;
+#include "mozilla/Span.h"
namespace mozilla {
-// Manages file I/O for the media cache. Data comes in over the network
+// Manages block management for the media cache. Data comes in over the network
// via callbacks on the main thread, however we don't want to write the
// incoming data to the media cache on the main thread, as this could block
// causing UI jank.
//
-// So FileBlockCache provides an abstraction for a temporary file accessible
+// So MediaBlockCacheBase provides an abstraction for a temporary memory buffer or file accessible
// as an array of blocks, which supports a block move operation, and
// allows synchronous reading and writing from any thread, with writes being
-// buffered so as not to block.
+// buffered as needed so as not to block.
//
-// Writes and cache block moves (which require reading) are deferred to
+// Writes and cache block moves (which require reading) may be deferred to
// their own non-main thread. This object also ensures that data which has
// been scheduled to be written, but hasn't actually *been* written, is read
// as if it had, i.e. pending writes are cached in readable memory until
// they're flushed to file.
//
// To improve efficiency, writes can only be done at block granularity,
// whereas reads can be done with byte granularity.
//
// Note it's also recommended not to read from the media cache from the main
// thread to prevent jank.
-//
-// When WriteBlock() or MoveBlock() are called, data about how to complete
-// the block change is added to mBlockChanges, indexed by block index, and
-// the block index is appended to the mChangeIndexList. This enables
-// us to quickly tell if a block has been changed, and ensures we can perform
-// the changes in the correct order. An event is dispatched to perform the
-// changes listed in mBlockChanges to file. Read() checks mBlockChanges and
-// determines the current data to return, reading from file or from
-// mBlockChanges as necessary.
-class FileBlockCache
+class MediaBlockCacheBase
{
public:
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileBlockCache)
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaBlockCacheBase)
- enum {
- BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE
- };
-
- FileBlockCache();
+ static_assert(
+ MediaCacheStream::BLOCK_SIZE <
+ static_cast<decltype(MediaCacheStream::BLOCK_SIZE)>(INT32_MAX),
+ "MediaCacheStream::BLOCK_SIZE should fit in 31 bits");
+ static const int32_t BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE;
protected:
- ~FileBlockCache();
+ virtual ~MediaBlockCacheBase() {}
public:
- nsresult Init();
+ virtual nsresult Init() = 0;
// Closes writer, shuts down thread.
- void Close();
+ virtual void Close() = 0;
// Can be called on any thread. This defers to a non-main thread.
- nsresult WriteBlock(uint32_t aBlockIndex,
- Span<const uint8_t> aData1, Span<const uint8_t> aData2);
+ virtual nsresult WriteBlock(uint32_t aBlockIndex,
+ Span<const uint8_t> aData1,
+ Span<const uint8_t> aData2) = 0;
// Synchronously reads data from file. May read from file or memory
// depending on whether written blocks have been flushed to file yet.
// Not recommended to be called from the main thread, as can cause jank.
- nsresult Read(int64_t aOffset,
- uint8_t* aData,
- int32_t aLength,
- int32_t* aBytes);
+ virtual nsresult Read(int64_t aOffset,
+ uint8_t* aData,
+ int32_t aLength,
+ int32_t* aBytes) = 0;
// Moves a block asynchronously. Can be called on any thread.
// This defers file I/O to a non-main thread.
- nsresult MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex);
-
- // Represents a change yet to be made to a block in the file. The change
- // is either a write (and the data to be written is stored in this struct)
- // or a move (and the index of the source block is stored instead).
- struct BlockChange final {
-
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
-
- // This block is waiting in memory to be written.
- // Stores a copy of the block, so we can write it asynchronously.
- explicit BlockChange(const uint8_t* aData)
- : mSourceBlockIndex(-1)
- {
- mData = MakeUnique<uint8_t[]>(BLOCK_SIZE);
- memcpy(mData.get(), aData, BLOCK_SIZE);
- }
-
- BlockChange(Span<const uint8_t> aData1, Span<const uint8_t> aData2)
- : mSourceBlockIndex(-1)
- {
- MOZ_ASSERT(aData1.Length() + aData2.Length() == BLOCK_SIZE);
- mData = MakeUnique<uint8_t[]>(BLOCK_SIZE);
- memcpy(mData.get(), aData1.Elements(), aData1.Length());
- memcpy(mData.get() + aData1.Length(), aData2.Elements(), aData2.Length());
- }
-
- // This block's contents are located in another file
- // block, i.e. this block has been moved.
- explicit BlockChange(int32_t aSourceBlockIndex)
- : mSourceBlockIndex(aSourceBlockIndex) {}
-
- UniquePtr<uint8_t[]> mData;
- const int32_t mSourceBlockIndex;
-
- bool IsMove() const {
- return mSourceBlockIndex != -1;
- }
- bool IsWrite() const {
- return mSourceBlockIndex == -1 &&
- mData.get() != nullptr;
- }
-
- private:
- // Private destructor, to discourage deletion outside of Release():
- ~BlockChange()
- {
- }
- };
-
-private:
- int64_t BlockIndexToOffset(int32_t aBlockIndex) {
- return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
- }
-
- void SetCacheFile(PRFileDesc* aFD);
-
- // Performs block writes and block moves on its own thread.
- void PerformBlockIOs();
-
- // Mutex which controls access to mFD and mFDCurrentPos. Don't hold
- // mDataMutex while holding mFileMutex! mFileMutex must be owned
- // while accessing any of the following data fields or methods.
- Mutex mFileMutex;
- // Moves a block already committed to file.
- nsresult MoveBlockInFile(int32_t aSourceBlockIndex,
- int32_t aDestBlockIndex);
- // Seeks file pointer.
- nsresult Seek(int64_t aOffset);
- // Reads data from file offset.
- nsresult ReadFromFile(int64_t aOffset,
- uint8_t* aDest,
- int32_t aBytesToRead,
- int32_t& aBytesRead);
- nsresult WriteBlockToFile(int32_t aBlockIndex, const uint8_t* aBlockData);
- // File descriptor we're writing to. This is created externally, but
- // shutdown by us.
- PRFileDesc* mFD;
- // The current file offset in the file.
- int64_t mFDCurrentPos;
-
- // Mutex which controls access to all data in this class, except mFD
- // and mFDCurrentPos. Don't hold mDataMutex while holding mFileMutex!
- // mDataMutex must be owned while accessing any of the following data
- // fields or methods.
- Mutex mDataMutex;
- // Ensures we either are running the event to preform IO, or an event
- // has been dispatched to preform the IO.
- // mDataMutex must be owned while calling this.
- void EnsureWriteScheduled();
-
- // Array of block changes to made. If mBlockChanges[offset/BLOCK_SIZE] == nullptr,
- // then the block has no pending changes to be written, but if
- // mBlockChanges[offset/BLOCK_SIZE] != nullptr, then either there's a block
- // cached in memory waiting to be written, or this block is the target of a
- // block move.
- nsTArray< RefPtr<BlockChange> > mBlockChanges;
- // Thread upon which block writes and block moves are performed. This is
- // created upon open, and shutdown (asynchronously) upon close (on the
- // main thread).
- nsCOMPtr<nsIThread> mThread;
- // Queue of pending block indexes that need to be written or moved.
- std::deque<int32_t> mChangeIndexList;
- // True if we've dispatched an event to commit all pending block changes
- // to file on mThread.
- bool mIsWriteScheduled;
- // True when a read is happening. Pending writes may be postponed, to give
- // higher priority to reads (which may be blocking the caller).
- bool mIsReading;
- // True if the writer is ready to enqueue writes.
- bool mIsOpen;
- // True if we've got a temporary file descriptor. Note: we don't use mFD
- // directly as that's synchronized via mFileMutex and we need to make
- // decisions about whether we can write while holding mDataMutex.
- bool mInitialized = false;
+ virtual nsresult MoveBlock(int32_t aSourceBlockIndex,
+ int32_t aDestBlockIndex) = 0;
};
} // End namespace mozilla.
-#endif /* FILE_BLOCK_CACHE_H_ */
+#endif /* MEDIA_BLOCK_CACHE_BASE_H_ */
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -9,16 +9,17 @@
#include "MediaCache.h"
#include "prio.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "MediaResource.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "FileBlockCache.h"
+#include "MediaBlockCacheBase.h"
#include "nsIObserverService.h"
#include "nsISeekableStream.h"
#include "nsIPrincipal.h"
#include "mozilla/Attributes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include <algorithm>
@@ -410,17 +411,17 @@ protected:
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.
uint32_t mBlockOwnersWatermark = 0;
// Writer which performs IO, asynchronously writing cache blocks.
- RefPtr<FileBlockCache> mFileCache;
+ RefPtr<MediaBlockCacheBase> mFileCache;
// The list of free blocks; they are not ordered.
BlockList mFreeBlocks;
// True if an event to run Update() has been queued but not processed
bool mUpdateQueued;
// Main-thread only. True when shutting down, and the update task should
// destroy this MediaCache.
bool mShutdownInsteadOfUpdating;
#ifdef DEBUG
@@ -770,17 +771,17 @@ MediaCache::GetMediaCache(int64_t aConte
return gMediaCache;
}
nsresult
MediaCache::ReadCacheFile(
int64_t aOffset, void* aData, int32_t aLength, int32_t* aBytes)
{
mReentrantMonitor.AssertCurrentThreadIn();
- RefPtr<FileBlockCache> fileCache = mFileCache;
+ RefPtr<MediaBlockCacheBase> fileCache = mFileCache;
if (!fileCache) {
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);
return fileCache->Read(aOffset,