LCOV - code coverage report
Current view: top level - dom/media - FileBlockCache.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 25 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13