summaryrefslogtreecommitdiffstats
path: root/dom/media/FileBlockCache.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/FileBlockCache.h')
-rw-r--r--dom/media/FileBlockCache.h184
1 files changed, 184 insertions, 0 deletions
diff --git a/dom/media/FileBlockCache.h b/dom/media/FileBlockCache.h
new file mode 100644
index 000000000..e23a5c70b
--- /dev/null
+++ b/dom/media/FileBlockCache.h
@@ -0,0 +1,184 @@
+/* -*- 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_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+#include "MediaCache.h"
+#include "nsDeque.h"
+#include "nsThreadUtils.h"
+#include <deque>
+
+struct PRFileDesc;
+
+namespace mozilla {
+
+// Manages file I/O 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
+// 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.
+//
+// Writes and cache block moves (which require reading) are 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 : public Runnable {
+public:
+ enum {
+ BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE
+ };
+
+ FileBlockCache();
+
+protected:
+ ~FileBlockCache();
+
+public:
+ // Assumes ownership of aFD.
+ nsresult Open(PRFileDesc* aFD);
+
+ // Closes writer, shuts down thread.
+ void Close();
+
+ // Can be called on any thread. This defers to a non-main thread.
+ nsresult WriteBlock(uint32_t aBlockIndex, const uint8_t* aData);
+
+ // Performs block writes and block moves on its own thread.
+ NS_IMETHOD Run() 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);
+
+ // 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);
+ }
+
+ // 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;
+ }
+
+ // Monitor which controls access to mFD and mFDCurrentPos. Don't hold
+ // mDataMonitor while holding mFileMonitor! mFileMonitor must be owned
+ // while accessing any of the following data fields or methods.
+ Monitor mFileMonitor;
+ // 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;
+
+ // Monitor which controls access to all data in this class, except mFD
+ // and mFDCurrentPos. Don't hold mDataMonitor while holding mFileMonitor!
+ // mDataMonitor must be owned while accessing any of the following data
+ // fields or methods.
+ Monitor mDataMonitor;
+ // Ensures we either are running the event to preform IO, or an event
+ // has been dispatched to preform the IO.
+ // mDataMonitor 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 if the writer is ready to write data to file.
+ bool mIsOpen;
+};
+
+} // End namespace mozilla.
+
+#endif /* FILE_BLOCK_CACHE_H_ */