Bug 1371882 - Virtualize FileBlockCache's API into MediaBlockCacheBase - r?cpearce draft
authorGerald Squelart <gsquelart@mozilla.com>
Fri, 09 Jun 2017 14:26:22 +1200
changeset 593032 520b0ceb707390a1a7e4d45d670d51cd785534a8
parent 593031 23c4bd7346e577e87d93e5ab8f2e58b8e21475d3
child 593033 dfd54d50eb3cbfd92958022b81ac4fc27a36120b
push id63579
push usergsquelart@mozilla.com
push dateTue, 13 Jun 2017 05:16:04 +0000
reviewerscpearce
bugs1371882
milestone56.0a1
Bug 1371882 - Virtualize FileBlockCache's API into MediaBlockCacheBase - r?cpearce MozReview-Commit-ID: Eya0RWiyiEP
dom/media/FileBlockCache.h
dom/media/MediaBlockCacheBase.h
dom/media/MediaCache.cpp
--- 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>
@@ -400,17 +401,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
@@ -760,17 +761,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,