Bug 1365538 - Postpone FileBlockCache writes while reading - r?cpearce
While inside FileBlock::Read, the mIsReading flag is set, which in turn
prevents starting the writer task, or makes it exit as soon as possible.
This should help multi-block reads (while data is being received) by giving
them priority, so that that blocking call can return faster.
At the end of Read, the writer task is re-dispatched if needed.
MozReview-Commit-ID: J32tHGFRMNU
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -91,16 +91,17 @@ FileBlockCache::Init()
}
FileBlockCache::FileBlockCache()
: mFileMutex("MediaCache.Writer.IO.Mutex"),
mFD(nullptr),
mFDCurrentPos(0),
mDataMutex("MediaCache.Writer.Data.Mutex"),
mIsWriteScheduled(false),
+ mIsReading(false),
mIsOpen(false)
{
}
FileBlockCache::~FileBlockCache()
{
NS_ASSERTION(!mIsOpen, "Should Close() FileBlockCache before destroying");
}
@@ -190,17 +191,17 @@ FileBlockCache::WriteBlock(uint32_t aBlo
return NS_OK;
}
void FileBlockCache::EnsureWriteScheduled()
{
mDataMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mIsOpen);
- if (mIsWriteScheduled) {
+ if (mIsWriteScheduled || mIsReading) {
return;
}
mIsWriteScheduled = true;
if (!mInitialized) {
// We're still waiting on a file descriptor. When it arrives,
// the write will be scheduled.
return;
}
@@ -294,16 +295,22 @@ nsresult FileBlockCache::Run()
while (!mChangeIndexList.empty()) {
if (!mIsOpen) {
// We've been closed, abort, discarding unwritten changes.
mIsWriteScheduled = false;
return NS_ERROR_FAILURE;
}
+ if (mIsReading) {
+ // We're trying to read; postpone all writes. (Reader will resume writes.)
+ mIsWriteScheduled = false;
+ return NS_OK;
+ }
+
// Process each pending change. We pop the index out of the change
// list, but leave the BlockChange in mBlockChanges until the change
// is written to file. This is so that any read which happens while
// we drop mDataMutex to write will refer to the data's source in
// memory, rather than the not-yet up to date data written to file.
// This also ensures we will insert a new index into mChangeIndexList
// when this happens.
@@ -343,16 +350,25 @@ nsresult FileBlockCache::Read(int64_t aO
int32_t aLength,
int32_t* aBytes)
{
MutexAutoLock mon(mDataMutex);
if (!mIsOpen || (aOffset / BLOCK_SIZE) > INT32_MAX)
return NS_ERROR_FAILURE;
+ mIsReading = true;
+ auto exitRead = MakeScopeExit([&] {
+ mIsReading = false;
+ if (!mChangeIndexList.empty()) {
+ // mReading has stopped or prevented pending writes, resume them.
+ EnsureWriteScheduled();
+ }
+ });
+
int32_t bytesToRead = aLength;
int64_t offset = aOffset;
uint8_t* dst = aData;
while (bytesToRead > 0) {
int32_t blockIndex = static_cast<int32_t>(offset / BLOCK_SIZE);
int32_t start = offset % BLOCK_SIZE;
int32_t amount = std::min(BLOCK_SIZE - start, bytesToRead);
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -184,16 +184,19 @@ private:
// 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.
std::deque<int32_t> mChangeIndexList;
// True if we've dispatched an event to commit all pending block changes
// to file on mThread.
bool mIsWriteScheduled;
+ // True when a read is happening. Pending writes may be postponed, to give
+ // higher priority to reads (which may be blocking the caller).
+ bool mIsReading;
// True if the writer is ready to enqueue writes.
bool mIsOpen;
// True if we've got a temporary file descriptor. Note: we don't use mFD
// directly as that's synchronized via mFileMutex and we need to make
// decisions about whether we can write while holding mDataMutex.
bool mInitialized = false;
};