--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -1,39 +1,84 @@
/* -*- 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/. */
+#include "FileBlockCache.h"
#include "mozilla/SharedThreadPool.h"
-#include "FileBlockCache.h"
#include "VideoUtils.h"
#include "prio.h"
#include <algorithm>
+#include "nsAnonymousTemporaryFile.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsXULAppAPI.h"
namespace mozilla {
-nsresult FileBlockCache::Open(PRFileDesc* aFD)
+LazyLogModule gFileBlockCacheLog("FileBlockCache");
+#define FBC_LOG(type, msg) MOZ_LOG(gFileBlockCacheLog, type, msg)
+
+void
+FileBlockCache::SetCacheFile(PRFileDesc* aFD)
{
- NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- NS_ENSURE_TRUE(aFD != nullptr, NS_ERROR_FAILURE);
- {
- MonitorAutoLock mon(mFileMonitor);
- mFD = aFD;
+ MOZ_ASSERT(NS_IsMainThread());
+ FBC_LOG(LogLevel::Debug,
+ ("FileBlockCache::SetFD(aFD=%p) mIsOpen=%d", aFD, mIsOpen));
+
+ if (!aFD) {
+ // Failed to get a temporary file. Shutdown.
+ mInitPromise->Reject(NS_ERROR_FAILURE, __func__);
+ Close();
+ return;
}
{
- MonitorAutoLock mon(mDataMonitor);
- nsresult res = NS_NewNamedThread("FileBlockCache",
- getter_AddRefs(mThread),
- nullptr,
- SharedThreadPool::kStackSize);
- mIsOpen = NS_SUCCEEDED(res);
- return res;
+ MonitorAutoLock lock(mFileMonitor);
+ mFD = aFD;
+ }
+ mInitPromise->Resolve(true, __func__);
+}
+
+nsresult
+FileBlockCache::Init()
+{
+ FBC_LOG(LogLevel::Debug, ("FileBlockCache::Init()"));
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock mon(mDataMonitor);
+ nsresult rv = NS_NewNamedThread("FileBlockCache",
+ getter_AddRefs(mThread),
+ nullptr,
+ SharedThreadPool::kStackSize);
+ if (NS_FAILED(rv)) {
+ return rv;
}
+ mAbstractThread = AbstractThread::CreateXPCOMThreadWrapper(mThread, false);
+ mIsOpen = true;
+
+ mInitPromise = new GenericPromise::Private(__func__);
+ if (XRE_IsParentProcess()) {
+ rv = NS_OpenAnonymousTemporaryFile(&mFD);
+ if (NS_SUCCEEDED(rv)) {
+ mInitPromise->Resolve(true, __func__);
+ }
+ } else {
+ // We must request a temporary file descriptor from the parent process.
+ RefPtr<FileBlockCache> self = this;
+ rv = dom::ContentChild::GetSingleton()->AsyncOpenAnonymousTemporaryFile(
+ [self](PRFileDesc* aFD) { self->SetCacheFile(aFD); });
+ }
+
+ if (NS_FAILED(rv)) {
+ Close();
+ }
+
+ return rv;
}
FileBlockCache::FileBlockCache()
: mFileMonitor("MediaCache.Writer.IO.Monitor"),
mFD(nullptr),
mFDCurrentPos(0),
mDataMonitor("MediaCache.Writer.Data.Monitor"),
mIsWriteScheduled(false),
@@ -54,34 +99,38 @@ FileBlockCache::~FileBlockCache()
if (prrc != PR_SUCCESS) {
NS_WARNING("PR_Close() failed.");
}
mFD = nullptr;
}
}
}
-
void FileBlockCache::Close()
{
+ FBC_LOG(LogLevel::Debug, ("FileBlockCache::Close"));
+
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- MonitorAutoLock mon(mDataMonitor);
-
- mIsOpen = false;
- if (mThread) {
- // We must shut down the thread in another runnable. This is called
- // while we're shutting down the media cache, and nsIThread::Shutdown()
- // can cause events to run before it completes, which could end up
- // opening more streams, while the media cache is shutting down and
- // releasing memory etc! Also note we close mFD in the destructor so
- // as to not disturb any IO that's currently running.
- nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mThread);
- SystemGroup::Dispatch("ShutdownThreadEvent", TaskCategory::Other, event.forget());
+ MonitorAutoLock mon(mDataMonitor);
+ mIsOpen = false;
+ if (!mThread) {
+ return;
}
+ mAbstractThread = nullptr;
+ // We must shut down the thread in another runnable. This is called
+ // while we're shutting down the media cache, and nsIThread::Shutdown()
+ // can cause events to run before it completes, which could end up
+ // opening more streams, while the media cache is shutting down and
+ // releasing memory etc! Also note we close mFD in the destructor so
+ // as to not disturb any IO that's currently running.
+ nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mThread);
+ SystemGroup::Dispatch(
+ "ShutdownThreadEvent", TaskCategory::Other, event.forget());
+ mThread = nullptr;
}
template<typename Container, typename Value>
bool
ContainerContains(const Container& aContainer, const Value& value)
{
return std::find(aContainer.begin(), aContainer.end(), value)
!= aContainer.end();
@@ -113,60 +162,79 @@ nsresult FileBlockCache::WriteBlock(uint
EnsureWriteScheduled();
return NS_OK;
}
void FileBlockCache::EnsureWriteScheduled()
{
mDataMonitor.AssertCurrentThreadOwns();
+ MOZ_ASSERT(mIsOpen);
- if (!mIsWriteScheduled) {
- mThread->Dispatch(this, NS_DISPATCH_NORMAL);
- mIsWriteScheduled = true;
+ if (mIsWriteScheduled) {
+ return;
}
+ mIsWriteScheduled = true;
+
+ RefPtr<FileBlockCache> self = this;
+ mInitPromise->Then(mAbstractThread,
+ __func__,
+ [self](bool aValue) { self->Run(); },
+ [self](nsresult rv) {}
+ // Failure handled by EnsureInitialized.
+ );
}
nsresult FileBlockCache::Seek(int64_t aOffset)
{
mFileMonitor.AssertCurrentThreadOwns();
if (mFDCurrentPos != aOffset) {
+ MOZ_ASSERT(mFD);
int64_t result = PR_Seek64(mFD, aOffset, PR_SEEK_SET);
if (result != aOffset) {
NS_WARNING("Failed to seek media cache file");
return NS_ERROR_FAILURE;
}
mFDCurrentPos = result;
}
return NS_OK;
}
nsresult FileBlockCache::ReadFromFile(int64_t aOffset,
uint8_t* aDest,
int32_t aBytesToRead,
int32_t& aBytesRead)
{
+ FBC_LOG(LogLevel::Debug,
+ ("FileBlockCache::ReadFromFile(offset=%" PRIu64 ", len=%u)",
+ aOffset,
+ aBytesToRead));
mFileMonitor.AssertCurrentThreadOwns();
+ MOZ_ASSERT(mFD);
nsresult res = Seek(aOffset);
if (NS_FAILED(res)) return res;
aBytesRead = PR_Read(mFD, aDest, aBytesToRead);
if (aBytesRead <= 0)
return NS_ERROR_FAILURE;
mFDCurrentPos += aBytesRead;
return NS_OK;
}
nsresult FileBlockCache::WriteBlockToFile(int32_t aBlockIndex,
const uint8_t* aBlockData)
{
+ FBC_LOG(LogLevel::Debug,
+ ("FileBlockCache::WriteBlockToFile(index=%u)", aBlockIndex));
+
mFileMonitor.AssertCurrentThreadOwns();
+ MOZ_ASSERT(mFD);
nsresult rv = Seek(BlockIndexToOffset(aBlockIndex));
if (NS_FAILED(rv)) return rv;
int32_t amount = PR_Write(mFD, aBlockData, BLOCK_SIZE);
if (amount < BLOCK_SIZE) {
NS_WARNING("Failed to write media cache block!");
return NS_ERROR_FAILURE;
@@ -174,35 +242,44 @@ nsresult FileBlockCache::WriteBlockToFil
mFDCurrentPos += BLOCK_SIZE;
return NS_OK;
}
nsresult FileBlockCache::MoveBlockInFile(int32_t aSourceBlockIndex,
int32_t aDestBlockIndex)
{
+ FBC_LOG(LogLevel::Debug,
+ ("FileBlockCache::MoveBlockInFile(src=%u, dest=%u)",
+ aSourceBlockIndex,
+ aDestBlockIndex));
+
mFileMonitor.AssertCurrentThreadOwns();
uint8_t buf[BLOCK_SIZE];
int32_t bytesRead = 0;
if (NS_FAILED(ReadFromFile(BlockIndexToOffset(aSourceBlockIndex),
buf,
BLOCK_SIZE,
bytesRead))) {
return NS_ERROR_FAILURE;
}
return WriteBlockToFile(aDestBlockIndex, buf);
}
nsresult FileBlockCache::Run()
{
+ NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
MonitorAutoLock mon(mDataMonitor);
- NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
NS_ASSERTION(!mChangeIndexList.empty(), "Only dispatch when there's work to do");
NS_ASSERTION(mIsWriteScheduled, "Should report write running or scheduled.");
+ MOZ_ASSERT(mFD);
+
+ FBC_LOG(LogLevel::Debug,
+ ("FileBlockCache::Run mFD=%p mIsOpen=%d", mFD, mIsOpen));
while (!mChangeIndexList.empty()) {
if (!mIsOpen) {
// We've been closed, abort, discarding unwritten changes.
mIsWriteScheduled = false;
return NS_ERROR_FAILURE;
}
@@ -247,17 +324,17 @@ nsresult FileBlockCache::Run()
nsresult FileBlockCache::Read(int64_t aOffset,
uint8_t* aData,
int32_t aLength,
int32_t* aBytes)
{
MonitorAutoLock mon(mDataMonitor);
- if (!mFD || (aOffset / BLOCK_SIZE) > INT32_MAX)
+ if (!mIsOpen || (aOffset / BLOCK_SIZE) > INT32_MAX)
return NS_ERROR_FAILURE;
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;
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -4,17 +4,19 @@
* 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 "mozilla/MozPromise.h"
#include "mozilla/UniquePtr.h"
+#include "mozilla/AbstractThread.h"
#include "nsTArray.h"
#include "MediaCache.h"
#include "nsDeque.h"
#include "nsThreadUtils.h"
#include <deque>
struct PRFileDesc;
@@ -57,18 +59,17 @@ public:
};
FileBlockCache();
protected:
~FileBlockCache();
public:
- // Assumes ownership of aFD.
- nsresult Open(PRFileDesc* aFD);
+ nsresult Init();
// 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.
@@ -125,16 +126,18 @@ public:
}
};
private:
int64_t BlockIndexToOffset(int32_t aBlockIndex) {
return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
}
+ void SetCacheFile(PRFileDesc* aFD);
+
// 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.
@@ -155,26 +158,35 @@ private:
// 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();
+ // Promise that tracks the request for an anonymous temporary file for the
+ // cache to store data into. The file descriptor must be requested from the
+ // parent process when the cache is initialized. While this promise is
+ // outstanding, the FileBlockCache buffers blocks in memory, and reads
+ // against the cache are serviced from the in-memory buffers.
+ RefPtr<GenericPromise::Private> mInitPromise;
+
// 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< RefPtr<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;
+ // Wrapper for mThread.
+ RefPtr<AbstractThread> mAbstractThread;
// 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 if the writer is ready to write data to file.
bool mIsOpen;
};