content/media/FileBlockCache.h

changeset 0
6474c204b198
     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_ */

mercurial