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