mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 977766 - HTTP cache v2: selective deletion of data (by load context info), r=honzab, michal
This commit is contained in:
parent
dad7119019
commit
de7c98ded1
@ -67,5 +67,13 @@ interface nsILoadContextInfo : nsISupports
|
||||
GetIsAnonymous(&anon);
|
||||
return anon;
|
||||
}
|
||||
|
||||
bool Equals(nsILoadContextInfo *aOther)
|
||||
{
|
||||
return (IsPrivate() == aOther->IsPrivate() &&
|
||||
AppId() == aOther->AppId() &&
|
||||
IsInBrowserElement() == aOther->IsInBrowserElement() &&
|
||||
IsAnonymous() == aOther->IsAnonymous());
|
||||
}
|
||||
%}
|
||||
};
|
||||
|
605
netwerk/cache2/CacheFileContextEvictor.cpp
Normal file
605
netwerk/cache2/CacheFileContextEvictor.cpp
Normal file
@ -0,0 +1,605 @@
|
||||
/* 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 "CacheLog.h"
|
||||
#include "CacheFileContextEvictor.h"
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheIndex.h"
|
||||
#include "CacheIndexIterator.h"
|
||||
#include "CacheFileUtils.h"
|
||||
#include "nsIFile.h"
|
||||
#include "LoadContextInfo.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIDirectoryEnumerator.h"
|
||||
#include "mozilla/Base64.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
const char kContextEvictionPrefix[] = "ce_";
|
||||
const uint32_t kContextEvictionPrefixLength =
|
||||
sizeof(kContextEvictionPrefix) - 1;
|
||||
|
||||
bool CacheFileContextEvictor::sDiskAlreadySearched = false;
|
||||
|
||||
CacheFileContextEvictor::CacheFileContextEvictor()
|
||||
: mEvicting(false)
|
||||
, mIndexIsUpToDate(false)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this));
|
||||
}
|
||||
|
||||
CacheFileContextEvictor::~CacheFileContextEvictor()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::Init(nsIFile *aCacheDirectory)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::Init()"));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
CacheIndex::IsUpToDate(&mIndexIsUpToDate);
|
||||
|
||||
mCacheDirectory = aCacheDirectory;
|
||||
|
||||
rv = aCacheDirectory->Clone(getter_AddRefs(mEntriesDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mEntriesDir->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!sDiskAlreadySearched) {
|
||||
LoadEvictInfoFromDisk();
|
||||
if ((mEntries.Length() != 0) && mIndexIsUpToDate) {
|
||||
CreateIterators();
|
||||
StartEvicting();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CacheFileContextEvictor::ContextsCount()
|
||||
{
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
return mEntries.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p]",
|
||||
this, aLoadContextInfo));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
CacheFileContextEvictorEntry *entry = nullptr;
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (mEntries[i]->mInfo->Equals(aLoadContextInfo)) {
|
||||
entry = mEntries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
entry = new CacheFileContextEvictorEntry();
|
||||
entry->mInfo = aLoadContextInfo;
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
||||
entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
|
||||
|
||||
PersistEvictionInfoToDisk(aLoadContextInfo);
|
||||
|
||||
if (mIndexIsUpToDate) {
|
||||
// Already existing context could be added again, in this case the iterator
|
||||
// would be recreated. Close the old iterator explicitely.
|
||||
if (entry->mIterator) {
|
||||
entry->mIterator->Close();
|
||||
entry->mIterator = nullptr;
|
||||
}
|
||||
|
||||
rv = CacheIndex::GetIterator(aLoadContextInfo, false,
|
||||
getter_AddRefs(entry->mIterator));
|
||||
if (NS_FAILED(rv)) {
|
||||
// This could probably happen during shutdown. Remove the entry from
|
||||
// the array, but leave the info on the disk. No entry can be opened
|
||||
// during shutdown and we'll load the eviction info on next start.
|
||||
LOG(("CacheFileContextEvictor::AddContext() - Cannot get an iterator. "
|
||||
"[rv=0x%08x]", rv));
|
||||
mEntries.RemoveElement(entry);
|
||||
return rv;
|
||||
}
|
||||
|
||||
StartEvicting();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::CacheIndexStateChanged()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this));
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
bool isUpToDate = false;
|
||||
CacheIndex::IsUpToDate(&isUpToDate);
|
||||
if (mEntries.Length() == 0) {
|
||||
// Just save the state and exit, since there is nothing to do
|
||||
mIndexIsUpToDate = isUpToDate;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!isUpToDate && !mIndexIsUpToDate) {
|
||||
// Index is outdated and status has not changed, nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (isUpToDate && mIndexIsUpToDate) {
|
||||
// Status has not changed, but make sure the eviction is running.
|
||||
if (mEvicting) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We're not evicting, but we should be evicting?!
|
||||
LOG(("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to "
|
||||
"date, we have some context to evict but eviction is not running! "
|
||||
"Starting now."));
|
||||
}
|
||||
|
||||
mIndexIsUpToDate = isUpToDate;
|
||||
|
||||
if (mIndexIsUpToDate) {
|
||||
CreateIterators();
|
||||
StartEvicting();
|
||||
} else {
|
||||
CloseIterators();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile,
|
||||
bool *_retval)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
|
||||
PromiseFlatCString(aKey).get()));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
|
||||
MOZ_ASSERT(info);
|
||||
if (!info) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CacheFileContextEvictorEntry *entry = nullptr;
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (info->Equals(mEntries[i]->mInfo)) {
|
||||
entry = mEntries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Didn't find equal context, "
|
||||
"returning false."));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
rv = aFile->GetLastModifiedTime(&lastModifiedTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
|
||||
", returning false."));
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = !(lastModifiedTime > entry->mTimeStamp);
|
||||
LOG(("CacheFileContextEvictor::WasEvicted() - returning %s. [mTimeStamp=%lld,"
|
||||
" lastModifiedTime=%lld]", *_retval ? "true" : "false",
|
||||
mEntries[0]->mTimeStamp, lastModifiedTime));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::PersistEvictionInfoToDisk(
|
||||
nsILoadContextInfo *aLoadContextInfo)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
|
||||
"loadContextInfo=%p]", this, aLoadContextInfo));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString path;
|
||||
file->GetNativePath(path);
|
||||
#endif
|
||||
|
||||
PRFileDesc *fd;
|
||||
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600,
|
||||
&fd);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file "
|
||||
"failed! [path=%s, rv=0x%08x]", path.get(), rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
PR_Close(fd);
|
||||
|
||||
LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully "
|
||||
"created file. [path=%s]", path.get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::RemoveEvictInfoFromDisk(
|
||||
nsILoadContextInfo *aLoadContextInfo)
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
|
||||
"loadContextInfo=%p]", this, aLoadContextInfo));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = GetContextFile(aLoadContextInfo, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString path;
|
||||
file->GetNativePath(path);
|
||||
#endif
|
||||
|
||||
rv = file->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file"
|
||||
" failed! [path=%s, rv=0x%08x]", path.get(), rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully "
|
||||
"removed file. [path=%s]", path.get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::LoadEvictInfoFromDisk()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
sDiskAlreadySearched = true;
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> enumerator;
|
||||
rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(enumerator));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDirectoryEnumerator> dirEnum = do_QueryInterface(enumerator, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = dirEnum->GetNextFile(getter_AddRefs(file));
|
||||
if (!file) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool isDir = false;
|
||||
file->IsDirectory(&isDir);
|
||||
if (isDir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString leaf;
|
||||
rv = file->GetNativeLeafName(leaf);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - "
|
||||
"GetNativeLeafName() failed! Skipping file."));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (leaf.Length() < kContextEvictionPrefixLength) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!StringBeginsWith(leaf, NS_LITERAL_CSTRING(kContextEvictionPrefix))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString encoded;
|
||||
encoded = Substring(leaf, kContextEvictionPrefixLength);
|
||||
encoded.ReplaceChar('-', '/');
|
||||
|
||||
nsAutoCString decoded;
|
||||
rv = Base64Decode(encoded, decoded);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding "
|
||||
"failed. Removing the file. [file=%s]", leaf.get()));
|
||||
file->Remove(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(decoded);
|
||||
|
||||
if (!info) {
|
||||
LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
|
||||
"context key, removing file. [contextKey=%s, file=%s]",
|
||||
decoded.get(), leaf.get()));
|
||||
file->Remove(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
rv = file->GetLastModifiedTime(&lastModifiedTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry();
|
||||
entry->mInfo = info;
|
||||
entry->mTimeStamp = lastModifiedTime;
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsIFile **_retval)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString leafName;
|
||||
leafName.Assign(NS_LITERAL_CSTRING(kContextEvictionPrefix));
|
||||
|
||||
nsAutoCString keyPrefix;
|
||||
CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
|
||||
|
||||
// TODO: This hack is needed because current CacheFileUtils::ParseKey() can
|
||||
// parse only the whole key and not just the key prefix generated by
|
||||
// CacheFileUtils::CreateKeyPrefix(). This should be removed once bug #968593
|
||||
// is fixed.
|
||||
keyPrefix.Append(":foo");
|
||||
|
||||
nsAutoCString data64;
|
||||
rv = Base64Encode(keyPrefix, data64);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Replace '/' with '-' since '/' cannot be part of the filename.
|
||||
data64.ReplaceChar('/', '-');
|
||||
|
||||
leafName.Append(data64);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = mCacheDirectory->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = file->AppendNative(leafName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
file.swap(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileContextEvictor::CreateIterators()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this));
|
||||
|
||||
CloseIterators();
|
||||
|
||||
nsresult rv;
|
||||
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ) {
|
||||
rv = CacheIndex::GetIterator(mEntries[i]->mInfo, false,
|
||||
getter_AddRefs(mEntries[i]->mIterator));
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator"
|
||||
". [rv=0x%08x]", rv));
|
||||
mEntries.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileContextEvictor::CloseIterators()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this));
|
||||
|
||||
for (uint32_t i = 0; i < mEntries.Length(); ++i) {
|
||||
if (mEntries[i]->mIterator) {
|
||||
mEntries[i]->mIterator->Close();
|
||||
mEntries[i]->mIterator = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileContextEvictor::StartEvicting()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this));
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
if (mEvicting) {
|
||||
LOG(("CacheFileContextEvictor::StartEvicting() - already evicintg."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEntries.Length() == 0) {
|
||||
LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict."));
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> ev;
|
||||
ev = NS_NewRunnableMethod(this, &CacheFileContextEvictor::EvictEntries);
|
||||
|
||||
nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
|
||||
|
||||
nsresult rv = ioThread->Dispatch(ev, CacheIOThread::EVICT);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to "
|
||||
"IO thread. [rv=0x%08x]", rv));
|
||||
}
|
||||
|
||||
mEvicting = true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileContextEvictor::EvictEntries()
|
||||
{
|
||||
LOG(("CacheFileContextEvictor::EvictEntries()"));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
mEvicting = false;
|
||||
|
||||
if (!mIndexIsUpToDate) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
|
||||
"outdated index."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (CacheIOThread::YieldAndRerun()) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher "
|
||||
"level events."));
|
||||
mEvicting = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mEntries.Length() == 0) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there "
|
||||
"is no context to evict."));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SHA1Sum::Hash hash;
|
||||
rv = mEntries[0]->mIterator->GetNextHash(&hash);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
|
||||
"iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
|
||||
mEntries[0]->mInfo.get()));
|
||||
RemoveEvictInfoFromDisk(mEntries[0]->mInfo);
|
||||
mEntries.RemoveElementAt(0);
|
||||
continue;
|
||||
} else if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Iterator failed to "
|
||||
"provide next hash (shutdown?), keeping eviction info on disk."
|
||||
" [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
|
||||
mEntries[0]->mInfo.get()));
|
||||
mEntries.RemoveElementAt(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Processing hash. "
|
||||
"[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]", LOGSHA1(&hash),
|
||||
mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
|
||||
|
||||
nsRefPtr<CacheFileHandle> handle;
|
||||
CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
|
||||
getter_AddRefs(handle));
|
||||
if (handle) {
|
||||
// We doom any active handle in CacheFileIOManager::EvictByContext(), so
|
||||
// this must be a new one. Skip it.
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
|
||||
"found an active handle. [handle=%p]", handle.get()));
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString leafName;
|
||||
CacheFileIOManager::HashToStr(&hash, leafName);
|
||||
|
||||
PRTime lastModifiedTime;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = mEntriesDir->Clone(getter_AddRefs(file));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = file->AppendNative(leafName);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = file->GetLastModifiedTime(&lastModifiedTime);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Cannot get last modified "
|
||||
"time, skipping entry."));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastModifiedTime > mEntries[0]->mTimeStamp) {
|
||||
LOG(("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. "
|
||||
"[mTimeStamp=%lld, lastModifiedTime=%lld]", mEntries[0]->mTimeStamp,
|
||||
lastModifiedTime));
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(("CacheFileContextEvictor::EvictEntries - Removing entry."));
|
||||
file->Remove(false);
|
||||
CacheIndex::RemoveEntry(&hash);
|
||||
}
|
||||
|
||||
NS_NOTREACHED("We should never get here");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
91
netwerk/cache2/CacheFileContextEvictor.h
Normal file
91
netwerk/cache2/CacheFileContextEvictor.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* 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 CacheFileContextEvictor__h__
|
||||
#define CacheFileContextEvictor__h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsIFile;
|
||||
class nsILoadContextInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheIndexIterator;
|
||||
|
||||
struct CacheFileContextEvictorEntry
|
||||
{
|
||||
nsCOMPtr<nsILoadContextInfo> mInfo;
|
||||
PRTime mTimeStamp; // in milliseconds
|
||||
nsRefPtr<CacheIndexIterator> mIterator;
|
||||
};
|
||||
|
||||
class CacheFileContextEvictor
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheFileContextEvictor)
|
||||
|
||||
CacheFileContextEvictor();
|
||||
virtual ~CacheFileContextEvictor();
|
||||
|
||||
nsresult Init(nsIFile *aCacheDirectory);
|
||||
|
||||
// Returns number of contexts that are being evicted.
|
||||
uint32_t ContextsCount();
|
||||
// Start evicting given context.
|
||||
nsresult AddContext(nsILoadContextInfo *aLoadContextInfo);
|
||||
// CacheFileIOManager calls this method when CacheIndex's state changes. We
|
||||
// check whether the index is up to date and start or stop evicting according
|
||||
// to index's state.
|
||||
nsresult CacheIndexStateChanged();
|
||||
// CacheFileIOManager calls this method to check whether an entry file should
|
||||
// be considered as evicted. It returns true when there is a matching context
|
||||
// info to the given key and the last modified time of the entry file is
|
||||
// earlier than the time stamp of the time when the context was added to the
|
||||
// evictor.
|
||||
nsresult WasEvicted(const nsACString &aKey, nsIFile *aFile, bool *_retval);
|
||||
|
||||
private:
|
||||
// Writes information about eviction of the given context to the disk. This is
|
||||
// done for every context added to the evictor to be able to recover eviction
|
||||
// after a shutdown or crash. When the context file is found after startup, we
|
||||
// restore mTimeStamp from the last modified time of the file.
|
||||
nsresult PersistEvictionInfoToDisk(nsILoadContextInfo *aLoadContextInfo);
|
||||
// Once we are done with eviction for the given context, the eviction info is
|
||||
// removed from the disk.
|
||||
nsresult RemoveEvictInfoFromDisk(nsILoadContextInfo *aLoadContextInfo);
|
||||
// Tries to load all contexts from the disk. This method is called just once
|
||||
// after startup.
|
||||
nsresult LoadEvictInfoFromDisk();
|
||||
nsresult GetContextFile(nsILoadContextInfo *aLoadContextInfo,
|
||||
nsIFile **_retval);
|
||||
|
||||
void CreateIterators();
|
||||
void CloseIterators();
|
||||
void StartEvicting();
|
||||
nsresult EvictEntries();
|
||||
|
||||
// Whether eviction is in progress
|
||||
bool mEvicting;
|
||||
// Whether index is up to date. We wait with eviction until the index finishes
|
||||
// update process when it is outdated. NOTE: We also stop eviction in progress
|
||||
// when the index is found outdated, the eviction is restarted again once the
|
||||
// update process finishes.
|
||||
bool mIndexIsUpToDate;
|
||||
// Whether we already tried to restore unfinished jobs from previous run after
|
||||
// startup.
|
||||
static bool sDiskAlreadySearched;
|
||||
// Array of contexts being evicted.
|
||||
nsTArray<nsAutoPtr<CacheFileContextEvictorEntry> > mEntries;
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
nsCOMPtr<nsIFile> mEntriesDir;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
@ -14,6 +14,7 @@
|
||||
#include "CacheFile.h"
|
||||
#include "CacheObserver.h"
|
||||
#include "nsIFile.h"
|
||||
#include "CacheFileContextEvictor.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIDirectoryEnumerator.h"
|
||||
@ -593,7 +594,7 @@ public:
|
||||
mRV = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
|
||||
getter_AddRefs(mHandle));
|
||||
} else {
|
||||
mRV = mIOMan->OpenFileInternal(&mHash, mFlags,
|
||||
mRV = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
|
||||
getter_AddRefs(mHandle));
|
||||
}
|
||||
mIOMan = nullptr;
|
||||
@ -1548,11 +1549,13 @@ CacheFileIOManager::OpenFile(const nsACString &aKey,
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
||||
const nsACString &aKey,
|
||||
uint32_t aFlags,
|
||||
CacheFileHandle **_retval)
|
||||
{
|
||||
LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
|
||||
"flags=%d]", LOGSHA1(aHash), aFlags));
|
||||
"key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
|
||||
aFlags));
|
||||
|
||||
MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
|
||||
|
||||
@ -1615,6 +1618,22 @@ CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
|
||||
rv = file->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists && mContextEvictor) {
|
||||
if (mContextEvictor->ContextsCount() == 0) {
|
||||
mContextEvictor = nullptr;
|
||||
} else {
|
||||
bool wasEvicted = false;
|
||||
mContextEvictor->WasEvicted(aKey, file, &wasEvicted);
|
||||
if (wasEvicted) {
|
||||
LOG(("CacheFileIOManager::OpenFileInternal() - Removing file since the "
|
||||
"entry was evicted by EvictByContext()"));
|
||||
exists = false;
|
||||
file->Remove(false);
|
||||
CacheIndex::RemoveEntry(aHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
@ -2564,7 +2583,8 @@ CacheFileIOManager::EvictAllInternal()
|
||||
for (uint32_t i = 0; i < handles.Length(); ++i) {
|
||||
rv = DoomFileInternal(handles[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
|
||||
"[handle=%p]", handles[i].get()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2599,6 +2619,144 @@ CacheFileIOManager::EvictAllInternal()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo)
|
||||
{
|
||||
LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
|
||||
aLoadContextInfo));
|
||||
|
||||
nsresult rv;
|
||||
nsRefPtr<CacheFileIOManager> ioMan = gInstance;
|
||||
|
||||
if (!ioMan) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> ev;
|
||||
ev = NS_NewRunnableMethodWithArg<nsCOMPtr<nsILoadContextInfo> >
|
||||
(ioMan, &CacheFileIOManager::EvictByContextInternal, aLoadContextInfo);
|
||||
|
||||
rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo)
|
||||
{
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, "
|
||||
"anonymous=%u, inBrowser=%u, appId=%u]", aLoadContextInfo,
|
||||
aLoadContextInfo->IsAnonymous(), aLoadContextInfo->IsInBrowserElement(),
|
||||
aLoadContextInfo->AppId()));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
MOZ_ASSERT(mIOThread->IsCurrentThread());
|
||||
|
||||
MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
|
||||
if (aLoadContextInfo->IsPrivate()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!mCacheDirectory) {
|
||||
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 that matches the load context
|
||||
nsTArray<nsRefPtr<CacheFileHandle> > handles;
|
||||
mHandles.GetActiveHandles(&handles);
|
||||
|
||||
for (uint32_t i = 0; i < handles.Length(); ++i) {
|
||||
bool equals;
|
||||
rv = CacheFileUtils::KeyMatchesLoadContextInfo(handles[i]->Key(),
|
||||
aLoadContextInfo,
|
||||
&equals);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
|
||||
"handle! [handle=%p, key=%s]", handles[i].get(),
|
||||
handles[i]->Key().get()));
|
||||
MOZ_CRASH("Unexpected error!");
|
||||
}
|
||||
|
||||
if (equals) {
|
||||
rv = DoomFileInternal(handles[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
|
||||
" [handle=%p]", handles[i].get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mContextEvictor) {
|
||||
mContextEvictor = new CacheFileContextEvictor();
|
||||
mContextEvictor->Init(mCacheDirectory);
|
||||
}
|
||||
|
||||
mContextEvictor->AddContext(aLoadContextInfo);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
CacheFileIOManager::CacheIndexStateChanged()
|
||||
{
|
||||
LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// CacheFileIOManager lives longer than CacheIndex so gInstance must be
|
||||
// non-null here.
|
||||
MOZ_ASSERT(gInstance);
|
||||
|
||||
// We have to re-distatch even if we are on IO thread to prevent reentering
|
||||
// the lock in CacheIndex
|
||||
nsCOMPtr<nsIRunnable> ev;
|
||||
ev = NS_NewRunnableMethod(
|
||||
gInstance, &CacheFileIOManager::CacheIndexStateChangedInternal);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
|
||||
MOZ_ASSERT(ioTarget);
|
||||
|
||||
rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::CacheIndexStateChangedInternal()
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
// ignore notification during shutdown
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mContextEvictor) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mContextEvictor->CacheIndexStateChanged();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileIOManager::TrashDirectory(nsIFile *aFile)
|
||||
{
|
||||
@ -3231,6 +3389,18 @@ CacheFileIOManager::CreateCacheTree()
|
||||
|
||||
mTreeCreated = true;
|
||||
|
||||
if (!mContextEvictor) {
|
||||
nsRefPtr<CacheFileContextEvictor> contextEvictor;
|
||||
contextEvictor = new CacheFileContextEvictor();
|
||||
|
||||
// Init() method will try to load unfinished contexts from the disk. Store
|
||||
// the evictor as a member only when there is some unfinished job.
|
||||
contextEvictor->Init(mCacheDirectory);
|
||||
if (contextEvictor->ContextsCount()) {
|
||||
contextEvictor.swap(mContextEvictor);
|
||||
}
|
||||
}
|
||||
|
||||
StartRemovingTrash();
|
||||
|
||||
return NS_OK;
|
||||
|
@ -21,6 +21,7 @@
|
||||
class nsIFile;
|
||||
class nsITimer;
|
||||
class nsIDirectoryEnumerator;
|
||||
class nsILoadContextInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -174,6 +175,7 @@ class CloseFileEvent;
|
||||
class ReadEvent;
|
||||
class WriteEvent;
|
||||
class MetadataWriteScheduleEvent;
|
||||
class CacheFileContextEvictor;
|
||||
|
||||
#define CACHEFILEIOLISTENER_IID \
|
||||
{ /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */ \
|
||||
@ -257,6 +259,7 @@ public:
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult EvictIfOverLimit();
|
||||
static nsresult EvictAll();
|
||||
static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo);
|
||||
|
||||
static nsresult InitIndexEntry(CacheFileHandle *aHandle,
|
||||
uint32_t aAppId,
|
||||
@ -295,6 +298,7 @@ private:
|
||||
friend class RenameFileEvent;
|
||||
friend class CacheIndex;
|
||||
friend class MetadataWriteScheduleEvent;
|
||||
friend class CacheFileContextEvictor;
|
||||
|
||||
virtual ~CacheFileIOManager();
|
||||
|
||||
@ -302,6 +306,7 @@ private:
|
||||
nsresult ShutdownInternal();
|
||||
|
||||
nsresult OpenFileInternal(const SHA1Sum::Hash *aHash,
|
||||
const nsACString &aKey,
|
||||
uint32_t aFlags,
|
||||
CacheFileHandle **_retval);
|
||||
nsresult OpenSpecialFileInternal(const nsACString &aKey,
|
||||
@ -322,6 +327,7 @@ private:
|
||||
nsresult EvictIfOverLimitInternal();
|
||||
nsresult OverLimitEvictionInternal();
|
||||
nsresult EvictAllInternal();
|
||||
nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo);
|
||||
|
||||
nsresult TrashDirectory(nsIFile *aFile);
|
||||
static void OnTrashTimer(nsITimer *aTimer, void *aClosure);
|
||||
@ -350,6 +356,9 @@ private:
|
||||
nsresult UnscheduleMetadataWriteInternal(CacheFile * aFile);
|
||||
nsresult ShutdownMetadataWriteSchedulingInternal();
|
||||
|
||||
static nsresult CacheIndexStateChanged();
|
||||
nsresult CacheIndexStateChangedInternal();
|
||||
|
||||
// Memory reporting (private part)
|
||||
size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
@ -370,6 +379,7 @@ private:
|
||||
nsCOMPtr<nsIFile> mTrashDir;
|
||||
nsCOMPtr<nsIDirectoryEnumerator> mTrashDirEnumerator;
|
||||
nsTArray<nsCString> mFailedTrashDirs;
|
||||
nsRefPtr<CacheFileContextEvictor> mContextEvictor;
|
||||
};
|
||||
|
||||
} // net
|
||||
|
@ -271,6 +271,20 @@ AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & a
|
||||
aTarget.Append(',');
|
||||
}
|
||||
|
||||
nsresult
|
||||
KeyMatchesLoadContextInfo(const nsACString &aKey, nsILoadContextInfo *aInfo,
|
||||
bool *_retval)
|
||||
{
|
||||
nsCOMPtr<nsILoadContextInfo> info = ParseKey(aKey);
|
||||
|
||||
if (!info) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*_retval = info->Equals(aInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // CacheFileUtils
|
||||
} // net
|
||||
} // mozilla
|
||||
|
@ -27,6 +27,11 @@ AppendKeyPrefix(nsILoadContextInfo *aInfo, nsACString &_retval);
|
||||
void
|
||||
AppendTagWithValue(nsACString & aTarget, char const aTag, nsCSubstring const & aValue);
|
||||
|
||||
nsresult
|
||||
KeyMatchesLoadContextInfo(const nsACString &aKey,
|
||||
nsILoadContextInfo *aInfo,
|
||||
bool *_retval);
|
||||
|
||||
} // CacheFileUtils
|
||||
} // net
|
||||
} // mozilla
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "CacheLog.h"
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheFileMetadata.h"
|
||||
#include "CacheIndexIterator.h"
|
||||
#include "CacheIndexContextIterator.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIDirectoryEnumerator.h"
|
||||
@ -73,9 +75,11 @@ public:
|
||||
if (entry && !mOldRecord) {
|
||||
mIndex->InsertRecordToFrecencyArray(entry->mRec);
|
||||
mIndex->InsertRecordToExpirationArray(entry->mRec);
|
||||
mIndex->AddRecordToIterators(entry->mRec);
|
||||
} else if (!entry && mOldRecord) {
|
||||
mIndex->RemoveRecordFromFrecencyArray(mOldRecord);
|
||||
mIndex->RemoveRecordFromExpirationArray(mOldRecord);
|
||||
mIndex->RemoveRecordFromIterators(mOldRecord);
|
||||
} else if (entry && mOldRecord) {
|
||||
bool replaceFrecency = false;
|
||||
bool replaceExpiration = false;
|
||||
@ -83,14 +87,14 @@ public:
|
||||
if (entry->mRec != mOldRecord) {
|
||||
// record has a different address, we have to replace it
|
||||
replaceFrecency = replaceExpiration = true;
|
||||
mIndex->ReplaceRecordInIterators(mOldRecord, entry->mRec);
|
||||
} else {
|
||||
if (entry->mRec->mFrecency == 0 &&
|
||||
entry->mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME) {
|
||||
// This is a special case when we want to make sure that the entry is
|
||||
// placed at the end of the lists even when the values didn't change.
|
||||
replaceFrecency = replaceExpiration = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (entry->mRec->mFrecency != mOldFrecency) {
|
||||
replaceFrecency = true;
|
||||
}
|
||||
@ -157,70 +161,6 @@ private:
|
||||
bool mDoNotSearchInUpdates;
|
||||
};
|
||||
|
||||
class CacheIndexAutoLock {
|
||||
public:
|
||||
CacheIndexAutoLock(CacheIndex *aIndex)
|
||||
: mIndex(aIndex)
|
||||
, mLocked(true)
|
||||
{
|
||||
mIndex->Lock();
|
||||
}
|
||||
~CacheIndexAutoLock()
|
||||
{
|
||||
if (mLocked) {
|
||||
mIndex->Unlock();
|
||||
}
|
||||
}
|
||||
void Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mIndex->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mIndex->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CacheIndex> mIndex;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
class CacheIndexAutoUnlock {
|
||||
public:
|
||||
CacheIndexAutoUnlock(CacheIndex *aIndex)
|
||||
: mIndex(aIndex)
|
||||
, mLocked(false)
|
||||
{
|
||||
mIndex->Unlock();
|
||||
}
|
||||
~CacheIndexAutoUnlock()
|
||||
{
|
||||
if (!mLocked) {
|
||||
mIndex->Lock();
|
||||
}
|
||||
}
|
||||
void Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mIndex->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mIndex->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CacheIndex> mIndex;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
class FileOpenHelper : public CacheFileIOListener
|
||||
{
|
||||
public:
|
||||
@ -330,7 +270,7 @@ CacheIndex::~CacheIndex()
|
||||
ReleaseBuffer();
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
CacheIndex::Lock()
|
||||
{
|
||||
mLock.Lock();
|
||||
@ -338,7 +278,7 @@ CacheIndex::Lock()
|
||||
MOZ_ASSERT(!mIndexStats.StateLogged());
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
CacheIndex::Unlock()
|
||||
{
|
||||
MOZ_ASSERT(!mIndexStats.StateLogged());
|
||||
@ -411,6 +351,18 @@ CacheIndex::PreShutdown()
|
||||
"dontMarkIndexClean=%d]", index->mState, index->mIndexOnDiskIsValid,
|
||||
index->mDontMarkIndexClean));
|
||||
|
||||
LOG(("CacheIndex::PreShutdown() - Closing iterators."));
|
||||
for (uint32_t i = 0; i < index->mIterators.Length(); ) {
|
||||
rv = index->mIterators[i]->CloseInternal(NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(rv)) {
|
||||
// CacheIndexIterator::CloseInternal() removes itself from mIteratos iff
|
||||
// it returns success.
|
||||
LOG(("CacheIndex::PreShutdown() - Failed to remove iterator %p. "
|
||||
"[rv=0x%08x]", rv));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
index->mShuttingDown = true;
|
||||
|
||||
if (index->mState == READY) {
|
||||
@ -1291,6 +1243,64 @@ CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserve
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
CacheIndex::GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
|
||||
CacheIndexIterator **_retval)
|
||||
{
|
||||
LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo, aAddNew));
|
||||
|
||||
nsRefPtr<CacheIndex> index = gInstance;
|
||||
|
||||
if (!index) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
CacheIndexAutoLock lock(index);
|
||||
|
||||
if (!index->IsIndexUsable()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsRefPtr<CacheIndexIterator> iter;
|
||||
if (aInfo) {
|
||||
iter = new CacheIndexContextIterator(index, aAddNew, aInfo);
|
||||
} else {
|
||||
iter = new CacheIndexIterator(index, aAddNew);
|
||||
}
|
||||
|
||||
iter->AddRecords(index->mFrecencyArray);
|
||||
|
||||
index->mIterators.AppendElement(iter);
|
||||
iter.swap(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
CacheIndex::IsUpToDate(bool *_retval)
|
||||
{
|
||||
LOG(("CacheIndex::IsUpToDate()"));
|
||||
|
||||
nsRefPtr<CacheIndex> index = gInstance;
|
||||
|
||||
if (!index) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
CacheIndexAutoLock lock(index);
|
||||
|
||||
if (!index->IsIndexUsable()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
*_retval = (index->mState == READY || index->mState == WRITING) &&
|
||||
!index->mIndexNeedsUpdate && !index->mShuttingDown;
|
||||
|
||||
LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIndex::IsIndexUsable()
|
||||
{
|
||||
@ -3012,6 +3022,10 @@ CacheIndex::ChangeState(EState aNewState)
|
||||
|
||||
mState = aNewState;
|
||||
|
||||
if (mState != SHUTDOWN) {
|
||||
CacheFileIOManager::CacheIndexStateChanged();
|
||||
}
|
||||
|
||||
if (mState == READY && mDiskConsumptionObservers.Length()) {
|
||||
for (uint32_t i = 0; i < mDiskConsumptionObservers.Length(); ++i) {
|
||||
DiskConsumptionObserver* o = mDiskConsumptionObservers[i];
|
||||
@ -3130,6 +3144,48 @@ CacheIndex::RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord)
|
||||
MOZ_ASSERT(removed);
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndex::AddRecordToIterators(CacheIndexRecord *aRecord)
|
||||
{
|
||||
AssertOwnsLock();
|
||||
|
||||
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
|
||||
// Add a new record only when iterator is supposed to be updated.
|
||||
if (mIterators[i]->ShouldBeNewAdded()) {
|
||||
mIterators[i]->AddRecord(aRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndex::RemoveRecordFromIterators(CacheIndexRecord *aRecord)
|
||||
{
|
||||
AssertOwnsLock();
|
||||
|
||||
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
|
||||
// Remove the record from iterator always, it makes no sence to return
|
||||
// non-existing entries. Also the pointer to the record is no longer valid
|
||||
// once the entry is removed from index.
|
||||
mIterators[i]->RemoveRecord(aRecord);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndex::ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
|
||||
CacheIndexRecord *aNewRecord)
|
||||
{
|
||||
AssertOwnsLock();
|
||||
|
||||
for (uint32_t i = 0; i < mIterators.Length(); ++i) {
|
||||
// We have to replace the record always since the pointer is no longer
|
||||
// valid after this point. NOTE: Replacing the record doesn't mean that
|
||||
// a new entry was added, it just means that the data in the entry was
|
||||
// changed (e.g. a file size) and we had to track this change in
|
||||
// mPendingUpdates since mIndex was read-only.
|
||||
mIterators[i]->ReplaceRecord(aOldRecord, aNewRecord);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheIndex::Run()
|
||||
{
|
||||
|
@ -34,6 +34,7 @@ namespace net {
|
||||
|
||||
class CacheFileMetadata;
|
||||
class FileOpenHelper;
|
||||
class CacheIndexIterator;
|
||||
|
||||
typedef struct {
|
||||
// Version of the index. The index must be ignored and deleted when the file
|
||||
@ -250,6 +251,19 @@ public:
|
||||
GetExpirationTime(), GetFileSize()));
|
||||
}
|
||||
|
||||
static bool RecordMatchesLoadContextInfo(CacheIndexRecord *aRec,
|
||||
nsILoadContextInfo *aInfo)
|
||||
{
|
||||
if (!aInfo->IsPrivate() &&
|
||||
aInfo->AppId() == aRec->mAppId &&
|
||||
aInfo->IsAnonymous() == !!(aRec->mFlags & kAnonymousMask) &&
|
||||
aInfo->IsInBrowserElement() == !!(aRec->mFlags & kInBrowserMask)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Memory reporting
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
@ -552,6 +566,18 @@ public:
|
||||
// Asynchronously gets the disk cache size, used for display in the UI.
|
||||
static nsresult AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver);
|
||||
|
||||
// Returns an iterator that returns entries matching a given context that were
|
||||
// present in the index at the time this method was called. If aAddNew is true
|
||||
// then the iterator will also return entries created after this call.
|
||||
// NOTE: When some entry is removed from index it is removed also from the
|
||||
// iterator regardless what aAddNew was passed.
|
||||
static nsresult GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
|
||||
CacheIndexIterator **_retval);
|
||||
|
||||
// Returns true if we _think_ that the index is up to date. I.e. the state is
|
||||
// READY or WRITING and mIndexNeedsUpdate as well as mShuttingDown is false.
|
||||
static nsresult IsUpToDate(bool *_retval);
|
||||
|
||||
// Memory reporting
|
||||
static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
@ -561,6 +587,7 @@ private:
|
||||
friend class CacheIndexAutoLock;
|
||||
friend class CacheIndexAutoUnlock;
|
||||
friend class FileOpenHelper;
|
||||
friend class CacheIndexIterator;
|
||||
|
||||
virtual ~CacheIndex();
|
||||
|
||||
@ -799,6 +826,12 @@ private:
|
||||
void RemoveRecordFromFrecencyArray(CacheIndexRecord *aRecord);
|
||||
void RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord);
|
||||
|
||||
// Methods used by CacheIndexEntryAutoManage to keep the iterators up to date.
|
||||
void AddRecordToIterators(CacheIndexRecord *aRecord);
|
||||
void RemoveRecordFromIterators(CacheIndexRecord *aRecord);
|
||||
void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
|
||||
CacheIndexRecord *aNewRecord);
|
||||
|
||||
// Memory reporting (private part)
|
||||
size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
@ -902,6 +935,8 @@ private:
|
||||
nsTArray<CacheIndexRecord *> mFrecencyArray;
|
||||
nsTArray<CacheIndexRecord *> mExpirationArray;
|
||||
|
||||
nsTArray<CacheIndexIterator *> mIterators;
|
||||
|
||||
class DiskConsumptionObserver : public nsRunnable
|
||||
{
|
||||
public:
|
||||
@ -947,6 +982,69 @@ private:
|
||||
nsTArray<nsRefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
|
||||
};
|
||||
|
||||
class CacheIndexAutoLock {
|
||||
public:
|
||||
CacheIndexAutoLock(CacheIndex *aIndex)
|
||||
: mIndex(aIndex)
|
||||
, mLocked(true)
|
||||
{
|
||||
mIndex->Lock();
|
||||
}
|
||||
~CacheIndexAutoLock()
|
||||
{
|
||||
if (mLocked) {
|
||||
mIndex->Unlock();
|
||||
}
|
||||
}
|
||||
void Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mIndex->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mIndex->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CacheIndex> mIndex;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
class CacheIndexAutoUnlock {
|
||||
public:
|
||||
CacheIndexAutoUnlock(CacheIndex *aIndex)
|
||||
: mIndex(aIndex)
|
||||
, mLocked(false)
|
||||
{
|
||||
mIndex->Unlock();
|
||||
}
|
||||
~CacheIndexAutoUnlock()
|
||||
{
|
||||
if (!mLocked) {
|
||||
mIndex->Lock();
|
||||
}
|
||||
}
|
||||
void Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mIndex->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mIndex->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CacheIndex> mIndex;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
45
netwerk/cache2/CacheIndexContextIterator.cpp
Normal file
45
netwerk/cache2/CacheIndexContextIterator.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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 "CacheLog.h"
|
||||
#include "CacheIndexContextIterator.h"
|
||||
#include "CacheIndex.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
CacheIndexContextIterator::CacheIndexContextIterator(CacheIndex *aIndex,
|
||||
bool aAddNew,
|
||||
nsILoadContextInfo *aInfo)
|
||||
: CacheIndexIterator(aIndex, aAddNew)
|
||||
, mInfo(aInfo)
|
||||
{
|
||||
}
|
||||
|
||||
CacheIndexContextIterator::~CacheIndexContextIterator()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndexContextIterator::AddRecord(CacheIndexRecord *aRecord)
|
||||
{
|
||||
if (CacheIndexEntry::RecordMatchesLoadContextInfo(aRecord, mInfo)) {
|
||||
CacheIndexIterator::AddRecord(aRecord);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndexContextIterator::AddRecords(
|
||||
const nsTArray<CacheIndexRecord *> &aRecords)
|
||||
{
|
||||
// We need to add one by one so that those with wrong context are ignored.
|
||||
for (uint32_t i = 0; i < aRecords.Length(); ++i) {
|
||||
AddRecord(aRecords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
32
netwerk/cache2/CacheIndexContextIterator.h
Normal file
32
netwerk/cache2/CacheIndexContextIterator.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* 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 CacheIndexContextIterator__h__
|
||||
#define CacheIndexContextIterator__h__
|
||||
|
||||
#include "CacheIndexIterator.h"
|
||||
|
||||
class nsILoadContextInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheIndexContextIterator : public CacheIndexIterator
|
||||
{
|
||||
public:
|
||||
CacheIndexContextIterator(CacheIndex *aIndex, bool aAddNew,
|
||||
nsILoadContextInfo *aInfo);
|
||||
virtual ~CacheIndexContextIterator();
|
||||
|
||||
private:
|
||||
virtual void AddRecord(CacheIndexRecord *aRecord);
|
||||
virtual void AddRecords(const nsTArray<CacheIndexRecord *> &aRecords);
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> mInfo;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
126
netwerk/cache2/CacheIndexIterator.cpp
Normal file
126
netwerk/cache2/CacheIndexIterator.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/* 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 "CacheLog.h"
|
||||
#include "CacheIndexIterator.h"
|
||||
#include "CacheIndex.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
CacheIndexIterator::CacheIndexIterator(CacheIndex *aIndex, bool aAddNew)
|
||||
: mStatus(NS_OK)
|
||||
, mIndex(aIndex)
|
||||
, mAddNew(aAddNew)
|
||||
{
|
||||
LOG(("CacheIndexIterator::CacheIndexIterator() [this=%p]", this));
|
||||
}
|
||||
|
||||
CacheIndexIterator::~CacheIndexIterator()
|
||||
{
|
||||
LOG(("CacheIndexIterator::~CacheIndexIterator() [this=%p]", this));
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheIndexIterator::GetNextHash(SHA1Sum::Hash *aHash)
|
||||
{
|
||||
LOG(("CacheIndexIterator::GetNextHash() [this=%p]", this));
|
||||
|
||||
CacheIndexAutoLock lock(mIndex);
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
if (!mRecords.Length()) {
|
||||
CloseInternal(NS_ERROR_NOT_AVAILABLE);
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
memcpy(aHash, mRecords[mRecords.Length() - 1]->mHash, sizeof(SHA1Sum::Hash));
|
||||
mRecords.RemoveElementAt(mRecords.Length() - 1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheIndexIterator::Close()
|
||||
{
|
||||
LOG(("CacheIndexIterator::Close() [this=%p]", this));
|
||||
|
||||
CacheIndexAutoLock lock(mIndex);
|
||||
|
||||
return CloseInternal(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheIndexIterator::CloseInternal(nsresult aStatus)
|
||||
{
|
||||
LOG(("CacheIndexIterator::CloseInternal() [this=%p, status=0x%08x]", this,
|
||||
aStatus));
|
||||
|
||||
// Make sure status will be a failure
|
||||
MOZ_ASSERT(NS_FAILED(aStatus));
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
aStatus = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
DebugOnly<bool> removed = mIndex->mIterators.RemoveElement(this);
|
||||
MOZ_ASSERT(removed);
|
||||
mStatus = aStatus;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndexIterator::AddRecord(CacheIndexRecord *aRecord)
|
||||
{
|
||||
LOG(("CacheIndexIterator::AddRecord() [this=%p, record=%p]", this, aRecord));
|
||||
|
||||
mRecords.AppendElement(aRecord);
|
||||
}
|
||||
|
||||
void
|
||||
CacheIndexIterator::AddRecords(const nsTArray<CacheIndexRecord *> &aRecords)
|
||||
{
|
||||
LOG(("CacheIndexIterator::AddRecords() [this=%p]", this));
|
||||
|
||||
mRecords.AppendElements(aRecords);
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIndexIterator::RemoveRecord(CacheIndexRecord *aRecord)
|
||||
{
|
||||
LOG(("CacheIndexIterator::RemoveRecord() [this=%p, record=%p]", this,
|
||||
aRecord));
|
||||
|
||||
return mRecords.RemoveElement(aRecord);
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIndexIterator::ReplaceRecord(CacheIndexRecord *aOldRecord,
|
||||
CacheIndexRecord *aNewRecord)
|
||||
{
|
||||
LOG(("CacheIndexIterator::ReplaceRecord() [this=%p, oldRecord=%p, "
|
||||
"newRecord=%p]", this, aOldRecord, aNewRecord));
|
||||
|
||||
if (RemoveRecord(aOldRecord)) {
|
||||
AddRecord(aNewRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
57
netwerk/cache2/CacheIndexIterator.h
Normal file
57
netwerk/cache2/CacheIndexIterator.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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 CacheIndexIterator__h__
|
||||
#define CacheIndexIterator__h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/SHA1.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheIndex;
|
||||
struct CacheIndexRecord;
|
||||
|
||||
class CacheIndexIterator
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheIndexIterator)
|
||||
|
||||
CacheIndexIterator(CacheIndex *aIndex, bool aAddNew);
|
||||
virtual ~CacheIndexIterator();
|
||||
|
||||
// Returns a hash of a next entry. If there is no entry NS_ERROR_NOT_AVAILABLE
|
||||
// is returned and the iterator is closed. Other error is returned when the
|
||||
// iterator is closed for other reason, e.g. shutdown.
|
||||
nsresult GetNextHash(SHA1Sum::Hash *aHash);
|
||||
|
||||
// Closes the iterator. This means the iterator is removed from the list of
|
||||
// iterators in CacheIndex.
|
||||
nsresult Close();
|
||||
|
||||
protected:
|
||||
friend class CacheIndex;
|
||||
|
||||
nsresult CloseInternal(nsresult aStatus);
|
||||
|
||||
bool ShouldBeNewAdded() { return mAddNew; }
|
||||
virtual void AddRecord(CacheIndexRecord *aRecord);
|
||||
virtual void AddRecords(const nsTArray<CacheIndexRecord *> &aRecords);
|
||||
bool RemoveRecord(CacheIndexRecord *aRecord);
|
||||
bool ReplaceRecord(CacheIndexRecord *aOldRecord,
|
||||
CacheIndexRecord *aNewRecord);
|
||||
|
||||
nsresult mStatus;
|
||||
nsRefPtr<CacheIndex> mIndex;
|
||||
nsTArray<CacheIndexRecord *> mRecords;
|
||||
bool mAddNew;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
@ -163,97 +163,6 @@ void CacheStorageService::ShutdownBackground()
|
||||
|
||||
namespace { // anon
|
||||
|
||||
// EvictionRunnable
|
||||
// Responsible for purgin and unregistring entries (loaded) in memory
|
||||
|
||||
class EvictionRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
EvictionRunnable(nsCSubstring const & aContextKey, TCacheEntryTable* aEntries,
|
||||
bool aUsingDisk,
|
||||
nsICacheEntryDoomCallback* aCallback)
|
||||
: mContextKey(aContextKey)
|
||||
, mEntries(aEntries)
|
||||
, mCallback(aCallback)
|
||||
, mUsingDisk(aUsingDisk) { }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
LOG(("EvictionRunnable::Run [this=%p, disk=%d]", this, mUsingDisk));
|
||||
if (CacheStorageService::IsOnManagementThread()) {
|
||||
if (mUsingDisk) {
|
||||
// TODO for non private entries:
|
||||
// - rename/move all files to TRASH, block shutdown
|
||||
// - start the TRASH removal process
|
||||
// - may also be invoked from the main thread...
|
||||
}
|
||||
|
||||
if (mEntries) {
|
||||
// Process only a limited number of entries during a single loop to
|
||||
// prevent block of the management thread.
|
||||
mBatch = 50;
|
||||
mEntries->Enumerate(&EvictionRunnable::EvictEntry, this);
|
||||
}
|
||||
|
||||
// Anything left? Process in a separate invokation.
|
||||
if (mEntries && mEntries->Count())
|
||||
NS_DispatchToCurrentThread(this);
|
||||
else if (mCallback)
|
||||
NS_DispatchToMainThread(this); // TODO - we may want caller thread
|
||||
}
|
||||
else if (NS_IsMainThread()) {
|
||||
mCallback->OnCacheEntryDoomed(NS_OK);
|
||||
}
|
||||
else {
|
||||
MOZ_ASSERT(false, "Not main or cache management thread");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~EvictionRunnable()
|
||||
{
|
||||
if (mCallback)
|
||||
ProxyReleaseMainThread(mCallback);
|
||||
}
|
||||
|
||||
static PLDHashOperator EvictEntry(const nsACString& aKey,
|
||||
nsRefPtr<CacheEntry>& aEntry,
|
||||
void* aClosure)
|
||||
{
|
||||
EvictionRunnable* evictor = static_cast<EvictionRunnable*>(aClosure);
|
||||
|
||||
LOG((" evicting entry=%p", aEntry.get()));
|
||||
|
||||
// HACK ...
|
||||
// in-mem-only should only be Purge(WHOLE)'ed
|
||||
// on-disk may use the same technique I think, disk eviction runs independently
|
||||
if (!evictor->mUsingDisk) {
|
||||
// When evicting memory-only entries we have to remove them from
|
||||
// the master table as well. PurgeAndDoom() enters the service
|
||||
// management lock.
|
||||
aEntry->PurgeAndDoom();
|
||||
}
|
||||
else {
|
||||
// Disk (+memory-only) entries are already removed from the master
|
||||
// hash table, save locking here!
|
||||
aEntry->DoomAlreadyRemoved();
|
||||
}
|
||||
|
||||
if (!--evictor->mBatch)
|
||||
return PLDHashOperator(PL_DHASH_REMOVE | PL_DHASH_STOP);
|
||||
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
nsCString mContextKey;
|
||||
nsAutoPtr<TCacheEntryTable> mEntries;
|
||||
nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
|
||||
uint32_t mBatch;
|
||||
bool mUsingDisk;
|
||||
};
|
||||
|
||||
// WalkRunnable
|
||||
// Responsible to visit the storage and walk all entries on it asynchronously
|
||||
|
||||
@ -402,7 +311,7 @@ void CacheStorageService::DropPrivateBrowsingEntries()
|
||||
sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys);
|
||||
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i)
|
||||
DoomStorageEntries(keys[i], true, nullptr);
|
||||
DoomStorageEntries(keys[i], nullptr, true, nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -536,7 +445,7 @@ NS_IMETHODIMP CacheStorageService::Clear()
|
||||
sGlobalEntryTables->EnumerateRead(&CollectContexts, &keys);
|
||||
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i)
|
||||
DoomStorageEntries(keys[i], true, nullptr);
|
||||
DoomStorageEntries(keys[i], nullptr, true, nullptr);
|
||||
}
|
||||
|
||||
rv = CacheFileIOManager::EvictAll();
|
||||
@ -1174,31 +1083,14 @@ CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey,
|
||||
aReplace = true;
|
||||
}
|
||||
|
||||
// Check entry that is memory-only is also in related memory-only hashtable.
|
||||
// If not, it has been evicted and we will truncate it ; doom is pending for it,
|
||||
// this consumer just made it sooner then the entry has actually been removed
|
||||
// from the master hash table.
|
||||
// (This can be bypassed when entry is about to be replaced anyway.)
|
||||
if (entryExists && !entry->IsUsingDiskLocked() && !aReplace) {
|
||||
nsAutoCString memoryStorageID(aContextKey);
|
||||
AppendMemoryStorageID(memoryStorageID);
|
||||
CacheEntryTable* memoryEntries;
|
||||
aReplace = sGlobalEntryTables->Get(memoryStorageID, &memoryEntries) &&
|
||||
memoryEntries->GetWeak(entryKey) != entry;
|
||||
|
||||
#ifdef MOZ_LOGGING
|
||||
if (aReplace) {
|
||||
LOG((" memory-only entry %p for %s already doomed, replacing", entry.get(), entryKey.get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// If truncate is demanded, delete and doom the current entry
|
||||
if (entryExists && aReplace) {
|
||||
entries->Remove(entryKey);
|
||||
|
||||
LOG((" dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
|
||||
// On purpose called under the lock to prevent races of doom and open on I/O thread
|
||||
// No need to remove from both memory-only and all-entries tables. The new entry
|
||||
// will overwrite the shadow entry in its ctor.
|
||||
entry->DoomAlreadyRemoved();
|
||||
|
||||
entry = nullptr;
|
||||
@ -1358,11 +1250,13 @@ CacheStorageService::DoomStorageEntries(CacheStorage const* aStorage,
|
||||
|
||||
mozilla::MutexAutoLock lock(mLock);
|
||||
|
||||
return DoomStorageEntries(contextKey, aStorage->WriteToDisk(), aCallback);
|
||||
return DoomStorageEntries(contextKey, aStorage->LoadInfo(),
|
||||
aStorage->WriteToDisk(), aCallback);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
|
||||
nsILoadContextInfo* aContext,
|
||||
bool aDiskStorage,
|
||||
nsICacheEntryDoomCallback* aCallback)
|
||||
{
|
||||
@ -1373,27 +1267,70 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey,
|
||||
nsAutoCString memoryStorageID(aContextKey);
|
||||
AppendMemoryStorageID(memoryStorageID);
|
||||
|
||||
nsAutoPtr<CacheEntryTable> entries;
|
||||
if (aDiskStorage) {
|
||||
LOG((" dooming disk+memory storage of %s", aContextKey.BeginReading()));
|
||||
// Grab all entries in this storage
|
||||
sGlobalEntryTables->RemoveAndForget(aContextKey, entries);
|
||||
// Just remove the memory-only records table
|
||||
|
||||
// Just remove all entries, CacheFileIOManager will take care of the files.
|
||||
sGlobalEntryTables->Remove(aContextKey);
|
||||
sGlobalEntryTables->Remove(memoryStorageID);
|
||||
}
|
||||
else {
|
||||
|
||||
if (aContext && !aContext->IsPrivate()) {
|
||||
LOG((" dooming disk entries"));
|
||||
CacheFileIOManager::EvictByContext(aContext);
|
||||
}
|
||||
} else {
|
||||
LOG((" dooming memory-only storage of %s", aContextKey.BeginReading()));
|
||||
// Grab the memory-only records table, EvictionRunnable will safely remove
|
||||
// entries one by one from the master hashtable on the background management
|
||||
// thread. Code at AddStorageEntry ensures a new entry will always replace
|
||||
// memory only entries that EvictionRunnable yet didn't manage to remove.
|
||||
sGlobalEntryTables->RemoveAndForget(memoryStorageID, entries);
|
||||
|
||||
class MemoryEntriesRemoval {
|
||||
public:
|
||||
static PLDHashOperator EvictEntry(const nsACString& aKey,
|
||||
CacheEntry* aEntry,
|
||||
void* aClosure)
|
||||
{
|
||||
CacheEntryTable* entries = static_cast<CacheEntryTable*>(aClosure);
|
||||
nsCString key(aKey);
|
||||
RemoveExactEntry(entries, key, aEntry, false);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
};
|
||||
|
||||
// Remove the memory entries table from the global tables.
|
||||
// Since we store memory entries also in the disk entries table
|
||||
// we need to remove the memory entries from the disk table one
|
||||
// by one manually.
|
||||
nsAutoPtr<CacheEntryTable> memoryEntries;
|
||||
sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries);
|
||||
|
||||
CacheEntryTable* entries;
|
||||
sGlobalEntryTables->Get(aContextKey, &entries);
|
||||
if (memoryEntries && entries)
|
||||
memoryEntries->EnumerateRead(&MemoryEntriesRemoval::EvictEntry, entries);
|
||||
}
|
||||
|
||||
nsRefPtr<EvictionRunnable> evict = new EvictionRunnable(
|
||||
aContextKey, entries.forget(), aDiskStorage, aCallback);
|
||||
// An artificial callback. This is a candidate for removal tho. In the new
|
||||
// cache any 'doom' or 'evict' function ensures that the entry or entries
|
||||
// being doomed is/are not accessible after the function returns. So there is
|
||||
// probably no need for a callback - has no meaning. But for compatibility
|
||||
// with the old cache that is still in the tree we keep the API similar to be
|
||||
// able to make tests as well as other consumers work for now.
|
||||
class Callback : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Callback(nsICacheEntryDoomCallback* aCallback) : mCallback(aCallback) { }
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mCallback->OnCacheEntryDoomed(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsICacheEntryDoomCallback> mCallback;
|
||||
};
|
||||
|
||||
return Dispatch(evict);
|
||||
if (aCallback) {
|
||||
nsRefPtr<nsRunnable> callback = new Callback(aCallback);
|
||||
return NS_DispatchToCurrentThread(callback);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -205,6 +205,7 @@ private:
|
||||
|
||||
private:
|
||||
nsresult DoomStorageEntries(nsCSubstring const& aContextKey,
|
||||
nsILoadContextInfo* aContext,
|
||||
bool aDiskStorage,
|
||||
nsICacheEntryDoomCallback* aCallback);
|
||||
nsresult AddStorageEntry(nsCSubstring const& aContextKey,
|
||||
|
@ -34,12 +34,15 @@ SOURCES += [
|
||||
'CacheEntry.cpp',
|
||||
'CacheFile.cpp',
|
||||
'CacheFileChunk.cpp',
|
||||
'CacheFileContextEvictor.cpp',
|
||||
'CacheFileInputStream.cpp',
|
||||
'CacheFileIOManager.cpp',
|
||||
'CacheFileMetadata.cpp',
|
||||
'CacheFileOutputStream.cpp',
|
||||
'CacheFileUtils.cpp',
|
||||
'CacheIndex.cpp',
|
||||
'CacheIndexContextIterator.cpp',
|
||||
'CacheIndexIterator.cpp',
|
||||
'CacheLog.cpp',
|
||||
'CacheStorage.cpp',
|
||||
'CacheStorageService.cpp',
|
||||
|
@ -48,9 +48,9 @@ function run_test()
|
||||
);
|
||||
|
||||
asyncOpenCacheEntry("http://b/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||
new OpenCallback(NEW, "a1m", "a1d", function(entry) {
|
||||
new OpenCallback(NEW, "b1m", "b1d", function(entry) {
|
||||
asyncOpenCacheEntry("http://b/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||
new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
|
||||
new OpenCallback(NORMAL, "b1m", "b1d", function(entry) {
|
||||
mc.fired();
|
||||
})
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user