Bug 968106 - HTTP cache v2: implementation of the eviction of the whole disk cache, r=honzab

This commit is contained in:
Michal Novotny 2014-03-07 12:22:59 +01:00
parent 72173a705a
commit 6d22e72788
9 changed files with 589 additions and 682 deletions

View File

@ -1,96 +0,0 @@
/* 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 "CacheEntriesEnumerator.h"
#include "CacheFileIOManager.h"
#include "CacheFile.h"
#include "nsIDirectoryEnumerator.h"
#include "nsIFile.h"
#include "nsIThread.h"
#include "nsISimpleEnumerator.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace net {
CacheEntriesEnumerator::CacheEntriesEnumerator(nsIFile* aEntriesDirectory)
: mEntriesDirectory(aEntriesDirectory)
{
MOZ_COUNT_CTOR(CacheEntriesEnumerator);
}
CacheEntriesEnumerator::~CacheEntriesEnumerator()
{
MOZ_COUNT_DTOR(CacheEntriesEnumerator);
if (mEnumerator) {
mEnumerator->Close();
ProxyReleaseMainThread(mEnumerator);
}
}
nsresult CacheEntriesEnumerator::Init()
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> e;
rv = mEntriesDirectory->GetDirectoryEntries(getter_AddRefs(e));
if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
mEnumerator = do_QueryInterface(e, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
bool CacheEntriesEnumerator::HasMore()
{
#ifdef DEBUG
if (!mThreadCheck)
mThreadCheck = NS_GetCurrentThread();
else
MOZ_ASSERT(mThreadCheck == NS_GetCurrentThread());
#endif
if (!mEnumerator) {
return false;
}
if (mCurrentFile)
return true;
nsresult rv;
rv = mEnumerator->GetNextFile(getter_AddRefs(mCurrentFile));
if (NS_FAILED(rv)) {
mEnumerator->Close();
mEnumerator = nullptr;
return false;
}
return !!mCurrentFile;
}
nsresult CacheEntriesEnumerator::GetNextFile(nsIFile** aFile)
{
#ifdef DEBUG
MOZ_ASSERT(mThreadCheck == NS_GetCurrentThread());
#endif
NS_ENSURE_TRUE(mCurrentFile, NS_ERROR_UNEXPECTED);
mCurrentFile.forget(aFile);
return NS_OK;
}
} // net
} // mozilla

View File

@ -1,53 +0,0 @@
/* 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 CacheEntriesEnumerator__h__
#define CacheEntriesEnumerator__h__
#include "nsCOMPtr.h"
class nsIFile;
class nsIDirectoryEnumerator;
class nsIThread;
namespace mozilla {
namespace net {
class CacheFileIOManager;
class CacheFileListener;
class CacheFile;
class CacheEntriesEnumeratorCallback : public nsISupports
{
public:
virtual void OnFile(CacheFile* aFile) = 0;
};
class CacheEntriesEnumerator
{
public:
~CacheEntriesEnumerator();
bool HasMore();
nsresult GetNextFile(nsIFile** aFile);
protected:
friend class CacheFileIOManager;
CacheEntriesEnumerator(nsIFile* aEntriesDirectory);
nsresult Init();
private:
nsCOMPtr<nsIDirectoryEnumerator> mEnumerator;
nsCOMPtr<nsIFile> mEntriesDirectory;
nsCOMPtr<nsIFile> mCurrentFile;
#ifdef DEBUG
nsCOMPtr<nsIThread> mThreadCheck;
#endif
};
} // net
} // mozilla
#endif

View File

@ -17,9 +17,11 @@
#include "nsITimer.h"
#include "nsISimpleEnumerator.h"
#include "nsIDirectoryEnumerator.h"
#include "nsIObserverService.h"
#include "nsISizeOf.h"
#include "mozilla/Telemetry.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Services.h"
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "private/pprio.h"
@ -437,6 +439,30 @@ CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
mTable.EnumerateEntries(&GetAllHandlesEnum, _retval);
}
static PLDHashOperator
GetActiveHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
{
nsTArray<nsRefPtr<CacheFileHandle> > *array =
static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
nsRefPtr<CacheFileHandle> handle = aEntry->GetNewestHandle();
MOZ_ASSERT(handle);
if (!handle->IsDoomed()) {
array->AppendElement(handle);
}
return PL_DHASH_NEXT;
}
void
CacheFileHandles::GetActiveHandles(
nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
{
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
}
void
CacheFileHandles::ClearAll()
{
@ -1111,6 +1137,7 @@ CacheFileIOManager::~CacheFileIOManager()
MOZ_COUNT_DTOR(CacheFileIOManager);
}
// static
nsresult
CacheFileIOManager::Init()
{
@ -1147,6 +1174,7 @@ CacheFileIOManager::InitInternal()
return NS_OK;
}
// static
nsresult
CacheFileIOManager::Shutdown()
{
@ -1251,6 +1279,7 @@ CacheFileIOManager::ShutdownInternal()
return NS_OK;
}
// static
nsresult
CacheFileIOManager::OnProfile()
{
@ -1487,6 +1516,7 @@ CacheFileIOManager::Notify(nsITimer * aTimer)
return NS_OK;
}
// static
nsresult
CacheFileIOManager::OpenFile(const nsACString &aKey,
uint32_t aFlags,
@ -1733,6 +1763,7 @@ CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
return NS_OK;
}
// static
nsresult
CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
char *aBuf, int32_t aCount,
@ -1798,6 +1829,7 @@ CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
return NS_OK;
}
// static
nsresult
CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
const char *aBuf, int32_t aCount, bool aValidate,
@ -1881,6 +1913,7 @@ CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
return NS_OK;
}
// static
nsresult
CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
CacheFileIOListener *aCallback)
@ -1968,6 +2001,7 @@ CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle)
return NS_OK;
}
// static
nsresult
CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
CacheFileIOListener *aCallback)
@ -2048,6 +2082,7 @@ CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
return NS_OK;
}
// static
nsresult
CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
{
@ -2085,6 +2120,7 @@ CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle)
return NS_OK;
}
// static
nsresult
CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
int64_t aTruncatePos, int64_t aEOFPos,
@ -2186,6 +2222,7 @@ CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
return NS_OK;
}
// static
nsresult
CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
const nsACString &aNewName,
@ -2274,6 +2311,7 @@ CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
return NS_OK;
}
// static
nsresult
CacheFileIOManager::EvictIfOverLimit()
{
@ -2439,6 +2477,122 @@ CacheFileIOManager::OverLimitEvictionInternal()
return NS_OK;
}
// static
nsresult
CacheFileIOManager::EvictAll()
{
LOG(("CacheFileIOManager::EvictAll()"));
nsresult rv;
nsRefPtr<CacheFileIOManager> ioMan = gInstance;
if (!ioMan) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIRunnable> ev;
ev = NS_NewRunnableMethod(ioMan, &CacheFileIOManager::EvictAllInternal);
rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::OPEN_PRIORITY);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
namespace {
class EvictionNotifierRunnable : public nsRunnable
{
public:
NS_DECL_NSIRUNNABLE
};
NS_IMETHODIMP
EvictionNotifierRunnable::Run()
{
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc) {
obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
}
return NS_OK;
}
} // anonymous namespace
nsresult
CacheFileIOManager::EvictAllInternal()
{
LOG(("CacheFileIOManager::EvictAllInternal()"));
nsresult rv;
MOZ_ASSERT(mIOThread->IsCurrentThread());
nsRefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
if (!mCacheDirectory) {
// This is a kind of hack. Somebody called EvictAll() without a profile.
// This happens in xpcshell tests that use cache without profile. We need
// to notify observers in this case since the tests are waiting for it.
NS_DispatchToMainThread(r);
return NS_ERROR_FILE_INVALID_PATH;
}
if (mShuttingDown) {
return NS_ERROR_NOT_INITIALIZED;
}
if (!mTreeCreated) {
rv = CreateCacheTree();
if (NS_FAILED(rv)) {
return rv;
}
}
// Doom all active handles
nsTArray<nsRefPtr<CacheFileHandle> > handles;
mHandles.GetActiveHandles(&handles);
for (uint32_t i = 0; i < handles.Length(); ++i) {
rv = DoomFileInternal(handles[i]);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsCOMPtr<nsIFile> file;
rv = mCacheDirectory->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Trash current entries directory
rv = TrashDirectory(file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Files are now inaccessible in entries directory, notify observers.
NS_DispatchToMainThread(r);
// Create a new empty entries directory
rv = CheckAndCreateDir(mCacheDirectory, kEntriesDir, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CacheIndex::RemoveAll();
return NS_OK;
}
nsresult
CacheFileIOManager::TrashDirectory(nsIFile *aFile)
{
@ -2510,6 +2664,7 @@ CacheFileIOManager::TrashDirectory(nsIFile *aFile)
return NS_OK;
}
// static
void
CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
{
@ -2755,6 +2910,7 @@ CacheFileIOManager::FindTrashDirToRemove()
return NS_ERROR_NOT_AVAILABLE;
}
// static
nsresult
CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
uint32_t aAppId,
@ -2783,6 +2939,7 @@ CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
return NS_OK;
}
// static
nsresult
CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
const uint32_t *aFrecency,
@ -2812,54 +2969,6 @@ CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
return NS_OK;
}
nsresult
CacheFileIOManager::EnumerateEntryFiles(EEnumerateMode aMode,
CacheEntriesEnumerator** aEnumerator)
{
LOG(("CacheFileIOManager::EnumerateEntryFiles(%d)", aMode));
nsresult rv;
nsRefPtr<CacheFileIOManager> ioMan = gInstance;
if (!ioMan) {
return NS_ERROR_NOT_INITIALIZED;
}
if (!ioMan->mCacheDirectory) {
return NS_ERROR_FILE_NOT_FOUND;
}
nsCOMPtr<nsIFile> file;
rv = ioMan->mCacheDirectory->Clone(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
switch (aMode) {
case ENTRIES:
rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
NS_ENSURE_SUCCESS(rv, rv);
break;
case DOOMED:
rv = file->AppendNative(NS_LITERAL_CSTRING(kDoomedDir));
NS_ENSURE_SUCCESS(rv, rv);
break;
default:
return NS_ERROR_INVALID_ARG;
}
nsAutoPtr<CacheEntriesEnumerator> enumerator(
new CacheEntriesEnumerator(file));
rv = enumerator->Init();
NS_ENSURE_SUCCESS(rv, rv);
*aEnumerator = enumerator.forget();
return NS_OK;
}
nsresult
CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
{
@ -2889,6 +2998,7 @@ CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
return NS_OK;
}
// static
void
CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
{
@ -2901,6 +3011,7 @@ CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
}
}
// static
nsresult
CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
{

View File

@ -6,7 +6,6 @@
#define CacheFileIOManager__h__
#include "CacheIOThread.h"
#include "CacheEntriesEnumerator.h"
#include "nsIEventTarget.h"
#include "nsITimer.h"
#include "nsCOMPtr.h"
@ -26,6 +25,7 @@ class nsIDirectoryEnumerator;
namespace mozilla {
namespace net {
class CacheFile;
#ifdef DEBUG_HANDLES
class CacheFileHandlesEntry;
#endif
@ -90,6 +90,7 @@ public:
nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval);
void RemoveHandle(CacheFileHandle *aHandlle);
void GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
void GetActiveHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
void ClearAll();
uint32_t HandleCount();
@ -255,6 +256,7 @@ public:
const nsACString &aNewName,
CacheFileIOListener *aCallback);
static nsresult EvictIfOverLimit();
static nsresult EvictAll();
static nsresult InitIndexEntry(CacheFileHandle *aHandle,
uint32_t aAppId,
@ -271,9 +273,6 @@ public:
DOOMED
};
static nsresult EnumerateEntryFiles(EEnumerateMode aMode,
CacheEntriesEnumerator** aEnumerator);
static void GetCacheDirectory(nsIFile** result);
// Memory reporting
@ -322,6 +321,7 @@ private:
const nsACString &aNewName);
nsresult EvictIfOverLimitInternal();
nsresult OverLimitEvictionInternal();
nsresult EvictAllInternal();
nsresult TrashDirectory(nsIFile *aFile);
static void OnTrashTimer(nsITimer *aTimer, void *aClosure);

View File

@ -34,7 +34,7 @@ public:
WRITE,
MANAGEMENT,
CLOSE,
BUILD_OR_UPDATE_INDEX,
INDEX,
EVICT,
LAST_LEVEL,

View File

@ -16,6 +16,7 @@
#include "prinrval.h"
#include "nsIFile.h"
#include "nsITimer.h"
#include "mozilla/AutoRestore.h"
#include <algorithm>
@ -221,6 +222,72 @@ private:
bool mLocked;
};
class FileOpenHelper : public CacheFileIOListener
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
FileOpenHelper(CacheIndex* aIndex)
: mIndex(aIndex)
, mCanceled(false)
{}
virtual ~FileOpenHelper() {}
void Cancel() {
mIndex->AssertOwnsLock();
mCanceled = true;
}
private:
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
nsresult aResult) {
MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf,
nsresult aResult) {
MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) {
MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) {
MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) {
MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!");
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<CacheIndex> mIndex;
bool mCanceled;
};
NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle,
nsresult aResult)
{
CacheIndexAutoLock lock(mIndex);
if (mCanceled) {
if (aHandle) {
CacheFileIOManager::DoomFile(aHandle, nullptr);
}
return NS_OK;
}
mIndex->OnFileOpenedInternal(this, aHandle, aResult);
return NS_OK;
}
NS_IMPL_ISUPPORTS1(FileOpenHelper, CacheFileIOListener);
CacheIndex * CacheIndex::gInstance = nullptr;
@ -239,16 +306,16 @@ CacheIndex::CacheIndex()
, mState(INITIAL)
, mShuttingDown(false)
, mIndexNeedsUpdate(false)
, mRemovingAll(false)
, mIndexOnDiskIsValid(false)
, mDontMarkIndexClean(false)
, mIndexTimeStamp(0)
, mUpdateEventPending(false)
, mSkipEntries(0)
, mProcessEntries(0)
, mRWBuf(nullptr)
, mRWBufSize(0)
, mRWBufPos(0)
, mReadOpenCount(0)
, mReadFailed(false)
, mJournalReadSuccessfully(false)
{
LOG(("CacheIndex::CacheIndex [this=%p]", this));
@ -300,6 +367,8 @@ CacheIndex::Init(nsIFile *aCacheDirectory)
nsRefPtr<CacheIndex> idx = new CacheIndex();
CacheIndexAutoLock lock(idx);
nsresult rv = idx->InitInternal(aCacheDirectory);
NS_ENSURE_SUCCESS(rv, rv);
@ -315,20 +384,9 @@ CacheIndex::InitInternal(nsIFile *aCacheDirectory)
rv = aCacheDirectory->Clone(getter_AddRefs(mCacheDirectory));
NS_ENSURE_SUCCESS(rv, rv);
ChangeState(READING);
mStartTime = TimeStamp::NowLoRes();
// dispatch an event since IO manager's path is not initialized yet
nsCOMPtr<nsIRunnable> event;
event = NS_NewRunnableMethod(this, &CacheIndex::ReadIndexFromDisk);
rv = NS_DispatchToCurrentThread(event);
if (NS_FAILED(rv)) {
ChangeState(INITIAL);
LOG(("CacheIndex::InitInternal() - Cannot dispatch event"));
NS_ENSURE_SUCCESS(rv, rv);
}
ReadIndexFromDisk();
return NS_OK;
}
@ -390,8 +448,8 @@ CacheIndex::PreShutdownInternal()
MOZ_ASSERT(mShuttingDown);
if (mTimer) {
mTimer = nullptr;
if (mUpdateTimer) {
mUpdateTimer = nullptr;
}
switch (mState) {
@ -405,8 +463,6 @@ CacheIndex::PreShutdownInternal()
FinishRead(false);
break;
case BUILDING:
FinishBuild(false);
break;
case UPDATING:
FinishUpdate(false);
break;
@ -466,8 +522,6 @@ CacheIndex::Shutdown()
index->FinishRead(false);
break;
case BUILDING:
index->FinishBuild(false);
break;
case UPDATING:
index->FinishUpdate(false);
break;
@ -978,6 +1032,90 @@ CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
return NS_OK;
}
// static
nsresult
CacheIndex::RemoveAll()
{
LOG(("CacheIndex::RemoveAll()"));
nsRefPtr<CacheIndex> index = gInstance;
if (!index) {
return NS_ERROR_NOT_INITIALIZED;
}
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
nsCOMPtr<nsIFile> file;
{
CacheIndexAutoLock lock(index);
MOZ_ASSERT(!index->mRemovingAll);
if (!index->IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
}
AutoRestore<bool> saveRemovingAll(index->mRemovingAll);
index->mRemovingAll = true;
// Doom index and journal handles but don't null them out since this will be
// done in FinishWrite/FinishRead methods.
if (index->mIndexHandle) {
CacheFileIOManager::DoomFile(index->mIndexHandle, nullptr);
} else {
// We don't have a handle to index file, so get the file here, but delete
// it outside the lock. Ignore the result since this is not fatal.
index->GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(file));
}
if (index->mJournalHandle) {
CacheFileIOManager::DoomFile(index->mJournalHandle, nullptr);
}
switch (index->mState) {
case WRITING:
index->FinishWrite(false);
break;
case READY:
// nothing to do
break;
case READING:
index->FinishRead(false);
break;
case BUILDING:
case UPDATING:
index->FinishUpdate(false);
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
}
// We should end up in READY state
MOZ_ASSERT(index->mState == READY);
// There should not be any handle
MOZ_ASSERT(!index->mIndexHandle);
MOZ_ASSERT(!index->mJournalHandle);
index->mIndexOnDiskIsValid = false;
index->mIndexNeedsUpdate = false;
index->mIndexStats.Clear();
index->mFrecencyArray.Clear();
index->mExpirationArray.Clear();
index->mIndex.Clear();
}
if (file) {
// Ignore the result. The file might not exist and the failure is not fatal.
file->Remove(false);
}
return NS_OK;
}
// static
nsresult
CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
@ -1274,10 +1412,11 @@ CacheIndex::WriteIndexToDisk()
mProcessEntries = mIndexStats.ActiveEntriesCount();
mIndexFileOpener = new FileOpenHelper(this);
rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
CacheFileIOManager::SPECIAL_FILE |
CacheFileIOManager::CREATE,
this);
mIndexFileOpener);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08x]", rv));
FinishWrite(false);
@ -1396,8 +1535,19 @@ CacheIndex::FinishWrite(bool aSucceeded)
ReleaseBuffer();
if (aSucceeded) {
// Opening of the file must not be in progress if writing succeeded.
MOZ_ASSERT(!mIndexFileOpener);
mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
mIndexOnDiskIsValid = true;
} else {
if (mIndexFileOpener) {
// If opening of the file is still in progress (e.g. WRITE process was
// canceled by RemoveAll()) then we need to cancel the opener to make sure
// that OnFileOpenedInternal() won't be called.
mIndexFileOpener->Cancel();
mIndexFileOpener = nullptr;
}
}
ProcessPendingOperations();
@ -1702,50 +1852,42 @@ CacheIndex::ReadIndexFromDisk()
nsresult rv;
CacheIndexAutoLock lock(this);
AssertOwnsLock();
MOZ_ASSERT(mState == INITIAL);
MOZ_ASSERT(mState == READING);
mReadFailed = false;
mReadOpenCount = 0;
ChangeState(READING);
mIndexFileOpener = new FileOpenHelper(this);
rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kIndexName),
CacheFileIOManager::SPECIAL_FILE |
CacheFileIOManager::OPEN,
this);
mIndexFileOpener);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
"failed [rv=0x%08x, file=%s]", rv, kIndexName));
mReadFailed = true;
} else {
mReadOpenCount++;
FinishRead(false);
return;
}
mJournalFileOpener = new FileOpenHelper(this);
rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kJournalName),
CacheFileIOManager::SPECIAL_FILE |
CacheFileIOManager::OPEN,
this);
mJournalFileOpener);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
"failed [rv=0x%08x, file=%s]", rv, kJournalName));
mReadFailed = true;
} else {
mReadOpenCount++;
FinishRead(false);
}
mTmpFileOpener = new FileOpenHelper(this);
rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
CacheFileIOManager::SPECIAL_FILE |
CacheFileIOManager::OPEN,
this);
mTmpFileOpener);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
"failed [rv=0x%08x, file=%s]", rv, kTempIndexName));
mReadFailed = true;
} else {
mReadOpenCount++;
}
if (mReadOpenCount == 0) {
FinishRead(false);
}
}
@ -2133,6 +2275,19 @@ CacheIndex::FinishRead(bool aSucceeded)
}
}
if (mIndexFileOpener) {
mIndexFileOpener->Cancel();
mIndexFileOpener = nullptr;
}
if (mJournalFileOpener) {
mJournalFileOpener->Cancel();
mJournalFileOpener = nullptr;
}
if (mTmpFileOpener) {
mTmpFileOpener->Cancel();
mTmpFileOpener = nullptr;
}
mIndexHandle = nullptr;
mJournalHandle = nullptr;
mRWHash = nullptr;
@ -2148,7 +2303,7 @@ CacheIndex::FinishRead(bool aSucceeded)
ProcessPendingOperations();
// Remove all entries that we haven't seen during this session
mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
StartBuildingIndex();
StartUpdatingIndex(true);
return;
}
@ -2156,7 +2311,7 @@ CacheIndex::FinishRead(bool aSucceeded)
mTmpJournal.Clear();
EnsureNoFreshEntry();
ProcessPendingOperations();
StartUpdatingIndex();
StartUpdatingIndex(false);
return;
}
@ -2171,9 +2326,9 @@ CacheIndex::FinishRead(bool aSucceeded)
// static
void
CacheIndex::DelayedBuildUpdate(nsITimer *aTimer, void *aClosure)
CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure)
{
LOG(("CacheIndex::DelayedBuildUpdate()"));
LOG(("CacheIndex::DelayedUpdate()"));
nsresult rv;
nsRefPtr<CacheIndex> index = gInstance;
@ -2184,7 +2339,7 @@ CacheIndex::DelayedBuildUpdate(nsITimer *aTimer, void *aClosure)
CacheIndexAutoLock lock(index);
index->mTimer = nullptr;
index->mUpdateTimer = nullptr;
if (!index->IsIndexUsable()) {
return;
@ -2194,30 +2349,34 @@ CacheIndex::DelayedBuildUpdate(nsITimer *aTimer, void *aClosure)
return;
}
MOZ_ASSERT(index->mState == BUILDING || index->mState == UPDATING);
// mUpdateEventPending must be false here since StartUpdatingIndex() won't
// schedule timer if it is true.
MOZ_ASSERT(!index->mUpdateEventPending);
if (index->mState != BUILDING && index->mState != UPDATING) {
LOG(("CacheIndex::DelayedUpdate() - Update was canceled"));
return;
}
// We need to redispatch to run with lower priority
nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
MOZ_ASSERT(ioThread);
rv = ioThread->Dispatch(index, CacheIOThread::BUILD_OR_UPDATE_INDEX);
index->mUpdateEventPending = true;
rv = ioThread->Dispatch(index, CacheIOThread::INDEX);
if (NS_FAILED(rv)) {
NS_WARNING("CacheIndex::DelayedBuildUpdate() - Can't dispatch event");
LOG(("CacheIndex::DelayedBuildUpdate() - Can't dispatch event" ));
if (index->mState == BUILDING) {
index->FinishBuild(false);
} else {
index->FinishUpdate(false);
}
index->mUpdateEventPending = false;
NS_WARNING("CacheIndex::DelayedUpdate() - Can't dispatch event");
LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event" ));
index->FinishUpdate(false);
}
}
nsresult
CacheIndex::ScheduleBuildUpdateTimer(uint32_t aDelay)
CacheIndex::ScheduleUpdateTimer(uint32_t aDelay)
{
LOG(("CacheIndex::ScheduleBuildUpdateTimer() [delay=%u]", aDelay));
LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay));
MOZ_ASSERT(!mTimer);
MOZ_ASSERT(!mUpdateTimer);
nsresult rv;
@ -2230,11 +2389,11 @@ CacheIndex::ScheduleBuildUpdateTimer(uint32_t aDelay)
rv = timer->SetTarget(ioTarget);
NS_ENSURE_SUCCESS(rv, rv);
rv = timer->InitWithFuncCallback(CacheIndex::DelayedBuildUpdate, nullptr,
rv = timer->InitWithFuncCallback(CacheIndex::DelayedUpdate, nullptr,
aDelay, nsITimer::TYPE_ONE_SHOT);
NS_ENSURE_SUCCESS(rv, rv);
mTimer.swap(timer);
mUpdateTimer.swap(timer);
return NS_OK;
}
@ -2299,43 +2458,16 @@ CacheIndex::InitEntryFromDiskData(CacheIndexEntry *aEntry,
(aFileSize + 0x3FF) >> 10)));
}
void
CacheIndex::StartBuildingIndex()
bool
CacheIndex::IsUpdatePending()
{
LOG(("CacheIndex::StartBuildingIndex()"));
AssertOwnsLock();
nsresult rv;
ChangeState(BUILDING);
mDontMarkIndexClean = false;
if (mShuttingDown) {
FinishBuild(false);
return;
if (mUpdateTimer || mUpdateEventPending) {
return true;
}
uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
if (elapsed < kBuildIndexStartDelay) {
rv = ScheduleBuildUpdateTimer(kBuildIndexStartDelay - elapsed);
if (NS_SUCCEEDED(rv)) {
return;
}
LOG(("CacheIndex::StartBuildingIndex() - ScheduleBuildUpdateTimer() failed."
" Starting build immediately."));
}
nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
MOZ_ASSERT(ioThread);
// We need to dispatch an event even if we are on IO thread since we need to
// build the inde with the correct priority.
rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
if (NS_FAILED(rv)) {
NS_WARNING("CacheIndex::StartBuildingIndex() - Can't dispatch event");
LOG(("CacheIndex::StartBuildingIndex() - Can't dispatch event" ));
FinishBuild(false);
}
return false;
}
void
@ -2356,13 +2488,13 @@ CacheIndex::BuildIndex()
rv = SetupDirectoryEnumerator();
}
if (mState == SHUTDOWN) {
// The index was shut down while we released the lock. FinishBuild() was
// The index was shut down while we released the lock. FinishUpdate() was
// already called from Shutdown(), so just simply return here.
return;
}
if (NS_FAILED(rv)) {
FinishBuild(false);
FinishUpdate(false);
return;
}
}
@ -2370,6 +2502,7 @@ CacheIndex::BuildIndex()
while (true) {
if (CacheIOThread::YieldAndRerun()) {
LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
mUpdateEventPending = true;
return;
}
@ -2383,7 +2516,7 @@ CacheIndex::BuildIndex()
return;
}
if (!file) {
FinishBuild(NS_SUCCEEDED(rv));
FinishUpdate(NS_SUCCEEDED(rv));
return;
}
@ -2475,54 +2608,17 @@ CacheIndex::BuildIndex()
NS_NOTREACHED("We should never get here");
}
void
CacheIndex::FinishBuild(bool aSucceeded)
{
LOG(("CacheIndex::FinishBuild() [succeeded=%d]", aSucceeded));
MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == BUILDING);
AssertOwnsLock();
if (mDirEnumerator) {
if (NS_IsMainThread()) {
LOG(("CacheIndex::FinishBuild() - posting of PreShutdownInternal failed? "
"Cannot safely release mDirEnumerator, leaking it!"));
NS_WARNING(("CacheIndex::FinishBuild() - Leaking mDirEnumerator!"));
// This can happen only in case dispatching event to IO thread failed in
// CacheIndex::PreShutdown().
mDirEnumerator.forget(); // Leak it since dir enumerator is not threadsafe
} else {
mDirEnumerator->Close();
mDirEnumerator = nullptr;
}
}
if (!aSucceeded) {
mDontMarkIndexClean = true;
}
if (mState == BUILDING) {
// Make sure we won't start update. Index should be up to date, if build
// was successful. If the build failed, there is no reason to believe that
// the update will succeed.
mIndexNeedsUpdate = false;
ChangeState(READY);
mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
}
}
bool
CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState)
{
// Start updating process when we are in or we are switching to READY state
// and index needs update, but not during shutdown.
// and index needs update, but not during shutdown or when removing all
// entries.
if ((mState == READY || aSwitchingToReadyState) && mIndexNeedsUpdate &&
!mShuttingDown) {
!mShuttingDown && !mRemovingAll) {
LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process"));
mIndexNeedsUpdate = false;
StartUpdatingIndex();
StartUpdatingIndex(false);
return true;
}
@ -2530,31 +2626,38 @@ CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState)
}
void
CacheIndex::StartUpdatingIndex()
CacheIndex::StartUpdatingIndex(bool aRebuild)
{
LOG(("CacheIndex::StartUpdatingIndex()"));
LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild));
AssertOwnsLock();
nsresult rv;
mIndexStats.Log();
ChangeState(UPDATING);
ChangeState(aRebuild ? BUILDING : UPDATING);
mDontMarkIndexClean = false;
if (mShuttingDown) {
if (mShuttingDown || mRemovingAll) {
FinishUpdate(false);
return;
}
if (IsUpdatePending()) {
LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending"));
return;
}
uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
if (elapsed < kUpdateIndexStartDelay) {
rv = ScheduleBuildUpdateTimer(kUpdateIndexStartDelay - elapsed);
rv = ScheduleUpdateTimer(kUpdateIndexStartDelay - elapsed);
if (NS_SUCCEEDED(rv)) {
return;
}
LOG(("CacheIndex::StartUpdatingIndex() - ScheduleBuildUpdateTimer() failed."
" Starting update immediately."));
LOG(("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. "
"Starting update immediately."));
}
nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
@ -2562,8 +2665,10 @@ CacheIndex::StartUpdatingIndex()
// We need to dispatch an event even if we are on IO thread since we need to
// update the index with the correct priority.
rv = ioThread->Dispatch(this, CacheIOThread::BUILD_OR_UPDATE_INDEX);
mUpdateEventPending = true;
rv = ioThread->Dispatch(this, CacheIOThread::INDEX);
if (NS_FAILED(rv)) {
mUpdateEventPending = false;
NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event");
LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event" ));
FinishUpdate(false);
@ -2588,7 +2693,7 @@ CacheIndex::UpdateIndex()
rv = SetupDirectoryEnumerator();
}
if (mState == SHUTDOWN) {
// The index was shut down while we released the lock. FinishBuild() was
// The index was shut down while we released the lock. FinishUpdate() was
// already called from Shutdown(), so just simply return here.
return;
}
@ -2603,6 +2708,7 @@ CacheIndex::UpdateIndex()
if (CacheIOThread::YieldAndRerun()) {
LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level "
"events."));
mUpdateEventPending = true;
return;
}
@ -2747,7 +2853,8 @@ CacheIndex::FinishUpdate(bool aSucceeded)
{
LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == UPDATING);
MOZ_ASSERT(mState == UPDATING || mState == BUILDING ||
(!aSucceeded && mState == SHUTDOWN));
AssertOwnsLock();
@ -2769,21 +2876,23 @@ CacheIndex::FinishUpdate(bool aSucceeded)
mDontMarkIndexClean = true;
}
if (mState == UPDATING) {
if (aSucceeded) {
// If we've iterated over all entries successfully then all entries that
// really exist on the disk are now marked as fresh. All non-fresh entries
// don't exist anymore and must be removed from the index.
mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
}
// Make sure we won't start update again. If the update failed, there is no
// reason to believe that it will succeed next time.
mIndexNeedsUpdate = false;
ChangeState(READY);
mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
if (mState == SHUTDOWN) {
return;
}
if (mState == UPDATING && aSucceeded) {
// If we've iterated over all entries successfully then all entries that
// really exist on the disk are now marked as fresh. All non-fresh entries
// don't exist anymore and must be removed from the index.
mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
}
// Make sure we won't start update. If the build or update failed, there is no
// reason to believe that it will succeed next time.
mIndexNeedsUpdate = false;
ChangeState(READY);
mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
}
// static
@ -2845,8 +2954,9 @@ CacheIndex::ChangeState(EState aNewState)
}
// Try to evict entries over limit everytime we're leaving state READING,
// BUILDING or UPDATING, but not during shutdown.
if (!mShuttingDown && aNewState != SHUTDOWN &&
// BUILDING or UPDATING, but not during shutdown or when removing all
// entries.
if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN &&
(mState == READING || mState == BUILDING || mState == UPDATING)) {
CacheFileIOManager::EvictIfOverLimit();
}
@ -2976,6 +3086,8 @@ CacheIndex::Run()
return NS_OK;
}
mUpdateEventPending = false;
switch (mState) {
case BUILDING:
BuildIndex();
@ -2984,21 +3096,22 @@ CacheIndex::Run()
UpdateIndex();
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
LOG(("CacheIndex::Run() - Update/Build was canceled"));
}
return NS_OK;
}
nsresult
CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener,
CacheFileHandle *aHandle, nsresult aResult)
{
LOG(("CacheIndex::OnFileOpened() [handle=%p, result=0x%08x]", aHandle,
aResult));
LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
"result=0x%08x]", aOpener, aHandle, aResult));
nsresult rv;
CacheIndexAutoLock lock(this);
AssertOwnsLock();
if (!IsIndexUsable()) {
return NS_ERROR_NOT_AVAILABLE;
@ -3010,9 +3123,12 @@ CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
switch (mState) {
case WRITING:
MOZ_ASSERT(aOpener == mIndexFileOpener);
mIndexFileOpener = nullptr;
if (NS_FAILED(aResult)) {
LOG(("CacheIndex::OnFileOpened() - Can't open index file for writing "
"[rv=0x%08x]", aResult));
LOG(("CacheIndex::OnFileOpenedInternal() - Can't open index file for "
"writing [rv=0x%08x]", aResult));
FinishWrite(false);
} else {
mIndexHandle = aHandle;
@ -3020,74 +3136,67 @@ CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
}
break;
case READING:
mReadOpenCount--;
if (aOpener == mIndexFileOpener) {
mIndexFileOpener = nullptr;
if (mReadFailed) {
if (NS_SUCCEEDED(aResult)) {
CacheFileIOManager::DoomFile(aHandle, nullptr);
}
if (mReadOpenCount == 0) {
FinishRead(false);
}
return NS_OK;
}
switch (mReadOpenCount) {
case 2: // kIndexName
if (NS_FAILED(aResult)) {
mReadFailed = true;
} else {
MOZ_ASSERT(aHandle->Key() == kIndexName);
if (aHandle->FileSize() == 0) {
mReadFailed = true;
CacheFileIOManager::DoomFile(aHandle, nullptr);
} else {
mIndexHandle = aHandle;
}
}
break;
case 1: // kJournalName
if (NS_SUCCEEDED(aResult)) {
MOZ_ASSERT(aHandle->Key() == kJournalName);
if (aHandle->FileSize() == 0) {
CacheFileIOManager::DoomFile(aHandle, nullptr);
} else {
mJournalHandle = aHandle;
}
}
break;
case 0: // kTempIndexName
if (NS_SUCCEEDED(aResult)) {
MOZ_ASSERT(aHandle->Key() == kTempIndexName);
if (aHandle->FileSize() == 0) {
FinishRead(false);
CacheFileIOManager::DoomFile(aHandle, nullptr);
if (mJournalHandle) { // this should never happen
LOG(("CacheIndex::OnFileOpened() - Unexpected state, all files "
"[%s, %s, %s] should never exist. Removing whole index.",
kIndexName, kJournalName, kTempIndexName));
FinishRead(false);
break;
}
}
if (mJournalHandle) {
// Rename journal to make sure we update index on next start in case
// firefox crashes
rv = CacheFileIOManager::RenameFile(
mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::OnFileOpened() - CacheFileIOManager::RenameFile"
"() failed synchronously [rv=0x%08x]", rv));
FinishRead(false);
break;
}
break;
} else {
mIndexHandle = aHandle;
}
StartReadingIndex();
} else {
FinishRead(false);
break;
}
} else if (aOpener == mJournalFileOpener) {
mJournalFileOpener = nullptr;
mJournalHandle = aHandle;
} else if (aOpener == mTmpFileOpener) {
mTmpFileOpener = nullptr;
mTmpHandle = aHandle;
} else {
MOZ_ASSERT(false, "Unexpected state!");
}
if (mIndexFileOpener || mJournalFileOpener || mTmpFileOpener) {
// Some opener still didn't finish
break;
}
// We fail and cancel all other openers when we opening index file fails.
MOZ_ASSERT(mIndexHandle);
if (mTmpHandle) {
CacheFileIOManager::DoomFile(mTmpHandle, nullptr);
mTmpHandle = nullptr;
if (mJournalHandle) { // this shouldn't normally happen
LOG(("CacheIndex::OnFileOpenedInternal() - Unexpected state, all "
"files [%s, %s, %s] should never exist. Removing whole index.",
kIndexName, kJournalName, kTempIndexName));
FinishRead(false);
break;
}
}
if (mJournalHandle) {
// Rename journal to make sure we update index on next start in case
// firefox crashes
rv = CacheFileIOManager::RenameFile(
mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this);
if (NS_FAILED(rv)) {
LOG(("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::"
"RenameFile() failed synchronously [rv=0x%08x]", rv));
FinishRead(false);
break;
}
} else {
StartReadingIndex();
}
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
@ -3096,6 +3205,13 @@ CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
return NS_OK;
}
nsresult
CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
{
MOZ_CRASH("CacheIndex::OnFileOpened should not be called!");
return NS_ERROR_UNEXPECTED;
}
nsresult
CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
nsresult aResult)
@ -3117,6 +3233,12 @@ CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
switch (mState) {
case WRITING:
if (mIndexHandle != aHandle) {
LOG(("CacheIndex::OnDataWritten() - ignoring notification since it "
"belongs to previously canceled operation [state=%d]", mState));
break;
}
if (NS_FAILED(aResult)) {
FinishWrite(false);
} else {
@ -3135,7 +3257,9 @@ CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
}
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
// Writing was canceled.
LOG(("CacheIndex::OnDataWritten() - ignoring notification since the "
"operation was previously canceled [state=%d]", mState));
}
return NS_OK;
@ -3153,12 +3277,10 @@ CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
return NS_ERROR_NOT_AVAILABLE;
}
if (mState == READY && mShuttingDown) {
return NS_OK;
}
switch (mState) {
case READING:
MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle);
if (NS_FAILED(aResult)) {
FinishRead(false);
} else {
@ -3170,7 +3292,9 @@ CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
}
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
// Reading was canceled.
LOG(("CacheIndex::OnDataRead() - ignoring notification since the "
"operation was previously canceled [state=%d]", mState));
}
return NS_OK;
@ -3211,12 +3335,26 @@ CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
// This is a result of renaming the new index written to tmpfile to index
// file. This is the last step when writing the index and the whole
// writing process is successful iff renaming was successful.
if (mIndexHandle != aHandle) {
LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
"belongs to previously canceled operation [state=%d]", mState));
break;
}
FinishWrite(NS_SUCCEEDED(aResult));
break;
case READING:
// This is a result of renaming journal file to tmpfile. It is renamed
// before we start reading index and journal file and it should normally
// succeed. If it fails give up reading of index.
if (mJournalHandle != aHandle) {
LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
"belongs to previously canceled operation [state=%d]", mState));
break;
}
if (NS_FAILED(aResult)) {
FinishRead(false);
} else {
@ -3224,7 +3362,9 @@ CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
}
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
// Reading/writing was canceled.
LOG(("CacheIndex::OnFileRenamed() - ignoring notification since the "
"operation was previously canceled [state=%d]", mState));
}
return NS_OK;
@ -3261,7 +3401,7 @@ CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) cons
n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
}
sizeOf = do_QueryInterface(mTimer);
sizeOf = do_QueryInterface(mUpdateTimer);
if (sizeOf) {
n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
}

View File

@ -30,6 +30,7 @@ namespace mozilla {
namespace net {
class CacheFileMetadata;
class FileOpenHelper;
typedef struct {
// Version of the index. The index must be ignored and deleted when the file
@ -327,10 +328,22 @@ public:
void Log() {
LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
"dirty=%u, fresh=%u, empty=%u, size=%lld]", mCount, mNotInitialized,
"dirty=%u, fresh=%u, empty=%u, size=%u]", mCount, mNotInitialized,
mRemoved, mDirty, mFresh, mEmpty, mSize));
}
void Clear() {
MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Clear() - state logged!");
mCount = 0;
mNotInitialized = 0;
mRemoved = 0;
mDirty = 0;
mFresh = 0;
mEmpty = 0;
mSize = 0;
}
#ifdef DEBUG
bool StateLogged() {
return mStateLogged;
@ -512,6 +525,9 @@ public:
const uint32_t *aExpirationTime,
const uint32_t *aSize);
// Remove all entries from the index. Called when clearing the whole cache.
static nsresult RemoveAll();
enum EntryStatus {
EXISTS = 0,
DOES_NOT_EXIST = 1,
@ -538,10 +554,13 @@ private:
friend class CacheIndexEntryAutoManage;
friend class CacheIndexAutoLock;
friend class CacheIndexAutoUnlock;
friend class FileOpenHelper;
virtual ~CacheIndex();
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
nsresult OnFileOpenedInternal(FileOpenHelper *aOpener,
CacheFileHandle *aHandle, nsresult aResult);
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
nsresult aResult);
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
@ -682,29 +701,28 @@ private:
// Following methods perform updating and building of the index.
// Timer callback that starts update or build process.
static void DelayedBuildUpdate(nsITimer *aTimer, void *aClosure);
static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
// Posts timer event that start update or build process.
nsresult ScheduleBuildUpdateTimer(uint32_t aDelay);
nsresult ScheduleUpdateTimer(uint32_t aDelay);
nsresult SetupDirectoryEnumerator();
void InitEntryFromDiskData(CacheIndexEntry *aEntry,
CacheFileMetadata *aMetaData,
int64_t aFileSize);
// Starts build process or fires a timer when it is too early after startup.
void StartBuildingIndex();
// Returns true when either a timer is scheduled or event is posted.
bool IsUpdatePending();
// Iterates through all files in entries directory that we didn't create/open
// during this session, parses them and adds the entries to the index.
void BuildIndex();
// Finalizes build process.
void FinishBuild(bool aSucceeded);
bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
// Starts update process or fires a timer when it is too early after startup.
void StartUpdatingIndex();
// Starts update or build process or fires a timer when it is too early after
// startup.
void StartUpdatingIndex(bool aRebuild);
// Iterates through all files in entries directory that we didn't create/open
// during this session and theirs last modified time is newer than timestamp
// in the index header. Parses the files and adds the entries to the index.
void UpdateIndex();
// Finalizes update process.
// Finalizes update or build process.
void FinishUpdate(bool aSucceeded);
static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
@ -797,6 +815,12 @@ private:
// set this flag should also call StartUpdatingIndexIfNeeded() to cover the
// case when we are currently in READY state.
bool mIndexNeedsUpdate;
// Set at the beginning of RemoveAll() which clears the whole index. When
// removing all entries we must stop any pending reading, writing, updating or
// building operation. This flag is checked at various places and it prevents
// we won't start another operation (e.g. canceling reading of the index would
// normally start update or build process)
bool mRemovingAll;
// Whether the index file on disk exists and is valid.
bool mIndexOnDiskIsValid;
// When something goes wrong during updating or building process, we don't
@ -812,7 +836,9 @@ private:
TimeStamp mLastDumpTime;
// Timer of delayed update/build.
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsITimer> mUpdateTimer;
// True when build or update event is posted
bool mUpdateEventPending;
// Helper members used when reading/writing index from/to disk.
// Contains number of entries that should be skipped:
@ -828,12 +854,6 @@ private:
uint32_t mRWBufPos;
nsRefPtr<CacheHash> mRWHash;
// When reading index from disk, we open index, journal and tmpindex files at
// the same time. This value tell us how many times CacheIndex::OnFileOpened()
// will be called and identifies the handle.
uint32_t mReadOpenCount;
// Reading of index failed completely if true.
bool mReadFailed;
// Reading of journal succeeded if true.
bool mJournalReadSuccessfully;
@ -841,6 +861,12 @@ private:
nsRefPtr<CacheFileHandle> mIndexHandle;
// Handle used for reading journal file.
nsRefPtr<CacheFileHandle> mJournalHandle;
// Used to check the existence of the file during reading process.
nsRefPtr<CacheFileHandle> mTmpHandle;
nsRefPtr<FileOpenHelper> mIndexFileOpener;
nsRefPtr<FileOpenHelper> mJournalFileOpener;
nsRefPtr<FileOpenHelper> mTmpFileOpener;
// Directory enumerator used when building and updating index.
nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;

View File

@ -489,223 +489,6 @@ NS_IMETHODIMP CacheStorageService::AppCacheStorage(nsILoadContextInfo *aLoadCont
return NS_OK;
}
namespace { // anon
class CacheFilesDeletor : public nsRunnable
, public CacheEntriesEnumeratorCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
CacheFilesDeletor(nsICacheEntryDoomCallback* aCallback);
~CacheFilesDeletor();
nsresult DeleteAll();
nsresult DeleteDoomed();
private:
nsresult Init(CacheFileIOManager::EEnumerateMode aMode);
NS_IMETHOD Run();
NS_IMETHOD Execute();
void Callback();
virtual void OnFile(CacheFile* aFile);
nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
nsAutoPtr<CacheEntriesEnumerator> mEnumerator;
nsRefPtr<CacheIOThread> mIOThread;
uint32_t mRunning;
enum {
ALL,
DOOMED
} mMode;
nsresult mRv;
};
NS_IMPL_ISUPPORTS_INHERITED0(CacheFilesDeletor, nsRunnable);
CacheFilesDeletor::CacheFilesDeletor(nsICacheEntryDoomCallback* aCallback)
: mCallback(aCallback)
, mRunning(0)
, mRv(NS_OK)
{
MOZ_COUNT_CTOR(CacheFilesDeletor);
MOZ_EVENT_TRACER_WAIT(static_cast<nsRunnable*>(this), "net::cache::deletor");
}
CacheFilesDeletor::~CacheFilesDeletor()
{
MOZ_COUNT_DTOR(CacheFilesDeletor);
MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor");
if (mMode == ALL) {
// Now delete the doomed entries if some left.
nsRefPtr<CacheFilesDeletor> deletor = new CacheFilesDeletor(mCallback);
nsRefPtr<nsRunnableMethod<CacheFilesDeletor, nsresult> > event =
NS_NewRunnableMethod(deletor.get(), &CacheFilesDeletor::DeleteDoomed);
NS_DispatchToMainThread(event);
}
}
nsresult CacheFilesDeletor::DeleteAll()
{
mMode = ALL;
return Init(CacheFileIOManager::ENTRIES);
}
nsresult CacheFilesDeletor::DeleteDoomed()
{
mMode = DOOMED;
return Init(CacheFileIOManager::DOOMED);
}
nsresult CacheFilesDeletor::Init(CacheFileIOManager::EEnumerateMode aMode)
{
nsresult rv;
rv = CacheFileIOManager::EnumerateEntryFiles(
aMode, getter_Transfers(mEnumerator));
if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
rv = NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
mIOThread = CacheFileIOManager::IOThread();
NS_ENSURE_TRUE(mIOThread, NS_ERROR_NOT_INITIALIZED);
rv = mIOThread->Dispatch(this, CacheIOThread::EVICT);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void CacheFilesDeletor::Callback()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc) {
obsSvc->NotifyObservers(CacheStorageService::SelfISupports(),
"cacheservice:empty-cache",
nullptr);
}
if (!mCallback)
return;
nsCOMPtr<nsICacheEntryDoomCallback> callback;
callback.swap(mCallback);
callback->OnCacheEntryDoomed(mRv);
}
NS_IMETHODIMP CacheFilesDeletor::Run()
{
if (!mRunning) {
MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor");
}
MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor::exec");
nsresult rv = Execute();
if (NS_SUCCEEDED(mRv))
mRv = rv;
if (!mEnumerator || !mEnumerator->HasMore()) {
// No enumerator or no more elements means the job is done.
mEnumerator = nullptr;
if (mMode != ALL) {
nsRefPtr<nsRunnableMethod<CacheFilesDeletor> > event =
NS_NewRunnableMethod(this, &CacheFilesDeletor::Callback);
NS_DispatchToMainThread(event);
}
}
MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor::exec");
return NS_OK;
}
nsresult CacheFilesDeletor::Execute()
{
LOG(("CacheFilesDeletor::Execute [this=%p]", this));
if (!mEnumerator) {
// No enumerator means the job is done.
return NS_OK;
}
nsresult rv;
switch (mMode) {
case ALL:
case DOOMED:
// Simply delete all files, don't doom then though the backend
while (mEnumerator->HasMore()) {
nsCOMPtr<nsIFile> file;
rv = mEnumerator->GetNextFile(getter_AddRefs(file));
if (NS_FAILED(rv))
return rv;
#ifdef PR_LOG
nsAutoCString key;
file->GetNativeLeafName(key);
LOG((" deleting file with key=%s", key.get()));
#endif
rv = file->Remove(false);
if (NS_FAILED(rv)) {
LOG((" could not remove the file, probably doomed, rv=0x%08x", rv));
}
++mRunning;
if (CacheIOThread::YieldAndRerun()) {
LOG((" deleted %u files, breaking loop for higher level events."));
return NS_OK;
}
}
break;
default:
MOZ_ASSERT(false);
}
return NS_OK;
}
void CacheFilesDeletor::OnFile(CacheFile* aFile)
{
LOG(("CacheFilesDeletor::OnFile [this=%p, file=%p]", this, aFile));
if (!aFile)
return;
MOZ_EVENT_TRACER_EXEC(static_cast<nsRunnable*>(this), "net::cache::deletor::file");
#ifdef PR_LOG
nsAutoCString key;
aFile->Key(key);
#endif
switch (mMode) {
case ALL:
case DOOMED:
LOG((" dooming file with key=%s", key.get()));
// Uncompromisely delete the file!
aFile->Doom(nullptr);
break;
}
MOZ_EVENT_TRACER_DONE(static_cast<nsRunnable*>(this), "net::cache::deletor::file");
}
} // anon
NS_IMETHODIMP CacheStorageService::Clear()
{
nsresult rv;
@ -723,12 +506,9 @@ NS_IMETHODIMP CacheStorageService::Clear()
DoomStorageEntries(keys[i], true, nullptr);
}
// TODO - Callback can be provided!
nsRefPtr<CacheFilesDeletor> deletor = new CacheFilesDeletor(nullptr);
rv = deletor->DeleteAll();
rv = CacheFileIOManager::EvictAll();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
} else {
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -21,7 +21,6 @@ EXPORTS += [
]
UNIFIED_SOURCES += [
'CacheEntriesEnumerator.cpp',
'CacheHashUtils.cpp',
'CacheIOThread.cpp',
'CacheObserver.cpp',