content/media/FileBlockCache.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial