Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / media / FileBlockCache.h
blob3a1daf0794946d831615dfd0f2c667cb2ba5cafd
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/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>
21 struct PRFileDesc;
23 namespace mozilla {
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.
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.
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.
41 // To improve efficiency, writes can only be done at block granularity,
42 // whereas reads can be done with byte granularity.
44 // Note it's also recommended not to read from the media cache from the main
45 // thread to prevent jank.
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 public:
57 FileBlockCache();
59 protected:
60 virtual ~FileBlockCache();
62 public:
63 // Launch thread and open temporary file.
64 nsresult Init() override;
66 // Will discard pending changes if any.
67 void Flush() override;
69 // Maximum number of blocks allowed in this block cache.
70 // Calculated from "media.cache_size" pref.
71 size_t GetMaxBlocks(size_t aCacheSizeInKB) const override;
73 // Can be called on any thread. This defers to a non-main thread.
74 nsresult WriteBlock(uint32_t aBlockIndex, Span<const uint8_t> aData1,
75 Span<const uint8_t> aData2) override;
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, uint8_t* aData, int32_t aLength,
81 int32_t* aBytes) override;
83 // Moves a block asynchronously. Can be called on any thread.
84 // This defers file I/O to a non-main thread.
85 nsresult MoveBlock(int32_t aSourceBlockIndex,
86 int32_t aDestBlockIndex) override;
88 // Represents a change yet to be made to a block in the file. The change
89 // is either a write (and the data to be written is stored in this struct)
90 // or a move (and the index of the source block is stored instead).
91 struct BlockChange final {
92 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
94 // This block is waiting in memory to be written.
95 // Stores a copy of the block, so we can write it asynchronously.
96 explicit BlockChange(const uint8_t* aData) : mSourceBlockIndex(-1) {
97 mData = MakeUnique<uint8_t[]>(BLOCK_SIZE);
98 memcpy(mData.get(), aData, BLOCK_SIZE);
101 BlockChange(Span<const uint8_t> aData1, Span<const uint8_t> aData2)
102 : mSourceBlockIndex(-1) {
103 MOZ_ASSERT(aData1.Length() + aData2.Length() == BLOCK_SIZE);
104 mData = MakeUnique<uint8_t[]>(BLOCK_SIZE);
105 memcpy(mData.get(), aData1.Elements(), aData1.Length());
106 memcpy(mData.get() + aData1.Length(), aData2.Elements(), aData2.Length());
109 // This block's contents are located in another file
110 // block, i.e. this block has been moved.
111 explicit BlockChange(int32_t aSourceBlockIndex)
112 : mSourceBlockIndex(aSourceBlockIndex) {}
114 UniquePtr<uint8_t[]> mData;
115 const int32_t mSourceBlockIndex;
117 bool IsMove() const { return mSourceBlockIndex != -1; }
118 bool IsWrite() const {
119 return mSourceBlockIndex == -1 && mData.get() != nullptr;
122 private:
123 // Private destructor, to discourage deletion outside of Release():
124 ~BlockChange() = default;
127 private:
128 int64_t BlockIndexToOffset(int32_t aBlockIndex) {
129 return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
132 void SetCacheFile(PRFileDesc* aFD);
134 // Close file in thread and terminate thread.
135 void Close();
137 // Performs block writes and block moves on its own thread.
138 void PerformBlockIOs();
140 // Mutex which controls access to mFD and mFDCurrentPos. Don't hold
141 // mDataMutex while holding mFileMutex! mFileMutex must be owned
142 // while accessing any of the following data fields or methods.
143 Mutex mFileMutex;
144 // Moves a block already committed to file.
145 nsresult MoveBlockInFile(int32_t aSourceBlockIndex, int32_t aDestBlockIndex);
146 // Seeks file pointer.
147 nsresult Seek(int64_t aOffset);
148 // Reads data from file offset.
149 nsresult ReadFromFile(int64_t aOffset, uint8_t* aDest, int32_t aBytesToRead,
150 int32_t& aBytesRead);
151 nsresult WriteBlockToFile(int32_t aBlockIndex, const uint8_t* aBlockData);
152 // File descriptor we're writing to. This is created externally, but
153 // shutdown by us.
154 PRFileDesc* mFD MOZ_PT_GUARDED_BY(mFileMutex);
155 // The current file offset in the file.
156 int64_t mFDCurrentPos MOZ_GUARDED_BY(mFileMutex);
158 // Mutex which controls access to all data in this class, except mFD
159 // and mFDCurrentPos. Don't hold mDataMutex while holding mFileMutex!
160 // mDataMutex must be owned while accessing any of the following data
161 // fields or methods.
162 Mutex mDataMutex;
163 // Ensures we either are running the event to preform IO, or an event
164 // has been dispatched to preform the IO.
165 // mDataMutex must be owned while calling this.
166 void EnsureWriteScheduled();
168 // Array of block changes to made. If mBlockChanges[offset/BLOCK_SIZE] ==
169 // nullptr, then the block has no pending changes to be written, but if
170 // mBlockChanges[offset/BLOCK_SIZE] != nullptr, then either there's a block
171 // cached in memory waiting to be written, or this block is the target of a
172 // block move.
173 nsTArray<RefPtr<BlockChange> > mBlockChanges MOZ_GUARDED_BY(mDataMutex);
174 // Event target upon which block writes and block moves are performed. This is
175 // created upon open, and dropped on close.
176 nsCOMPtr<nsISerialEventTarget> mBackgroundET MOZ_GUARDED_BY(mDataMutex);
177 // Queue of pending block indexes that need to be written or moved.
178 std::deque<int32_t> mChangeIndexList MOZ_GUARDED_BY(mDataMutex);
179 // True if we've dispatched an event to commit all pending block changes
180 // to file on mBackgroundET.
181 bool mIsWriteScheduled MOZ_GUARDED_BY(mDataMutex);
182 // True when a read is happening. Pending writes may be postponed, to give
183 // higher priority to reads (which may be blocking the caller).
184 bool mIsReading MOZ_GUARDED_BY(mDataMutex);
185 // True if we've got a temporary file descriptor. Note: we don't use mFD
186 // directly as that's synchronized via mFileMutex and we need to make
187 // decisions about whether we can write while holding mDataMutex.
188 bool mInitialized MOZ_GUARDED_BY(mDataMutex) = false;
191 } // End namespace mozilla.
193 #endif /* FILE_BLOCK_CACHE_H_ */