Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#include <limits>
#include "CacheLog.h"
#include "CacheFileIOManager.h"
#include "CacheHashUtils.h"
#include "CacheStorageService.h"
#include "CacheIndex.h"
#include "CacheFileUtils.h"
#include "nsError.h"
#include "nsThreadUtils.h"
#include "CacheFile.h"
#include "CacheObserver.h"
#include "nsIFile.h"
#include "CacheFileContextEvictor.h"
#include "nsITimer.h"
#include "nsIDirectoryEnumerator.h"
#include "nsEffectiveTLDService.h"
#include "nsIObserverService.h"
#include "nsISizeOf.h"
#include "mozilla/net/MozURL.h"
#include "mozilla/glean/NetwerkCache2Metrics.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Services.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "private/pprio.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Preferences.h"
#include "nsNetUtil.h"
#include "mozilla/glean/NetwerkMetrics.h"
#ifdef MOZ_BACKGROUNDTASKS
# include "mozilla/BackgroundTasksRunner.h"
# include "nsIBackgroundTasks.h"
#endif
// include files for ftruncate (or equivalent)
#if defined(XP_UNIX)
# include <unistd.h>
#elif defined(XP_WIN)
# include <windows.h>
# undef CreateFile
# undef CREATE_NEW
#else
// XXX add necessary include file for ftruncate (or equivalent)
#endif
namespace mozilla::net {
#define kOpenHandlesLimit 128
#define kMetadataWriteDelay 5000
#define kRemoveTrashStartDelay 60000 // in milliseconds
#define kSmartSizeUpdateInterval 60000 // in milliseconds
#ifdef ANDROID
const uint32_t kMaxCacheSizeKB = 512 * 1024; // 512 MB
#else
const uint32_t kMaxCacheSizeKB = 1024 * 1024; // 1 GB
#endif
const uint32_t kMaxClearOnShutdownCacheSizeKB = 150 * 1024; // 150 MB
const auto kPurgeExtension = ".purge.bg_rm"_ns;
bool CacheFileHandle::DispatchRelease() {
if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
return false;
}
nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
if (!ioTarget) {
return false;
}
nsresult rv = ioTarget->Dispatch(
NewNonOwningRunnableMethod("net::CacheFileHandle::Release", this,
&CacheFileHandle::Release),
nsIEventTarget::DISPATCH_NORMAL);
return NS_SUCCEEDED(rv);
}
NS_IMPL_ADDREF(CacheFileHandle)
NS_IMETHODIMP_(MozExternalRefCountType)
CacheFileHandle::Release() {
nsrefcnt count = mRefCnt - 1;
if (DispatchRelease()) {
// Redispatched to the IO thread.
return count;
}
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
LOG(("CacheFileHandle::Release() [this=%p, refcnt=%" PRIuPTR "]", this,
mRefCnt.get()));
MOZ_ASSERT(0 != mRefCnt, "dup release");
count = --mRefCnt;
NS_LOG_RELEASE(this, count, "CacheFileHandle");
if (0 == count) {
mRefCnt = 1;
delete (this);
return 0;
}
return count;
}
NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash* aHash, bool aPriority,
PinningStatus aPinning)
: mHash(aHash),
mIsDoomed(false),
mClosed(false),
mPriority(aPriority),
mSpecialFile(false),
mInvalid(false),
mFileExists(false),
mDoomWhenFoundPinned(false),
mDoomWhenFoundNonPinned(false),
mKilled(false),
mPinning(aPinning),
mFileSize(-1),
mFD(nullptr) {
// If we initialize mDoomed in the initialization list, that initialization is
// not guaranteeded to be atomic. Whereas this assignment here is guaranteed
// to be atomic. TSan will see this (atomic) assignment and be satisfied
// that cross-thread accesses to mIsDoomed are properly synchronized.
mIsDoomed = false;
LOG((
"CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]",
this, LOGSHA1(aHash)));
}
CacheFileHandle::CacheFileHandle(const nsACString& aKey, bool aPriority,
PinningStatus aPinning)
: mHash(nullptr),
mIsDoomed(false),
mClosed(false),
mPriority(aPriority),
mSpecialFile(true),
mInvalid(false),
mFileExists(false),
mDoomWhenFoundPinned(false),
mDoomWhenFoundNonPinned(false),
mKilled(false),
mPinning(aPinning),
mFileSize(-1),
mFD(nullptr),
mKey(aKey) {
// See comment above about the initialization of mIsDoomed.
mIsDoomed = false;
LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
PromiseFlatCString(aKey).get()));
}
CacheFileHandle::~CacheFileHandle() {
LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
if (!IsClosed() && ioMan) {
ioMan->CloseHandleInternal(this);
}
}
void CacheFileHandle::Log() {
nsAutoCString leafName;
if (mFile) {
mFile->GetNativeLeafName(leafName);
}
if (mSpecialFile) {
LOG(
("CacheFileHandle::Log() - special file [this=%p, "
"isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
"pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64
", leafName=%s, key=%s]",
this, bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
static_cast<uint32_t>(mPinning), bool(mFileExists), int64_t(mFileSize),
leafName.get(), mKey.get()));
} else {
LOG(
("CacheFileHandle::Log() - entry file [this=%p, "
"hash=%08x%08x%08x%08x%08x, "
"isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
"pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64
", leafName=%s, key=%s]",
this, LOGSHA1(mHash), bool(mIsDoomed), bool(mPriority), bool(mClosed),
bool(mInvalid), static_cast<uint32_t>(mPinning), bool(mFileExists),
int64_t(mFileSize), leafName.get(), mKey.get()));
}
}
uint32_t CacheFileHandle::FileSizeInK() const {
MOZ_ASSERT(mFileSize != -1);
uint64_t size64 = mFileSize;
size64 += 0x3FF;
size64 >>= 10;
uint32_t size;
if (size64 >> 32) {
NS_WARNING(
"CacheFileHandle::FileSizeInK() - FileSize is too large, "
"truncating to PR_UINT32_MAX");
size = PR_UINT32_MAX;
} else {
size = static_cast<uint32_t>(size64);
}
return size;
}
bool CacheFileHandle::SetPinned(bool aPinned) {
LOG(("CacheFileHandle::SetPinned [this=%p, pinned=%d]", this, aPinned));
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
mPinning = aPinned ? PinningStatus::PINNED : PinningStatus::NON_PINNED;
if ((MOZ_UNLIKELY(mDoomWhenFoundPinned) && aPinned) ||
(MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && !aPinned)) {
LOG((" dooming, when: pinned=%d, non-pinned=%d, found: pinned=%d",
bool(mDoomWhenFoundPinned), bool(mDoomWhenFoundNonPinned), aPinned));
mDoomWhenFoundPinned = false;
mDoomWhenFoundNonPinned = false;
return false;
}
return true;
}
// Memory reporting
size_t CacheFileHandle::SizeOfExcludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
size_t n = 0;
nsCOMPtr<nsISizeOf> sizeOf;
sizeOf = do_QueryInterface(mFile);
if (sizeOf) {
n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
}
n += mallocSizeOf(mFD);
n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
return n;
}
size_t CacheFileHandle::SizeOfIncludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
}
/******************************************************************************
* CacheFileHandles::HandleHashKey
*****************************************************************************/
void CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
mHandles.InsertElementAt(0, aHandle);
}
void CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
DebugOnly<bool> found{};
found = mHandles.RemoveElement(aHandle);
MOZ_ASSERT(found);
}
already_AddRefed<CacheFileHandle>
CacheFileHandles::HandleHashKey::GetNewestHandle() {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
RefPtr<CacheFileHandle> handle;
if (mHandles.Length()) {
handle = mHandles[0];
}
return handle.forget();
}
void CacheFileHandles::HandleHashKey::GetHandles(
nsTArray<RefPtr<CacheFileHandle>>& aResult) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
CacheFileHandle* handle = mHandles[i];
aResult.AppendElement(handle);
}
}
#ifdef DEBUG
void CacheFileHandles::HandleHashKey::AssertHandlesState() {
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
CacheFileHandle* handle = mHandles[i];
MOZ_ASSERT(handle->IsDoomed());
}
}
#endif
size_t CacheFileHandles::HandleHashKey::SizeOfExcludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
size_t n = 0;
n += mallocSizeOf(mHash.get());
for (uint32_t i = 0; i < mHandles.Length(); ++i) {
n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
}
return n;
}
/******************************************************************************
* CacheFileHandles
*****************************************************************************/
CacheFileHandles::CacheFileHandles() {
LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
MOZ_COUNT_CTOR(CacheFileHandles);
}
CacheFileHandles::~CacheFileHandles() {
LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
MOZ_COUNT_DTOR(CacheFileHandles);
}
nsresult CacheFileHandles::GetHandle(const SHA1Sum::Hash* aHash,
CacheFileHandle** _retval) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
MOZ_ASSERT(aHash);
#ifdef DEBUG_HANDLES
LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aHash)));
#endif
// find hash entry for key
HandleHashKey* entry = mTable.GetEntry(*aHash);
if (!entry) {
LOG(
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"no handle entries found",
LOGSHA1(aHash)));
return NS_ERROR_NOT_AVAILABLE;
}
#ifdef DEBUG_HANDLES
Log(entry);
#endif
// Check if the entry is doomed
RefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
if (!handle) {
LOG(
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"no handle found %p, entry %p",
LOGSHA1(aHash), handle.get(), entry));
return NS_ERROR_NOT_AVAILABLE;
}
if (handle->IsDoomed()) {
LOG(
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"found doomed handle %p, entry %p",
LOGSHA1(aHash), handle.get(), entry));
return NS_ERROR_NOT_AVAILABLE;
}
LOG(
("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
"found handle %p, entry %p",
LOGSHA1(aHash), handle.get(), entry));
handle.forget(_retval);
return NS_OK;
}
already_AddRefed<CacheFileHandle> CacheFileHandles::NewHandle(
const SHA1Sum::Hash* aHash, bool aPriority,
CacheFileHandle::PinningStatus aPinning) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
MOZ_ASSERT(aHash);
#ifdef DEBUG_HANDLES
LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aHash)));
#endif
// find hash entry for key
HandleHashKey* entry = mTable.PutEntry(*aHash);
#ifdef DEBUG_HANDLES
Log(entry);
#endif
#ifdef DEBUG
entry->AssertHandlesState();
#endif
RefPtr<CacheFileHandle> handle =
new CacheFileHandle(entry->Hash(), aPriority, aPinning);
entry->AddHandle(handle);
LOG(
("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
"created new handle %p, entry=%p",
LOGSHA1(aHash), handle.get(), entry));
return handle.forget();
}
void CacheFileHandles::RemoveHandle(CacheFileHandle* aHandle) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
MOZ_ASSERT(aHandle);
if (!aHandle) {
return;
}
#ifdef DEBUG_HANDLES
LOG((
"CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]",
aHandle, LOGSHA1(aHandle->Hash())));
#endif
// find hash entry for key
HandleHashKey* entry = mTable.GetEntry(*aHandle->Hash());
if (!entry) {
MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
"Should find entry when removing a handle before shutdown");
LOG(
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
"no entries found",
LOGSHA1(aHandle->Hash())));
return;
}
#ifdef DEBUG_HANDLES
Log(entry);
#endif
LOG(
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
"removing handle %p",
LOGSHA1(entry->Hash()), aHandle));
entry->RemoveHandle(aHandle);
if (entry->IsEmpty()) {
LOG(
("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
"list is empty, removing entry %p",
LOGSHA1(entry->Hash()), entry));
mTable.RemoveEntry(entry);
}
}
void CacheFileHandles::GetAllHandles(
nsTArray<RefPtr<CacheFileHandle>>* _retval) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
iter.Get()->GetHandles(*_retval);
}
}
void CacheFileHandles::GetActiveHandles(
nsTArray<RefPtr<CacheFileHandle>>* _retval) {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
RefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle();
MOZ_ASSERT(handle);
if (!handle->IsDoomed()) {
_retval->AppendElement(handle);
}
}
}
void CacheFileHandles::ClearAll() {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
mTable.Clear();
}
uint32_t CacheFileHandles::HandleCount() { return mTable.Count(); }
#ifdef DEBUG_HANDLES
void CacheFileHandles::Log(CacheFileHandlesEntry* entry) {
LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
nsTArray<RefPtr<CacheFileHandle>> array;
aEntry->GetHandles(array);
for (uint32_t i = 0; i < array.Length(); ++i) {
CacheFileHandle* handle = array[i];
handle->Log();
}
LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
}
#endif
// Memory reporting
size_t CacheFileHandles::SizeOfExcludingThis(
mozilla::MallocSizeOf mallocSizeOf) const {
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
return mTable.SizeOfExcludingThis(mallocSizeOf);
}
// Events
class ShutdownEvent : public Runnable, nsITimerCallback {
NS_DECL_ISUPPORTS_INHERITED
public:
ShutdownEvent() : Runnable("net::ShutdownEvent") {}
protected:
~ShutdownEvent() = default;
public:
NS_IMETHOD Run() override {
CacheFileIOManager::gInstance->ShutdownInternal();
mNotified = true;
NS_DispatchToMainThread(
NS_NewRunnableFunction("CacheFileIOManager::ShutdownEvent::Run", []() {
// This empty runnable is dispatched just in case the MT event loop
// becomes empty - we need to process a task to break out of
// SpinEventLoopUntil.
}));
return NS_OK;
}
NS_IMETHOD Notify(nsITimer* timer) override {
if (mNotified) {
return NS_OK;
}
// If there is any IO blocking on the IO thread, this will
// try to cancel it.
CacheFileIOManager::gInstance->mIOThread->CancelBlockingIO();
// After this runs the first time, the browser_cache_max_shutdown_io_lag
// time has elapsed. The CacheIO thread may pick up more blocking IO tasks
// so we want to block those too if necessary.
mTimer->SetDelay(
StaticPrefs::browser_cache_shutdown_io_time_between_cancellations_ms());
return NS_OK;
}
void PostAndWait() {
nsresult rv = CacheFileIOManager::gInstance->mIOThread->Dispatch(
this,
CacheIOThread::WRITE); // When writes and closing of handles is done
MOZ_ASSERT(NS_SUCCEEDED(rv));
// If we failed to post the even there's no reason to go into the loop
// because mNotified will never be set to true.
if (NS_FAILED(rv)) {
NS_WARNING("Posting ShutdownEvent task failed");
return;
}
rv = NS_NewTimerWithCallback(
getter_AddRefs(mTimer), this,
StaticPrefs::browser_cache_max_shutdown_io_lag() * 1000,
nsITimer::TYPE_REPEATING_SLACK);
MOZ_ASSERT(NS_SUCCEEDED(rv));
mozilla::SpinEventLoopUntil("CacheFileIOManager::ShutdownEvent"_ns,
[&]() { return bool(mNotified); });
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
}
protected:
Atomic<bool> mNotified{false};
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ISUPPORTS_INHERITED(ShutdownEvent, Runnable, nsITimerCallback)
// Class responsible for reporting IO performance stats
class IOPerfReportEvent {
public:
explicit IOPerfReportEvent(CacheFileUtils::CachePerfStats::EDataType aType)
: mType(aType), mEventCounter(0) {}
void Start(CacheIOThread* aIOThread) {
mStartTime = TimeStamp::Now();
mEventCounter = aIOThread->EventCounter();
}
void Report(CacheIOThread* aIOThread) {
if (mStartTime.IsNull()) {
return;
}
// Single IO operations can take less than 1ms. So we use microseconds to
// keep a good resolution of data.
uint32_t duration = (TimeStamp::Now() - mStartTime).ToMicroseconds();
// This is a simple prefiltering of values that might differ a lot from the
// average value. Do not add the value to the filtered stats when the event
// had to wait in a long queue.
uint32_t eventCounter = aIOThread->EventCounter();
bool shortOnly = eventCounter - mEventCounter >= 5;
CacheFileUtils::CachePerfStats::AddValue(mType, duration, shortOnly);
}
protected:
CacheFileUtils::CachePerfStats::EDataType mType;
TimeStamp mStartTime;
uint32_t mEventCounter;
};
class OpenFileEvent : public Runnable, public IOPerfReportEvent {
public:
OpenFileEvent(const nsACString& aKey, uint32_t aFlags,
CacheFileIOListener* aCallback)
: Runnable("net::OpenFileEvent"),
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_OPEN),
mFlags(aFlags),
mCallback(aCallback),
mKey(aKey) {
mIOMan = CacheFileIOManager::gInstance;
if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
Start(mIOMan->mIOThread);
}
}
protected:
~OpenFileEvent() = default;
public:
NS_IMETHOD Run() override {
nsresult rv = NS_OK;
if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
SHA1Sum sum;
sum.update(mKey.BeginReading(), mKey.Length());
sum.finish(mHash);
}
if (!mIOMan) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
rv = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
getter_AddRefs(mHandle));
} else {
rv = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
getter_AddRefs(mHandle));
if (NS_SUCCEEDED(rv)) {
Report(mIOMan->mIOThread);
}
}
mIOMan = nullptr;
if (mHandle) {
if (mHandle->Key().IsEmpty()) {
mHandle->Key() = mKey;
}
}
}
mCallback->OnFileOpened(mHandle, rv);
return NS_OK;
}
protected:
SHA1Sum::Hash mHash{};
uint32_t mFlags;
nsCOMPtr<CacheFileIOListener> mCallback;
RefPtr<CacheFileIOManager> mIOMan;
RefPtr<CacheFileHandle> mHandle;
nsCString mKey;
};
class ReadEvent : public Runnable, public IOPerfReportEvent {
public:
ReadEvent(CacheFileHandle* aHandle, int64_t aOffset, char* aBuf,
int32_t aCount, CacheFileIOListener* aCallback)
: Runnable("net::ReadEvent"),
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_READ),
mHandle(aHandle),
mOffset(aOffset),
mBuf(aBuf),
mCount(aCount),
mCallback(aCallback) {
if (!mHandle->IsSpecialFile()) {
Start(CacheFileIOManager::gInstance->mIOThread);
}
}
protected:
~ReadEvent() = default;
public:
NS_IMETHOD Run() override {
nsresult rv;
if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
rv = CacheFileIOManager::gInstance->ReadInternal(mHandle, mOffset, mBuf,
mCount);
if (NS_SUCCEEDED(rv)) {
Report(CacheFileIOManager::gInstance->mIOThread);
}
}
mCallback->OnDataRead(mHandle, mBuf, rv);
return NS_OK;
}
protected:
RefPtr<CacheFileHandle> mHandle;
int64_t mOffset;
char* mBuf;
int32_t mCount;
nsCOMPtr<CacheFileIOListener> mCallback;
};
class WriteEvent : public Runnable, public IOPerfReportEvent {
public:
WriteEvent(CacheFileHandle* aHandle, int64_t aOffset, const char* aBuf,
int32_t aCount, bool aValidate, bool aTruncate,
CacheFileIOListener* aCallback)
: Runnable("net::WriteEvent"),
IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_WRITE),
mHandle(aHandle),
mOffset(aOffset),
mBuf(aBuf),
mCount(aCount),
mValidate(aValidate),
mTruncate(aTruncate),
mCallback(aCallback) {
if (!mHandle->IsSpecialFile()) {
Start(CacheFileIOManager::gInstance->mIOThread);
}
}
protected:
~WriteEvent() {
if (!mCallback && mBuf) {
free(const_cast<char*>(mBuf));
}
}
public:
NS_IMETHOD Run() override {
nsresult rv;
if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
// We usually get here only after the internal shutdown
// (i.e. mShuttingDown == true). Pretend write has succeeded
// to avoid any past-shutdown file dooming.
rv = (CacheObserver::IsPastShutdownIOLag() ||
CacheFileIOManager::gInstance->mShuttingDown)
? NS_OK
: NS_ERROR_NOT_INITIALIZED;
} else {
rv = CacheFileIOManager::gInstance->WriteInternal(
mHandle, mOffset, mBuf, mCount, mValidate, mTruncate);
if (NS_SUCCEEDED(rv)) {
Report(CacheFileIOManager::gInstance->mIOThread);
}
if (NS_FAILED(rv) && !mCallback) {
// No listener is going to handle the error, doom the file
CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
}
}
if (mCallback) {
mCallback->OnDataWritten(mHandle, mBuf, rv);
} else {
free(const_cast<char*>(mBuf));
mBuf = nullptr;
}
return NS_OK;
}
protected:
RefPtr<CacheFileHandle> mHandle;
int64_t mOffset;
const char* mBuf;
int32_t mCount;
bool mValidate : 1;
bool mTruncate : 1;
nsCOMPtr<CacheFileIOListener> mCallback;
};
class DoomFileEvent : public Runnable {
public:
DoomFileEvent(CacheFileHandle* aHandle, CacheFileIOListener* aCallback)
: Runnable("net::DoomFileEvent"),
mCallback(aCallback),
mHandle(aHandle) {}
protected:
~DoomFileEvent() = default;
public:
NS_IMETHOD Run() override {
nsresult rv;
if (mHandle->IsClosed()) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
rv = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
}
if (mCallback) {
mCallback->OnFileDoomed(mHandle, rv);
}
return NS_OK;
}
protected:
nsCOMPtr<CacheFileIOListener> mCallback;
nsCOMPtr<nsIEventTarget> mTarget;
RefPtr<CacheFileHandle> mHandle;
};
class DoomFileByKeyEvent : public Runnable {
public:
DoomFileByKeyEvent(const nsACString& aKey, CacheFileIOListener* aCallback)
: Runnable("net::DoomFileByKeyEvent"), mCallback(aCallback) {
SHA1Sum sum;
sum.update(aKey.BeginReading(), aKey.Length());
sum.finish(mHash);
mIOMan = CacheFileIOManager::gInstance;
}
protected:
~DoomFileByKeyEvent() = default;
public:
NS_IMETHOD Run() override {
nsresult rv;
if (!mIOMan) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
rv = mIOMan->DoomFileByKeyInternal(&mHash);
mIOMan = nullptr;
}
if (mCallback) {
mCallback->OnFileDoomed(nullptr, rv);