content/media/FileBlockCache.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:388f278225b1
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/Monitor.h"
12 #include "nsTArray.h"
13 #include "MediaCache.h"
14 #include "nsDeque.h"
15 #include "nsThreadUtils.h"
16
17 struct PRFileDesc;
18
19 namespace mozilla {
20
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 };
56
57 FileBlockCache();
58
59 ~FileBlockCache();
60
61 // Assumes ownership of aFD.
62 nsresult Open(PRFileDesc* aFD);
63
64 // Closes writer, shuts down thread.
65 void Close();
66
67 // Can be called on any thread. This defers to a non-main thread.
68 nsresult WriteBlock(uint32_t aBlockIndex, const uint8_t* aData);
69
70 // Performs block writes and block moves on its own thread.
71 NS_IMETHOD Run() MOZ_OVERRIDE;
72
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);
80
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);
84
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 {
89
90 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
91
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 }
100
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) {}
105
106 nsAutoArrayPtr<uint8_t> mData;
107 const int32_t mSourceBlockIndex;
108
109 bool IsMove() const {
110 return mSourceBlockIndex != -1;
111 }
112 bool IsWrite() const {
113 return mSourceBlockIndex == -1 &&
114 mData.get() != nullptr;
115 }
116
117 private:
118 // Private destructor, to discourage deletion outside of Release():
119 ~BlockChange()
120 {
121 }
122 };
123
124 class Int32Queue : private nsDeque {
125 public:
126 int32_t PopFront() {
127 int32_t front = ObjectAt(0);
128 nsDeque::PopFront();
129 return front;
130 }
131
132 void PushBack(int32_t aValue) {
133 nsDeque::Push(reinterpret_cast<void*>(aValue));
134 }
135
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 }
144
145 bool IsEmpty() {
146 return nsDeque::GetSize() == 0;
147 }
148
149 private:
150 int32_t ObjectAt(int32_t aIndex) {
151 void* v = nsDeque::ObjectAt(aIndex);
152 return reinterpret_cast<uintptr_t>(v);
153 }
154 };
155
156 private:
157 int64_t BlockIndexToOffset(int32_t aBlockIndex) {
158 return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
159 }
160
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;
181
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 };
210
211 } // End namespace mozilla.
212
213 #endif /* FILE_BLOCK_CACHE_H_ */

mercurial