content/media/FileBlockCache.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef FILE_BLOCK_CACHE_H_
michael@0 8 #define FILE_BLOCK_CACHE_H_
michael@0 9
michael@0 10 #include "mozilla/Attributes.h"
michael@0 11 #include "mozilla/Monitor.h"
michael@0 12 #include "nsTArray.h"
michael@0 13 #include "MediaCache.h"
michael@0 14 #include "nsDeque.h"
michael@0 15 #include "nsThreadUtils.h"
michael@0 16
michael@0 17 struct PRFileDesc;
michael@0 18
michael@0 19 namespace mozilla {
michael@0 20
michael@0 21 // Manages file I/O for the media cache. Data comes in over the network
michael@0 22 // via callbacks on the main thread, however we don't want to write the
michael@0 23 // incoming data to the media cache on the main thread, as this could block
michael@0 24 // causing UI jank.
michael@0 25 //
michael@0 26 // So FileBlockCache provides an abstraction for a temporary file accessible
michael@0 27 // as an array of blocks, which supports a block move operation, and
michael@0 28 // allows synchronous reading and writing from any thread, with writes being
michael@0 29 // buffered so as not to block.
michael@0 30 //
michael@0 31 // Writes and cache block moves (which require reading) are deferred to
michael@0 32 // their own non-main thread. This object also ensures that data which has
michael@0 33 // been scheduled to be written, but hasn't actually *been* written, is read
michael@0 34 // as if it had, i.e. pending writes are cached in readable memory until
michael@0 35 // they're flushed to file.
michael@0 36 //
michael@0 37 // To improve efficiency, writes can only be done at block granularity,
michael@0 38 // whereas reads can be done with byte granularity.
michael@0 39 //
michael@0 40 // Note it's also recommended not to read from the media cache from the main
michael@0 41 // thread to prevent jank.
michael@0 42 //
michael@0 43 // When WriteBlock() or MoveBlock() are called, data about how to complete
michael@0 44 // the block change is added to mBlockChanges, indexed by block index, and
michael@0 45 // the block index is appended to the mChangeIndexList. This enables
michael@0 46 // us to quickly tell if a block has been changed, and ensures we can perform
michael@0 47 // the changes in the correct order. An event is dispatched to perform the
michael@0 48 // changes listed in mBlockChanges to file. Read() checks mBlockChanges and
michael@0 49 // determines the current data to return, reading from file or from
michael@0 50 // mBlockChanges as necessary.
michael@0 51 class FileBlockCache : public nsRunnable {
michael@0 52 public:
michael@0 53 enum {
michael@0 54 BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE
michael@0 55 };
michael@0 56
michael@0 57 FileBlockCache();
michael@0 58
michael@0 59 ~FileBlockCache();
michael@0 60
michael@0 61 // Assumes ownership of aFD.
michael@0 62 nsresult Open(PRFileDesc* aFD);
michael@0 63
michael@0 64 // Closes writer, shuts down thread.
michael@0 65 void Close();
michael@0 66
michael@0 67 // Can be called on any thread. This defers to a non-main thread.
michael@0 68 nsresult WriteBlock(uint32_t aBlockIndex, const uint8_t* aData);
michael@0 69
michael@0 70 // Performs block writes and block moves on its own thread.
michael@0 71 NS_IMETHOD Run() MOZ_OVERRIDE;
michael@0 72
michael@0 73 // Synchronously reads data from file. May read from file or memory
michael@0 74 // depending on whether written blocks have been flushed to file yet.
michael@0 75 // Not recommended to be called from the main thread, as can cause jank.
michael@0 76 nsresult Read(int64_t aOffset,
michael@0 77 uint8_t* aData,
michael@0 78 int32_t aLength,
michael@0 79 int32_t* aBytes);
michael@0 80
michael@0 81 // Moves a block asynchronously. Can be called on any thread.
michael@0 82 // This defers file I/O to a non-main thread.
michael@0 83 nsresult MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex);
michael@0 84
michael@0 85 // Represents a change yet to be made to a block in the file. The change
michael@0 86 // is either a write (and the data to be written is stored in this struct)
michael@0 87 // or a move (and the index of the source block is stored instead).
michael@0 88 struct BlockChange MOZ_FINAL {
michael@0 89
michael@0 90 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
michael@0 91
michael@0 92 // This block is waiting in memory to be written.
michael@0 93 // Stores a copy of the block, so we can write it asynchronously.
michael@0 94 BlockChange(const uint8_t* aData)
michael@0 95 : mSourceBlockIndex(-1)
michael@0 96 {
michael@0 97 mData = new uint8_t[BLOCK_SIZE];
michael@0 98 memcpy(mData.get(), aData, BLOCK_SIZE);
michael@0 99 }
michael@0 100
michael@0 101 // This block's contents are located in another file
michael@0 102 // block, i.e. this block has been moved.
michael@0 103 BlockChange(int32_t aSourceBlockIndex)
michael@0 104 : mSourceBlockIndex(aSourceBlockIndex) {}
michael@0 105
michael@0 106 nsAutoArrayPtr<uint8_t> mData;
michael@0 107 const int32_t mSourceBlockIndex;
michael@0 108
michael@0 109 bool IsMove() const {
michael@0 110 return mSourceBlockIndex != -1;
michael@0 111 }
michael@0 112 bool IsWrite() const {
michael@0 113 return mSourceBlockIndex == -1 &&
michael@0 114 mData.get() != nullptr;
michael@0 115 }
michael@0 116
michael@0 117 private:
michael@0 118 // Private destructor, to discourage deletion outside of Release():
michael@0 119 ~BlockChange()
michael@0 120 {
michael@0 121 }
michael@0 122 };
michael@0 123
michael@0 124 class Int32Queue : private nsDeque {
michael@0 125 public:
michael@0 126 int32_t PopFront() {
michael@0 127 int32_t front = ObjectAt(0);
michael@0 128 nsDeque::PopFront();
michael@0 129 return front;
michael@0 130 }
michael@0 131
michael@0 132 void PushBack(int32_t aValue) {
michael@0 133 nsDeque::Push(reinterpret_cast<void*>(aValue));
michael@0 134 }
michael@0 135
michael@0 136 bool Contains(int32_t aValue) {
michael@0 137 for (int32_t i = 0; i < GetSize(); ++i) {
michael@0 138 if (ObjectAt(i) == aValue) {
michael@0 139 return true;
michael@0 140 }
michael@0 141 }
michael@0 142 return false;
michael@0 143 }
michael@0 144
michael@0 145 bool IsEmpty() {
michael@0 146 return nsDeque::GetSize() == 0;
michael@0 147 }
michael@0 148
michael@0 149 private:
michael@0 150 int32_t ObjectAt(int32_t aIndex) {
michael@0 151 void* v = nsDeque::ObjectAt(aIndex);
michael@0 152 return reinterpret_cast<uintptr_t>(v);
michael@0 153 }
michael@0 154 };
michael@0 155
michael@0 156 private:
michael@0 157 int64_t BlockIndexToOffset(int32_t aBlockIndex) {
michael@0 158 return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
michael@0 159 }
michael@0 160
michael@0 161 // Monitor which controls access to mFD and mFDCurrentPos. Don't hold
michael@0 162 // mDataMonitor while holding mFileMonitor! mFileMonitor must be owned
michael@0 163 // while accessing any of the following data fields or methods.
michael@0 164 Monitor mFileMonitor;
michael@0 165 // Moves a block already committed to file.
michael@0 166 nsresult MoveBlockInFile(int32_t aSourceBlockIndex,
michael@0 167 int32_t aDestBlockIndex);
michael@0 168 // Seeks file pointer.
michael@0 169 nsresult Seek(int64_t aOffset);
michael@0 170 // Reads data from file offset.
michael@0 171 nsresult ReadFromFile(int64_t aOffset,
michael@0 172 uint8_t* aDest,
michael@0 173 int32_t aBytesToRead,
michael@0 174 int32_t& aBytesRead);
michael@0 175 nsresult WriteBlockToFile(int32_t aBlockIndex, const uint8_t* aBlockData);
michael@0 176 // File descriptor we're writing to. This is created externally, but
michael@0 177 // shutdown by us.
michael@0 178 PRFileDesc* mFD;
michael@0 179 // The current file offset in the file.
michael@0 180 int64_t mFDCurrentPos;
michael@0 181
michael@0 182 // Monitor which controls access to all data in this class, except mFD
michael@0 183 // and mFDCurrentPos. Don't hold mDataMonitor while holding mFileMonitor!
michael@0 184 // mDataMonitor must be owned while accessing any of the following data
michael@0 185 // fields or methods.
michael@0 186 Monitor mDataMonitor;
michael@0 187 // Ensures we either are running the event to preform IO, or an event
michael@0 188 // has been dispatched to preform the IO.
michael@0 189 // mDataMonitor must be owned while calling this.
michael@0 190 void EnsureWriteScheduled();
michael@0 191 // Array of block changes to made. If mBlockChanges[offset/BLOCK_SIZE] == nullptr,
michael@0 192 // then the block has no pending changes to be written, but if
michael@0 193 // mBlockChanges[offset/BLOCK_SIZE] != nullptr, then either there's a block
michael@0 194 // cached in memory waiting to be written, or this block is the target of a
michael@0 195 // block move.
michael@0 196 nsTArray< nsRefPtr<BlockChange> > mBlockChanges;
michael@0 197 // Thread upon which block writes and block moves are performed. This is
michael@0 198 // created upon open, and shutdown (asynchronously) upon close (on the
michael@0 199 // main thread).
michael@0 200 nsCOMPtr<nsIThread> mThread;
michael@0 201 // Queue of pending block indexes that need to be written or moved.
michael@0 202 //nsAutoTArray<int32_t, 8> mChangeIndexList;
michael@0 203 Int32Queue mChangeIndexList;
michael@0 204 // True if we've dispatched an event to commit all pending block changes
michael@0 205 // to file on mThread.
michael@0 206 bool mIsWriteScheduled;
michael@0 207 // True if the writer is ready to write data to file.
michael@0 208 bool mIsOpen;
michael@0 209 };
michael@0 210
michael@0 211 } // End namespace mozilla.
michael@0 212
michael@0 213 #endif /* FILE_BLOCK_CACHE_H_ */

mercurial