Backout bff74cecc67c, ffe0edb2aae7, b60b7c267cef, 6da154b43265, bcf6fd3ab9bb (bug 1182961 parts 1--5) for possible intermittent failures and performance problems.

a=bustage
This commit is contained in:
Nicholas Nethercote 2015-08-06 16:30:47 -07:00
parent de4e7b8407
commit 90c3d013d4
5 changed files with 424 additions and 246 deletions

View File

@ -427,13 +427,37 @@ CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
}
}
static PLDHashOperator
GetAllHandlesEnum(CacheFileHandles::HandleHashKey* aEntry, void *aClosure)
{
nsTArray<nsRefPtr<CacheFileHandle> > *array =
static_cast<nsTArray<nsRefPtr<CacheFileHandle> > *>(aClosure);
aEntry->GetHandles(*array);
return PL_DHASH_NEXT;
}
void
CacheFileHandles::GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
{
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
iter.Get()->GetHandles(*_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
@ -441,14 +465,7 @@ CacheFileHandles::GetActiveHandles(
nsTArray<nsRefPtr<CacheFileHandle> > *_retval)
{
MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
nsRefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle();
MOZ_ASSERT(handle);
if (!handle->IsDoomed()) {
_retval->AppendElement(handle);
}
}
mTable.EnumerateEntries(&GetActiveHandlesEnum, _retval);
}
void

View File

@ -629,7 +629,7 @@ CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash)
} else if (index->mState == READY ||
(entryRemoved && !entry->IsFresh())) {
// Removed non-fresh entries can be present as a result of
// MergeJournal()
// ProcessJournalEntry()
LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
" exist, update is needed"));
index->mIndexNeedsUpdate = true;
@ -848,7 +848,7 @@ CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash)
} else if (index->mState == READY ||
(entryRemoved && !entry->IsFresh())) {
// Removed non-fresh entries can be present as a result of
// MergeJournal()
// ProcessJournalEntry()
LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
", update is needed"));
index->mIndexNeedsUpdate = true;
@ -1488,58 +1488,64 @@ CacheIndex::ProcessPendingOperations()
AssertOwnsLock();
for (auto iter = mPendingUpdates.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntryUpdate* update = iter.Get();
LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(update->Hash())));
MOZ_ASSERT(update->IsFresh());
CacheIndexEntry* entry = mIndex.GetEntry(*update->Hash());
{
CacheIndexEntryAutoManage emng(update->Hash(), this);
emng.DoNotSearchInUpdates();
if (update->IsRemoved()) {
if (entry) {
if (entry->IsRemoved()) {
MOZ_ASSERT(entry->IsFresh());
MOZ_ASSERT(entry->IsDirty());
} else if (!entry->IsDirty() && entry->IsFileEmpty()) {
// Entries with empty file are not stored in index on disk. Just
// remove the entry, but only in case the entry is not dirty, i.e.
// the entry file was empty when we wrote the index.
mIndex.RemoveEntry(*update->Hash());
entry = nullptr;
} else {
entry->MarkRemoved();
entry->MarkDirty();
entry->MarkFresh();
}
}
} else if (entry) {
// Some information in mIndex can be newer than in mPendingUpdates (see
// bug 1074832). This will copy just those values that were really
// updated.
update->ApplyUpdate(entry);
} else {
// There is no entry in mIndex, copy all information from
// mPendingUpdates to mIndex.
entry = mIndex.PutEntry(*update->Hash());
*entry = *update;
}
}
iter.Remove();
}
mPendingUpdates.EnumerateEntries(&CacheIndex::UpdateEntryInIndex, this);
MOZ_ASSERT(mPendingUpdates.Count() == 0);
EnsureCorrectStats();
}
// static
PLDHashOperator
CacheIndex::UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry, void* aClosure)
{
CacheIndex *index = static_cast<CacheIndex *>(aClosure);
LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aEntry->Hash())));
MOZ_ASSERT(aEntry->IsFresh());
CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
emng.DoNotSearchInUpdates();
if (aEntry->IsRemoved()) {
if (entry) {
if (entry->IsRemoved()) {
MOZ_ASSERT(entry->IsFresh());
MOZ_ASSERT(entry->IsDirty());
} else if (!entry->IsDirty() && entry->IsFileEmpty()) {
// Entries with empty file are not stored in index on disk. Just remove
// the entry, but only in case the entry is not dirty, i.e. the entry
// file was empty when we wrote the index.
index->mIndex.RemoveEntry(*aEntry->Hash());
entry = nullptr;
} else {
entry->MarkRemoved();
entry->MarkDirty();
entry->MarkFresh();
}
}
return PL_DHASH_REMOVE;
}
if (entry) {
// Some information in mIndex can be newer than in mPendingUpdates (see bug
// 1074832). This will copy just those values that were really updated.
aEntry->ApplyUpdate(entry);
} else {
// There is no entry in mIndex, copy all information from mPendingUpdates
// to mIndex.
entry = index->mIndex.PutEntry(*aEntry->Hash());
*entry = *aEntry;
}
return PL_DHASH_REMOVE;
}
bool
CacheIndex::WriteIndexToDiskIfNeeded()
{
@ -1604,6 +1610,21 @@ CacheIndex::WriteIndexToDisk()
mSkipEntries = 0;
}
namespace {
struct WriteRecordsHelper
{
char *mBuf;
uint32_t mSkip;
uint32_t mProcessMax;
uint32_t mProcessed;
#ifdef DEBUG
bool mHasMore;
#endif
};
} // namespace
void
CacheIndex::WriteRecords()
{
@ -1626,49 +1647,27 @@ CacheIndex::WriteRecords()
}
uint32_t hashOffset = mRWBufPos;
char* buf = mRWBuf + mRWBufPos;
uint32_t skip = mSkipEntries;
uint32_t processMax = (mRWBufSize - mRWBufPos) / sizeof(CacheIndexRecord);
MOZ_ASSERT(processMax != 0 || mProcessEntries == 0); // TODO make sure we can write an empty index
uint32_t processed = 0;
WriteRecordsHelper data;
data.mBuf = mRWBuf + mRWBufPos;
data.mSkip = mSkipEntries;
data.mProcessMax = (mRWBufSize - mRWBufPos) / sizeof(CacheIndexRecord);
MOZ_ASSERT(data.mProcessMax != 0 || mProcessEntries == 0); // TODO make sure we can write an empty index
data.mProcessed = 0;
#ifdef DEBUG
bool hasMore = false;
data.mHasMore = false;
#endif
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
if (entry->IsRemoved() ||
!entry->IsInitialized() ||
entry->IsFileEmpty()) {
continue;
}
if (skip) {
skip--;
continue;
}
if (processed == processMax) {
#ifdef DEBUG
hasMore = true;
#endif
break;
}
entry->WriteToBuf(buf);
buf += sizeof(CacheIndexRecord);
processed++;
}
MOZ_ASSERT(mRWBufPos != static_cast<uint32_t>(buf - mRWBuf) ||
mIndex.EnumerateEntries(&CacheIndex::CopyRecordsToRWBuf, &data);
MOZ_ASSERT(mRWBufPos != static_cast<uint32_t>(data.mBuf - mRWBuf) ||
mProcessEntries == 0);
mRWBufPos = buf - mRWBuf;
mSkipEntries += processed;
mRWBufPos = data.mBuf - mRWBuf;
mSkipEntries += data.mProcessed;
MOZ_ASSERT(mSkipEntries <= mProcessEntries);
mRWHash->Update(mRWBuf + hashOffset, mRWBufPos - hashOffset);
if (mSkipEntries == mProcessEntries) {
MOZ_ASSERT(!hasMore);
MOZ_ASSERT(!data.mHasMore);
// We've processed all records
if (mRWBufPos + sizeof(CacheHash::Hash32_t) > mRWBufSize) {
@ -1680,7 +1679,7 @@ CacheIndex::WriteRecords()
NetworkEndian::writeUint32(mRWBuf + mRWBufPos, mRWHash->GetHash());
mRWBufPos += sizeof(CacheHash::Hash32_t);
} else {
MOZ_ASSERT(hasMore);
MOZ_ASSERT(data.mHasMore);
}
rv = CacheFileIOManager::Write(mIndexHandle, fileOffset, mRWBuf, mRWBufPos,
@ -1711,25 +1710,7 @@ CacheIndex::FinishWrite(bool aSucceeded)
// Opening of the file must not be in progress if writing succeeded.
MOZ_ASSERT(!mIndexFileOpener);
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
bool remove = false;
{
CacheIndexEntryAutoManage emng(entry->Hash(), this);
if (entry->IsRemoved()) {
emng.DoNotSearchInIndex();
remove = true;
} else if (entry->IsDirty()) {
entry->ClearDirty();
}
}
if (remove) {
iter.Remove();
}
}
mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
mIndexOnDiskIsValid = true;
} else {
if (mIndexFileOpener) {
@ -1750,6 +1731,62 @@ CacheIndex::FinishWrite(bool aSucceeded)
}
}
// static
PLDHashOperator
CacheIndex::CopyRecordsToRWBuf(CacheIndexEntry *aEntry, void* aClosure)
{
if (aEntry->IsRemoved()) {
return PL_DHASH_NEXT;
}
if (!aEntry->IsInitialized()) {
return PL_DHASH_NEXT;
}
if (aEntry->IsFileEmpty()) {
return PL_DHASH_NEXT;
}
WriteRecordsHelper *data = static_cast<WriteRecordsHelper *>(aClosure);
if (data->mSkip) {
data->mSkip--;
return PL_DHASH_NEXT;
}
if (data->mProcessed == data->mProcessMax) {
#ifdef DEBUG
data->mHasMore = true;
#endif
return PL_DHASH_STOP;
}
aEntry->WriteToBuf(data->mBuf);
data->mBuf += sizeof(CacheIndexRecord);
data->mProcessed++;
return PL_DHASH_NEXT;
}
// static
PLDHashOperator
CacheIndex::ApplyIndexChanges(CacheIndexEntry *aEntry, void* aClosure)
{
CacheIndex *index = static_cast<CacheIndex *>(aClosure);
CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
if (aEntry->IsRemoved()) {
emng.DoNotSearchInIndex();
return PL_DHASH_REMOVE;
}
if (aEntry->IsDirty()) {
aEntry->ClearDirty();
}
return PL_DHASH_NEXT;
}
nsresult
CacheIndex::GetFile(const nsACString &aName, nsIFile **_retval)
{
@ -1934,13 +1971,7 @@ CacheIndex::WriteLogToDisk()
NS_ENSURE_SUCCESS(rv, rv);
WriteLogHelper wlh(fd);
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
if (entry->IsRemoved() || entry->IsDirty()) {
wlh.AddEntry(entry);
}
iter.Remove();
}
mIndex.EnumerateEntries(&CacheIndex::WriteEntryToLog, &wlh);
rv = wlh.Finish();
PR_Close(fd);
@ -1973,6 +2004,19 @@ CacheIndex::WriteLogToDisk()
return NS_OK;
}
// static
PLDHashOperator
CacheIndex::WriteEntryToLog(CacheIndexEntry *aEntry, void* aClosure)
{
WriteLogHelper *wlh = static_cast<WriteLogHelper *>(aClosure);
if (aEntry->IsRemoved() || aEntry->IsDirty()) {
wlh->AddEntry(aEntry);
}
return PL_DHASH_REMOVE;
}
void
CacheIndex::ReadIndexFromDisk()
{
@ -2309,46 +2353,48 @@ CacheIndex::MergeJournal()
AssertOwnsLock();
for (auto iter = mTmpJournal.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
LOG(("CacheIndex::MergeJournal() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(entry->Hash())));
CacheIndexEntry* entry2 = mIndex.GetEntry(*entry->Hash());
CacheIndexEntryAutoManage emng(entry->Hash(), this);
if (entry->IsRemoved()) {
if (entry2) {
entry2->MarkRemoved();
entry2->MarkDirty();
}
} else {
if (!entry2) {
entry2 = mIndex.PutEntry(*entry->Hash());
}
*entry2 = *entry;
entry2->MarkDirty();
}
iter.Remove();
}
mTmpJournal.EnumerateEntries(&CacheIndex::ProcessJournalEntry, this);
MOZ_ASSERT(mTmpJournal.Count() == 0);
}
// static
PLDHashOperator
CacheIndex::ProcessJournalEntry(CacheIndexEntry *aEntry, void* aClosure)
{
CacheIndex *index = static_cast<CacheIndex *>(aClosure);
LOG(("CacheIndex::ProcessJournalEntry() [hash=%08x%08x%08x%08x%08x]",
LOGSHA1(aEntry->Hash())));
CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
if (aEntry->IsRemoved()) {
if (entry) {
entry->MarkRemoved();
entry->MarkDirty();
}
} else {
if (!entry) {
entry = index->mIndex.PutEntry(*aEntry->Hash());
}
*entry = *aEntry;
entry->MarkDirty();
}
return PL_DHASH_REMOVE;
}
void
CacheIndex::EnsureNoFreshEntry()
{
#ifdef DEBUG_STATS
CacheIndexStats debugStats;
debugStats.DisableLogging();
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
debugStats.BeforeChange(nullptr);
debugStats.AfterChange(iter.Get());
}
mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
MOZ_ASSERT(debugStats.Fresh() == 0);
#endif
}
@ -2360,14 +2406,21 @@ CacheIndex::EnsureCorrectStats()
MOZ_ASSERT(mPendingUpdates.Count() == 0);
CacheIndexStats debugStats;
debugStats.DisableLogging();
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
debugStats.BeforeChange(nullptr);
debugStats.AfterChange(iter.Get());
}
mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
MOZ_ASSERT(debugStats == mIndexStats);
#endif
}
// static
PLDHashOperator
CacheIndex::SumIndexStats(CacheIndexEntry *aEntry, void* aClosure)
{
CacheIndexStats *stats = static_cast<CacheIndexStats *>(aClosure);
stats->BeforeChange(nullptr);
stats->AfterChange(aEntry);
return PL_DHASH_NEXT;
}
void
CacheIndex::FinishRead(bool aSucceeded)
{
@ -2424,7 +2477,7 @@ CacheIndex::FinishRead(bool aSucceeded)
EnsureNoFreshEntry();
ProcessPendingOperations();
// Remove all entries that we haven't seen during this session
RemoveNonFreshEntries();
mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
StartUpdatingIndex(true);
return;
}
@ -3012,7 +3065,7 @@ CacheIndex::FinishUpdate(bool 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.
RemoveNonFreshEntries();
mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
}
// Make sure we won't start update. If the build or update failed, there is no
@ -3023,25 +3076,23 @@ CacheIndex::FinishUpdate(bool aSucceeded)
mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
}
void
CacheIndex::RemoveNonFreshEntries()
// static
PLDHashOperator
CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure)
{
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
if (entry->IsFresh()) {
continue;
}
LOG(("CacheIndex::RemoveNonFreshEntries() - Removing entry. "
"[hash=%08x%08x%08x%08x%08x]", LOGSHA1(entry->Hash())));
{
CacheIndexEntryAutoManage emng(entry->Hash(), this);
emng.DoNotSearchInIndex();
}
iter.Remove();
if (aEntry->IsFresh()) {
return PL_DHASH_NEXT;
}
LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. "
"[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry->Hash())));
CacheIndex *index = static_cast<CacheIndex *>(aClosure);
CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
emng.DoNotSearchInIndex();
return PL_DHASH_REMOVE;
}
// static

