1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/FileBlockCache.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,213 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef FILE_BLOCK_CACHE_H_ 1.11 +#define FILE_BLOCK_CACHE_H_ 1.12 + 1.13 +#include "mozilla/Attributes.h" 1.14 +#include "mozilla/Monitor.h" 1.15 +#include "nsTArray.h" 1.16 +#include "MediaCache.h" 1.17 +#include "nsDeque.h" 1.18 +#include "nsThreadUtils.h" 1.19 + 1.20 +struct PRFileDesc; 1.21 + 1.22 +namespace mozilla { 1.23 + 1.24 +// Manages file I/O for the media cache. Data comes in over the network 1.25 +// via callbacks on the main thread, however we don't want to write the 1.26 +// incoming data to the media cache on the main thread, as this could block 1.27 +// causing UI jank. 1.28 +// 1.29 +// So FileBlockCache provides an abstraction for a temporary file accessible 1.30 +// as an array of blocks, which supports a block move operation, and 1.31 +// allows synchronous reading and writing from any thread, with writes being 1.32 +// buffered so as not to block. 1.33 +// 1.34 +// Writes and cache block moves (which require reading) are deferred to 1.35 +// their own non-main thread. This object also ensures that data which has 1.36 +// been scheduled to be written, but hasn't actually *been* written, is read 1.37 +// as if it had, i.e. pending writes are cached in readable memory until 1.38 +// they're flushed to file. 1.39 +// 1.40 +// To improve efficiency, writes can only be done at block granularity, 1.41 +// whereas reads can be done with byte granularity. 1.42 +// 1.43 +// Note it's also recommended not to read from the media cache from the main 1.44 +// thread to prevent jank. 1.45 +// 1.46 +// When WriteBlock() or MoveBlock() are called, data about how to complete 1.47 +// the block change is added to mBlockChanges, indexed by block index, and 1.48 +// the block index is appended to the mChangeIndexList. This enables 1.49 +// us to quickly tell if a block has been changed, and ensures we can perform 1.50 +// the changes in the correct order. An event is dispatched to perform the 1.51 +// changes listed in mBlockChanges to file. Read() checks mBlockChanges and 1.52 +// determines the current data to return, reading from file or from 1.53 +// mBlockChanges as necessary. 1.54 +class FileBlockCache : public nsRunnable { 1.55 +public: 1.56 + enum { 1.57 + BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE 1.58 + }; 1.59 + 1.60 + FileBlockCache(); 1.61 + 1.62 + ~FileBlockCache(); 1.63 + 1.64 + // Assumes ownership of aFD. 1.65 + nsresult Open(PRFileDesc* aFD); 1.66 + 1.67 + // Closes writer, shuts down thread. 1.68 + void Close(); 1.69 + 1.70 + // Can be called on any thread. This defers to a non-main thread. 1.71 + nsresult WriteBlock(uint32_t aBlockIndex, const uint8_t* aData); 1.72 + 1.73 + // Performs block writes and block moves on its own thread. 1.74 + NS_IMETHOD Run() MOZ_OVERRIDE; 1.75 + 1.76 + // Synchronously reads data from file. May read from file or memory 1.77 + // depending on whether written blocks have been flushed to file yet. 1.78 + // Not recommended to be called from the main thread, as can cause jank. 1.79 + nsresult Read(int64_t aOffset, 1.80 + uint8_t* aData, 1.81 + int32_t aLength, 1.82 + int32_t* aBytes); 1.83 + 1.84 + // Moves a block asynchronously. Can be called on any thread. 1.85 + // This defers file I/O to a non-main thread. 1.86 + nsresult MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlockIndex); 1.87 + 1.88 + // Represents a change yet to be made to a block in the file. The change 1.89 + // is either a write (and the data to be written is stored in this struct) 1.90 + // or a move (and the index of the source block is stored instead). 1.91 + struct BlockChange MOZ_FINAL { 1.92 + 1.93 + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange) 1.94 + 1.95 + // This block is waiting in memory to be written. 1.96 + // Stores a copy of the block, so we can write it asynchronously. 1.97 + BlockChange(const uint8_t* aData) 1.98 + : mSourceBlockIndex(-1) 1.99 + { 1.100 + mData = new uint8_t[BLOCK_SIZE]; 1.101 + memcpy(mData.get(), aData, BLOCK_SIZE); 1.102 + } 1.103 + 1.104 + // This block's contents are located in another file 1.105 + // block, i.e. this block has been moved. 1.106 + BlockChange(int32_t aSourceBlockIndex) 1.107 + : mSourceBlockIndex(aSourceBlockIndex) {} 1.108 + 1.109 + nsAutoArrayPtr<uint8_t> mData; 1.110 + const int32_t mSourceBlockIndex; 1.111 + 1.112 + bool IsMove() const { 1.113 + return mSourceBlockIndex != -1; 1.114 + } 1.115 + bool IsWrite() const { 1.116 + return mSourceBlockIndex == -1 && 1.117 + mData.get() != nullptr; 1.118 + } 1.119 + 1.120 + private: 1.121 + // Private destructor, to discourage deletion outside of Release(): 1.122 + ~BlockChange() 1.123 + { 1.124 + } 1.125 + }; 1.126 + 1.127 + class Int32Queue : private nsDeque { 1.128 + public: 1.129 + int32_t PopFront() { 1.130 + int32_t front = ObjectAt(0); 1.131 + nsDeque::PopFront(); 1.132 + return front; 1.133 + } 1.134 + 1.135 + void PushBack(int32_t aValue) { 1.136 + nsDeque::Push(reinterpret_cast<void*>(aValue)); 1.137 + } 1.138 + 1.139 + bool Contains(int32_t aValue) { 1.140 + for (int32_t i = 0; i < GetSize(); ++i) { 1.141 + if (ObjectAt(i) == aValue) { 1.142 + return true; 1.143 + } 1.144 + } 1.145 + return false; 1.146 + } 1.147 + 1.148 + bool IsEmpty() { 1.149 + return nsDeque::GetSize() == 0; 1.150 + } 1.151 + 1.152 + private: 1.153 + int32_t ObjectAt(int32_t aIndex) { 1.154 + void* v = nsDeque::ObjectAt(aIndex); 1.155 + return reinterpret_cast<uintptr_t>(v); 1.156 + } 1.157 + }; 1.158 + 1.159 +private: 1.160 + int64_t BlockIndexToOffset(int32_t aBlockIndex) { 1.161 + return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE; 1.162 + } 1.163 + 1.164 + // Monitor which controls access to mFD and mFDCurrentPos. Don't hold 1.165 + // mDataMonitor while holding mFileMonitor! mFileMonitor must be owned 1.166 + // while accessing any of the following data fields or methods. 1.167 + Monitor mFileMonitor; 1.168 + // Moves a block already committed to file. 1.169 + nsresult MoveBlockInFile(int32_t aSourceBlockIndex, 1.170 + int32_t aDestBlockIndex); 1.171 + // Seeks file pointer. 1.172 + nsresult Seek(int64_t aOffset); 1.173 + // Reads data from file offset. 1.174 + nsresult ReadFromFile(int64_t aOffset, 1.175 + uint8_t* aDest, 1.176 + int32_t aBytesToRead, 1.177 + int32_t& aBytesRead); 1.178 + nsresult WriteBlockToFile(int32_t aBlockIndex, const uint8_t* aBlockData); 1.179 + // File descriptor we're writing to. This is created externally, but 1.180 + // shutdown by us. 1.181 + PRFileDesc* mFD; 1.182 + // The current file offset in the file. 1.183 + int64_t mFDCurrentPos; 1.184 + 1.185 + // Monitor which controls access to all data in this class, except mFD 1.186 + // and mFDCurrentPos. Don't hold mDataMonitor while holding mFileMonitor! 1.187 + // mDataMonitor must be owned while accessing any of the following data 1.188 + // fields or methods. 1.189 + Monitor mDataMonitor; 1.190 + // Ensures we either are running the event to preform IO, or an event 1.191 + // has been dispatched to preform the IO. 1.192 + // mDataMonitor must be owned while calling this. 1.193 + void EnsureWriteScheduled(); 1.194 + // Array of block changes to made. If mBlockChanges[offset/BLOCK_SIZE] == nullptr, 1.195 + // then the block has no pending changes to be written, but if 1.196 + // mBlockChanges[offset/BLOCK_SIZE] != nullptr, then either there's a block 1.197 + // cached in memory waiting to be written, or this block is the target of a 1.198 + // block move. 1.199 + nsTArray< nsRefPtr<BlockChange> > mBlockChanges; 1.200 + // Thread upon which block writes and block moves are performed. This is 1.201 + // created upon open, and shutdown (asynchronously) upon close (on the 1.202 + // main thread). 1.203 + nsCOMPtr<nsIThread> mThread; 1.204 + // Queue of pending block indexes that need to be written or moved. 1.205 + //nsAutoTArray<int32_t, 8> mChangeIndexList; 1.206 + Int32Queue mChangeIndexList; 1.207 + // True if we've dispatched an event to commit all pending block changes 1.208 + // to file on mThread. 1.209 + bool mIsWriteScheduled; 1.210 + // True if the writer is ready to write data to file. 1.211 + bool mIsOpen; 1.212 +}; 1.213 + 1.214 +} // End namespace mozilla. 1.215 + 1.216 +#endif /* FILE_BLOCK_CACHE_H_ */