View File

@ -723,6 +723,8 @@ private:
// Merge all pending operations from mPendingUpdates into mIndex.
void ProcessPendingOperations();
static PLDHashOperator UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry,
void* aClosure);
// Following methods perform writing of the index file.
//
@ -743,6 +745,11 @@ private:
// Finalizes writing process.
void FinishWrite(bool aSucceeded);
static PLDHashOperator CopyRecordsToRWBuf(CacheIndexEntry *aEntry,
void* aClosure);
static PLDHashOperator ApplyIndexChanges(CacheIndexEntry *aEntry,
void* aClosure);
// Following methods perform writing of the journal during shutdown. All these
// methods must be called only during shutdown since they write/delete files
// directly on the main thread instead of using CacheFileIOManager that does
@ -756,6 +763,9 @@ private:
// Writes journal to the disk and clears dirty flag in index header.
nsresult WriteLogToDisk();
static PLDHashOperator WriteEntryToLog(CacheIndexEntry *aEntry,
void* aClosure);
// Following methods perform reading of the index from the disk.
//
// Index is read at startup just after initializing the CacheIndex. There are
@ -810,9 +820,13 @@ private:
// In debug build this method is called after processing pending operations
// to make sure mIndexStats contains correct information.
void EnsureCorrectStats();
static PLDHashOperator SumIndexStats(CacheIndexEntry *aEntry, void* aClosure);
// Finalizes reading process.
void FinishRead(bool aSucceeded);
static PLDHashOperator ProcessJournalEntry(CacheIndexEntry *aEntry,
void* aClosure);
// Following methods perform updating and building of the index.
// Timer callback that starts update or build process.
static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
@ -839,7 +853,8 @@ private:
// Finalizes update or build process.
void FinishUpdate(bool aSucceeded);
void RemoveNonFreshEntries();
static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
void* aClosure);
enum EState {
// Initial state in which the index is not usable

View File

@ -1442,6 +1442,25 @@ nsCookieService::HandleCorruptDB(DBState* aDBState)
}
}
static PLDHashOperator
RebuildDBCallback(nsCookieEntry *aEntry,
void *aArg)
{
mozIStorageBindingParamsArray* paramsArray =
static_cast<mozIStorageBindingParamsArray*>(aArg);
const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
nsCookie* cookie = cookies[i];
if (!cookie->IsSession()) {
bindCookieParameters(paramsArray, nsCookieKey(aEntry), cookie);
}
}
return PL_DHASH_NEXT;
}
void
nsCookieService::RebuildCorruptDB(DBState* aDBState)
{
@ -1495,18 +1514,7 @@ nsCookieService::RebuildCorruptDB(DBState* aDBState)
mozIStorageAsyncStatement* stmt = aDBState->stmtInsert;
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
for (auto iter = aDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
nsCookieEntry* entry = iter.Get();
const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
nsCookie* cookie = cookies[i];
if (!cookie->IsSession()) {
bindCookieParameters(paramsArray, nsCookieKey(entry), cookie);
}
}
}
aDBState->hostTable.EnumerateEntries(RebuildDBCallback, paramsArray.get());
// Make sure we've got something to write. If we don't, we're done.
uint32_t length;
@ -1945,6 +1953,20 @@ nsCookieService::RemoveAll()
return NS_OK;
}
static PLDHashOperator
COMArrayCallback(nsCookieEntry *aEntry,
void *aArg)
{
nsCOMArray<nsICookie> *data = static_cast<nsCOMArray<nsICookie> *>(aArg);
const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
data->AppendObject(cookies[i]);
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
{
@ -1956,12 +1978,7 @@ nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
EnsureReadComplete();
nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
cookieList.AppendObject(cookies[i]);
}
}
mDBState->hostTable.EnumerateEntries(COMArrayCallback, &cookieList);
return NS_NewArrayEnumerator(aEnumerator, cookieList);
}
@ -3663,6 +3680,45 @@ nsCookieService::RemoveAllFromMemory()
mDBState->cookieOldestTime = INT64_MAX;
}
// stores temporary data for enumerating over the hash entries,
// since enumeration is done using callback functions
struct nsPurgeData
{
typedef nsTArray<nsListIter> ArrayType;
nsPurgeData(int64_t aCurrentTime,
int64_t aPurgeTime,
ArrayType &aPurgeList,
nsIMutableArray *aRemovedList,
mozIStorageBindingParamsArray *aParamsArray)
: currentTime(aCurrentTime)
, purgeTime(aPurgeTime)
, oldestTime(INT64_MAX)
, purgeList(aPurgeList)
, removedList(aRemovedList)
, paramsArray(aParamsArray)
{
}
// the current time, in seconds
int64_t currentTime;
// lastAccessed time older than which cookies are eligible for purge
int64_t purgeTime;
// lastAccessed time of the oldest cookie found during purge, to update our indicator
int64_t oldestTime;
// list of cookies over the age limit, for purging
ArrayType &purgeList;
// list of all cookies we've removed, for notification
nsIMutableArray *removedList;
// The array of parameters to be bound to the statement for deletion later.
mozIStorageBindingParamsArray *paramsArray;
};
// comparator class for lastaccessed times of cookies.
class CompareCookiesByAge {
public:
@ -3703,6 +3759,42 @@ public:
}
};
PLDHashOperator
purgeCookiesCallback(nsCookieEntry *aEntry,
void *aArg)
{
nsPurgeData &data = *static_cast<nsPurgeData*>(aArg);
const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
mozIStorageBindingParamsArray *array = data.paramsArray;
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ) {
nsListIter iter(aEntry, i);
nsCookie *cookie = cookies[i];
// check if the cookie has expired
if (cookie->Expiry() <= data.currentTime) {
data.removedList->AppendElement(cookie, false);
COOKIE_LOGEVICTED(cookie, "Cookie expired");
// remove from list; do not increment our iterator
gCookieService->RemoveCookieFromList(iter, array);
} else {
// check if the cookie is over the age limit
if (cookie->LastAccessed() <= data.purgeTime) {
data.purgeList.AppendElement(iter);
} else if (cookie->LastAccessed() < data.oldestTime) {
// reset our indicator
data.oldestTime = cookie->LastAccessed();
}
++i;
}
}
return PL_DHASH_NEXT;
}
// purges expired and old cookies in a batch operation.
already_AddRefed<nsIArray>
nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
@ -3715,8 +3807,7 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
("PurgeCookies(): beginning purge with %ld cookies and %lld oldest age",
mDBState->cookieCount, aCurrentTimeInUsec - mDBState->cookieOldestTime));
typedef nsAutoTArray<nsListIter, kMaxNumberOfCookies> PurgeList;
PurgeList purgeList;
nsAutoTArray<nsListIter, kMaxNumberOfCookies> purgeList;
nsCOMPtr<nsIMutableArray> removedList = do_CreateInstance(NS_ARRAY_CONTRACTID);
@ -3728,40 +3819,9 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
}
int64_t currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
int64_t purgeTime = aCurrentTimeInUsec - mCookiePurgeAge;
int64_t oldestTime = INT64_MAX;
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
nsCookieEntry* entry = iter.Get();
const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ) {
nsListIter iter(entry, i);
nsCookie* cookie = cookies[i];
// check if the cookie has expired
if (cookie->Expiry() <= currentTime) {
removedList->AppendElement(cookie, false);
COOKIE_LOGEVICTED(cookie, "Cookie expired");
// remove from list; do not increment our iterator
gCookieService->RemoveCookieFromList(iter, paramsArray);
} else {
// check if the cookie is over the age limit
if (cookie->LastAccessed() <= purgeTime) {
purgeList.AppendElement(iter);
} else if (cookie->LastAccessed() < oldestTime) {
// reset our indicator
oldestTime = cookie->LastAccessed();
}
++i;
}
}
}
nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC,
aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList, paramsArray);
mDBState->hostTable.EnumerateEntries(purgeCookiesCallback, &data);
uint32_t postExpiryCookieCount = mDBState->cookieCount;
@ -3774,7 +3834,7 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
mDBState->cookieCount - mMaxNumberOfCookies : 0;
if (purgeList.Length() > excess) {
// We're not purging everything in the list, so update our indicator.
oldestTime = purgeList[excess].Cookie()->LastAccessed();
data.oldestTime = purgeList[excess].Cookie()->LastAccessed();
purgeList.SetLength(excess);
}
@ -3783,7 +3843,7 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
// together, and with ascending index. this allows us to iterate backwards
// over the list removing cookies, without having to adjust indexes as we go.
purgeList.Sort(CompareCookiesByIndex());
for (PurgeList::index_type i = purgeList.Length(); i--; ) {
for (nsPurgeData::ArrayType::index_type i = purgeList.Length(); i--; ) {
nsCookie *cookie = purgeList[i].Cookie();
removedList->AppendElement(cookie, false);
COOKIE_LOGEVICTED(cookie, "Cookie too old");
@ -3805,7 +3865,7 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
}
// reset the oldest time indicator
mDBState->cookieOldestTime = oldestTime;
mDBState->cookieOldestTime = data.oldestTime;
COOKIE_LOGSTRING(LogLevel::Debug,
("PurgeCookies(): %ld expired; %ld purged; %ld remain; %lld oldest age",
@ -3942,6 +4002,47 @@ nsCookieService::GetCookiesFromHost(const nsACString &aHost,
return NS_NewArrayEnumerator(aEnumerator, cookieList);
}
namespace {
/**
* This structure is used as a in/out parameter when enumerating the cookies
* for an app.
* It will contain the app id and onlyBrowserElement flag information as input
* and will contain the array of matching cookies as output.
*/
struct GetCookiesForAppStruct {
uint32_t appId;
bool onlyBrowserElement;
nsCOMArray<nsICookie> cookies;
GetCookiesForAppStruct() = delete;
GetCookiesForAppStruct(uint32_t aAppId, bool aOnlyBrowserElement)
: appId(aAppId)
, onlyBrowserElement(aOnlyBrowserElement)
{}
};
} // namespace
/* static */ PLDHashOperator
nsCookieService::GetCookiesForApp(nsCookieEntry* entry, void* arg)
{
GetCookiesForAppStruct* data = static_cast<GetCookiesForAppStruct*>(arg);
if (entry->mAppId != data->appId ||
(data->onlyBrowserElement && !entry->mInBrowserElement)) {
return PL_DHASH_NEXT;
}
const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
data->cookies.AppendObject(cookies[i]);
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsCookieService::GetCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement,
nsISimpleEnumerator** aEnumerator)
@ -3953,23 +4054,10 @@ nsCookieService::GetCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement,
NS_ENSURE_TRUE(aAppId != NECKO_UNKNOWN_APP_ID, NS_ERROR_INVALID_ARG);
nsCOMArray<nsICookie> cookies;
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
nsCookieEntry* entry = iter.Get();
GetCookiesForAppStruct data(aAppId, aOnlyBrowserElement);
mDBState->hostTable.EnumerateEntries(GetCookiesForApp, &data);
if (entry->mAppId != aAppId ||
(aOnlyBrowserElement && !entry->mInBrowserElement)) {
continue;
}
const nsCookieEntry::ArrayType& entryCookies = entry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < entryCookies.Length(); ++i) {
cookies.AppendObject(entryCookies[i]);
}
}
return NS_NewArrayEnumerator(aEnumerator, cookies);
return NS_NewArrayEnumerator(aEnumerator, data.cookies);
}
NS_IMETHODIMP

View File

@ -316,6 +316,12 @@ class nsCookieService final : public nsICookieService
already_AddRefed<nsIArray> CreatePurgeList(nsICookie2* aCookie);
void UpdateCookieOldestTime(DBState* aDBState, nsCookie* aCookie);
/**
* This method is used to iterate the cookie hash table and select the ones
* that are part of a specific app.
*/
static PLDHashOperator GetCookiesForApp(nsCookieEntry* entry, void* arg);
/**
* This method is a helper that allows calling nsICookieManager::Remove()
* with appId/inBrowserElement parameters.
@ -350,6 +356,7 @@ class nsCookieService final : public nsICookieService
int64_t mCookiePurgeAge;
// friends!
friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg);
friend class DBListenerErrorHandler;
friend class ReadCookieDBListener;
friend class CloseCookieDBListener;