1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheIndex.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3562 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheIndex.h" 1.9 + 1.10 +#include "CacheLog.h" 1.11 +#include "CacheFileIOManager.h" 1.12 +#include "CacheFileMetadata.h" 1.13 +#include "CacheIndexIterator.h" 1.14 +#include "CacheIndexContextIterator.h" 1.15 +#include "nsThreadUtils.h" 1.16 +#include "nsISimpleEnumerator.h" 1.17 +#include "nsIDirectoryEnumerator.h" 1.18 +#include "nsISizeOf.h" 1.19 +#include "nsPrintfCString.h" 1.20 +#include "mozilla/DebugOnly.h" 1.21 +#include "prinrval.h" 1.22 +#include "nsIFile.h" 1.23 +#include "nsITimer.h" 1.24 +#include "mozilla/AutoRestore.h" 1.25 +#include <algorithm> 1.26 + 1.27 + 1.28 +#define kMinUnwrittenChanges 300 1.29 +#define kMinDumpInterval 20000 // in milliseconds 1.30 +#define kMaxBufSize 16384 1.31 +#define kIndexVersion 0x00000001 1.32 +#define kUpdateIndexStartDelay 50000 // in milliseconds 1.33 + 1.34 +const char kIndexName[] = "index"; 1.35 +const char kTempIndexName[] = "index.tmp"; 1.36 +const char kJournalName[] = "index.log"; 1.37 + 1.38 +namespace mozilla { 1.39 +namespace net { 1.40 + 1.41 +/** 1.42 + * This helper class is responsible for keeping CacheIndex::mIndexStats, 1.43 + * CacheIndex::mFrecencyArray and CacheIndex::mExpirationArray up to date. 1.44 + */ 1.45 +class CacheIndexEntryAutoManage 1.46 +{ 1.47 +public: 1.48 + CacheIndexEntryAutoManage(const SHA1Sum::Hash *aHash, CacheIndex *aIndex) 1.49 + : mIndex(aIndex) 1.50 + , mOldRecord(nullptr) 1.51 + , mOldFrecency(0) 1.52 + , mOldExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME) 1.53 + , mDoNotSearchInIndex(false) 1.54 + , mDoNotSearchInUpdates(false) 1.55 + { 1.56 + mIndex->AssertOwnsLock(); 1.57 + 1.58 + mHash = aHash; 1.59 + CacheIndexEntry *entry = FindEntry(); 1.60 + mIndex->mIndexStats.BeforeChange(entry); 1.61 + if (entry && entry->IsInitialized() && !entry->IsRemoved()) { 1.62 + mOldRecord = entry->mRec; 1.63 + mOldFrecency = entry->mRec->mFrecency; 1.64 + mOldExpirationTime = entry->mRec->mExpirationTime; 1.65 + } 1.66 + } 1.67 + 1.68 + ~CacheIndexEntryAutoManage() 1.69 + { 1.70 + mIndex->AssertOwnsLock(); 1.71 + 1.72 + CacheIndexEntry *entry = FindEntry(); 1.73 + mIndex->mIndexStats.AfterChange(entry); 1.74 + if (!entry || !entry->IsInitialized() || entry->IsRemoved()) { 1.75 + entry = nullptr; 1.76 + } 1.77 + 1.78 + if (entry && !mOldRecord) { 1.79 + mIndex->InsertRecordToFrecencyArray(entry->mRec); 1.80 + mIndex->InsertRecordToExpirationArray(entry->mRec); 1.81 + mIndex->AddRecordToIterators(entry->mRec); 1.82 + } else if (!entry && mOldRecord) { 1.83 + mIndex->RemoveRecordFromFrecencyArray(mOldRecord); 1.84 + mIndex->RemoveRecordFromExpirationArray(mOldRecord); 1.85 + mIndex->RemoveRecordFromIterators(mOldRecord); 1.86 + } else if (entry && mOldRecord) { 1.87 + bool replaceFrecency = false; 1.88 + bool replaceExpiration = false; 1.89 + 1.90 + if (entry->mRec != mOldRecord) { 1.91 + // record has a different address, we have to replace it 1.92 + replaceFrecency = replaceExpiration = true; 1.93 + mIndex->ReplaceRecordInIterators(mOldRecord, entry->mRec); 1.94 + } else { 1.95 + if (entry->mRec->mFrecency == 0 && 1.96 + entry->mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME) { 1.97 + // This is a special case when we want to make sure that the entry is 1.98 + // placed at the end of the lists even when the values didn't change. 1.99 + replaceFrecency = replaceExpiration = true; 1.100 + } else { 1.101 + if (entry->mRec->mFrecency != mOldFrecency) { 1.102 + replaceFrecency = true; 1.103 + } 1.104 + if (entry->mRec->mExpirationTime != mOldExpirationTime) { 1.105 + replaceExpiration = true; 1.106 + } 1.107 + } 1.108 + } 1.109 + 1.110 + if (replaceFrecency) { 1.111 + mIndex->RemoveRecordFromFrecencyArray(mOldRecord); 1.112 + mIndex->InsertRecordToFrecencyArray(entry->mRec); 1.113 + } 1.114 + if (replaceExpiration) { 1.115 + mIndex->RemoveRecordFromExpirationArray(mOldRecord); 1.116 + mIndex->InsertRecordToExpirationArray(entry->mRec); 1.117 + } 1.118 + } else { 1.119 + // both entries were removed or not initialized, do nothing 1.120 + } 1.121 + } 1.122 + 1.123 + // We cannot rely on nsTHashtable::GetEntry() in case we are enumerating the 1.124 + // entries and returning PL_DHASH_REMOVE. Destructor is called before the 1.125 + // entry is removed. Caller must call one of following methods to skip 1.126 + // lookup in the hashtable. 1.127 + void DoNotSearchInIndex() { mDoNotSearchInIndex = true; } 1.128 + void DoNotSearchInUpdates() { mDoNotSearchInUpdates = true; } 1.129 + 1.130 +private: 1.131 + CacheIndexEntry * FindEntry() 1.132 + { 1.133 + CacheIndexEntry *entry = nullptr; 1.134 + 1.135 + switch (mIndex->mState) { 1.136 + case CacheIndex::READING: 1.137 + case CacheIndex::WRITING: 1.138 + if (!mDoNotSearchInUpdates) { 1.139 + entry = mIndex->mPendingUpdates.GetEntry(*mHash); 1.140 + } 1.141 + // no break 1.142 + case CacheIndex::BUILDING: 1.143 + case CacheIndex::UPDATING: 1.144 + case CacheIndex::READY: 1.145 + if (!entry && !mDoNotSearchInIndex) { 1.146 + entry = mIndex->mIndex.GetEntry(*mHash); 1.147 + } 1.148 + break; 1.149 + case CacheIndex::INITIAL: 1.150 + case CacheIndex::SHUTDOWN: 1.151 + default: 1.152 + MOZ_ASSERT(false, "Unexpected state!"); 1.153 + } 1.154 + 1.155 + return entry; 1.156 + } 1.157 + 1.158 + const SHA1Sum::Hash *mHash; 1.159 + nsRefPtr<CacheIndex> mIndex; 1.160 + CacheIndexRecord *mOldRecord; 1.161 + uint32_t mOldFrecency; 1.162 + uint32_t mOldExpirationTime; 1.163 + bool mDoNotSearchInIndex; 1.164 + bool mDoNotSearchInUpdates; 1.165 +}; 1.166 + 1.167 +class FileOpenHelper : public CacheFileIOListener 1.168 +{ 1.169 +public: 1.170 + NS_DECL_THREADSAFE_ISUPPORTS 1.171 + 1.172 + FileOpenHelper(CacheIndex* aIndex) 1.173 + : mIndex(aIndex) 1.174 + , mCanceled(false) 1.175 + {} 1.176 + 1.177 + virtual ~FileOpenHelper() {} 1.178 + 1.179 + void Cancel() { 1.180 + mIndex->AssertOwnsLock(); 1.181 + mCanceled = true; 1.182 + } 1.183 + 1.184 +private: 1.185 + NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult); 1.186 + NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, 1.187 + nsresult aResult) { 1.188 + MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!"); 1.189 + return NS_ERROR_UNEXPECTED; 1.190 + } 1.191 + NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, 1.192 + nsresult aResult) { 1.193 + MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!"); 1.194 + return NS_ERROR_UNEXPECTED; 1.195 + } 1.196 + NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) { 1.197 + MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!"); 1.198 + return NS_ERROR_UNEXPECTED; 1.199 + } 1.200 + NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) { 1.201 + MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!"); 1.202 + return NS_ERROR_UNEXPECTED; 1.203 + } 1.204 + NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) { 1.205 + MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!"); 1.206 + return NS_ERROR_UNEXPECTED; 1.207 + } 1.208 + 1.209 + nsRefPtr<CacheIndex> mIndex; 1.210 + bool mCanceled; 1.211 +}; 1.212 + 1.213 +NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle, 1.214 + nsresult aResult) 1.215 +{ 1.216 + CacheIndexAutoLock lock(mIndex); 1.217 + 1.218 + if (mCanceled) { 1.219 + if (aHandle) { 1.220 + CacheFileIOManager::DoomFile(aHandle, nullptr); 1.221 + } 1.222 + 1.223 + return NS_OK; 1.224 + } 1.225 + 1.226 + mIndex->OnFileOpenedInternal(this, aHandle, aResult); 1.227 + 1.228 + return NS_OK; 1.229 +} 1.230 + 1.231 +NS_IMPL_ISUPPORTS(FileOpenHelper, CacheFileIOListener); 1.232 + 1.233 + 1.234 +CacheIndex * CacheIndex::gInstance = nullptr; 1.235 + 1.236 + 1.237 +NS_IMPL_ADDREF(CacheIndex) 1.238 +NS_IMPL_RELEASE(CacheIndex) 1.239 + 1.240 +NS_INTERFACE_MAP_BEGIN(CacheIndex) 1.241 + NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener) 1.242 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.243 +NS_INTERFACE_MAP_END_THREADSAFE 1.244 + 1.245 + 1.246 +CacheIndex::CacheIndex() 1.247 + : mLock("CacheFile.mLock") 1.248 + , mState(INITIAL) 1.249 + , mShuttingDown(false) 1.250 + , mIndexNeedsUpdate(false) 1.251 + , mRemovingAll(false) 1.252 + , mIndexOnDiskIsValid(false) 1.253 + , mDontMarkIndexClean(false) 1.254 + , mIndexTimeStamp(0) 1.255 + , mUpdateEventPending(false) 1.256 + , mSkipEntries(0) 1.257 + , mProcessEntries(0) 1.258 + , mRWBuf(nullptr) 1.259 + , mRWBufSize(0) 1.260 + , mRWBufPos(0) 1.261 + , mJournalReadSuccessfully(false) 1.262 +{ 1.263 + LOG(("CacheIndex::CacheIndex [this=%p]", this)); 1.264 + MOZ_COUNT_CTOR(CacheIndex); 1.265 + MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!"); 1.266 +} 1.267 + 1.268 +CacheIndex::~CacheIndex() 1.269 +{ 1.270 + LOG(("CacheIndex::~CacheIndex [this=%p]", this)); 1.271 + MOZ_COUNT_DTOR(CacheIndex); 1.272 + 1.273 + ReleaseBuffer(); 1.274 +} 1.275 + 1.276 +void 1.277 +CacheIndex::Lock() 1.278 +{ 1.279 + mLock.Lock(); 1.280 + 1.281 + MOZ_ASSERT(!mIndexStats.StateLogged()); 1.282 +} 1.283 + 1.284 +void 1.285 +CacheIndex::Unlock() 1.286 +{ 1.287 + MOZ_ASSERT(!mIndexStats.StateLogged()); 1.288 + 1.289 + mLock.Unlock(); 1.290 +} 1.291 + 1.292 +inline void 1.293 +CacheIndex::AssertOwnsLock() 1.294 +{ 1.295 + mLock.AssertCurrentThreadOwns(); 1.296 +} 1.297 + 1.298 +// static 1.299 +nsresult 1.300 +CacheIndex::Init(nsIFile *aCacheDirectory) 1.301 +{ 1.302 + LOG(("CacheIndex::Init()")); 1.303 + 1.304 + MOZ_ASSERT(NS_IsMainThread()); 1.305 + 1.306 + if (gInstance) { 1.307 + return NS_ERROR_ALREADY_INITIALIZED; 1.308 + } 1.309 + 1.310 + nsRefPtr<CacheIndex> idx = new CacheIndex(); 1.311 + 1.312 + CacheIndexAutoLock lock(idx); 1.313 + 1.314 + nsresult rv = idx->InitInternal(aCacheDirectory); 1.315 + NS_ENSURE_SUCCESS(rv, rv); 1.316 + 1.317 + idx.swap(gInstance); 1.318 + return NS_OK; 1.319 +} 1.320 + 1.321 +nsresult 1.322 +CacheIndex::InitInternal(nsIFile *aCacheDirectory) 1.323 +{ 1.324 + nsresult rv; 1.325 + 1.326 + rv = aCacheDirectory->Clone(getter_AddRefs(mCacheDirectory)); 1.327 + NS_ENSURE_SUCCESS(rv, rv); 1.328 + 1.329 + mStartTime = TimeStamp::NowLoRes(); 1.330 + 1.331 + ReadIndexFromDisk(); 1.332 + 1.333 + return NS_OK; 1.334 +} 1.335 + 1.336 +// static 1.337 +nsresult 1.338 +CacheIndex::PreShutdown() 1.339 +{ 1.340 + LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance)); 1.341 + 1.342 + MOZ_ASSERT(NS_IsMainThread()); 1.343 + 1.344 + nsresult rv; 1.345 + nsRefPtr<CacheIndex> index = gInstance; 1.346 + 1.347 + if (!index) { 1.348 + return NS_ERROR_NOT_INITIALIZED; 1.349 + } 1.350 + 1.351 + CacheIndexAutoLock lock(index); 1.352 + 1.353 + LOG(("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, " 1.354 + "dontMarkIndexClean=%d]", index->mState, index->mIndexOnDiskIsValid, 1.355 + index->mDontMarkIndexClean)); 1.356 + 1.357 + LOG(("CacheIndex::PreShutdown() - Closing iterators.")); 1.358 + for (uint32_t i = 0; i < index->mIterators.Length(); ) { 1.359 + rv = index->mIterators[i]->CloseInternal(NS_ERROR_FAILURE); 1.360 + if (NS_FAILED(rv)) { 1.361 + // CacheIndexIterator::CloseInternal() removes itself from mIteratos iff 1.362 + // it returns success. 1.363 + LOG(("CacheIndex::PreShutdown() - Failed to remove iterator %p. " 1.364 + "[rv=0x%08x]", rv)); 1.365 + i++; 1.366 + } 1.367 + } 1.368 + 1.369 + index->mShuttingDown = true; 1.370 + 1.371 + if (index->mState == READY) { 1.372 + return NS_OK; // nothing to do 1.373 + } 1.374 + 1.375 + nsCOMPtr<nsIRunnable> event; 1.376 + event = NS_NewRunnableMethod(index, &CacheIndex::PreShutdownInternal); 1.377 + 1.378 + nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget(); 1.379 + MOZ_ASSERT(ioTarget); 1.380 + 1.381 + // PreShutdownInternal() will be executed before any queued event on INDEX 1.382 + // level. That's OK since we don't want to wait for any operation in progess. 1.383 + // We need to interrupt it and save journal as quickly as possible. 1.384 + rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL); 1.385 + if (NS_FAILED(rv)) { 1.386 + NS_WARNING("CacheIndex::PreShutdown() - Can't dispatch event"); 1.387 + LOG(("CacheIndex::PreShutdown() - Can't dispatch event" )); 1.388 + return rv; 1.389 + } 1.390 + 1.391 + return NS_OK; 1.392 +} 1.393 + 1.394 +void 1.395 +CacheIndex::PreShutdownInternal() 1.396 +{ 1.397 + CacheIndexAutoLock lock(this); 1.398 + 1.399 + LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, " 1.400 + "dontMarkIndexClean=%d]", mState, mIndexOnDiskIsValid, 1.401 + mDontMarkIndexClean)); 1.402 + 1.403 + MOZ_ASSERT(mShuttingDown); 1.404 + 1.405 + if (mUpdateTimer) { 1.406 + mUpdateTimer = nullptr; 1.407 + } 1.408 + 1.409 + switch (mState) { 1.410 + case WRITING: 1.411 + FinishWrite(false); 1.412 + break; 1.413 + case READY: 1.414 + // nothing to do, write the journal in Shutdown() 1.415 + break; 1.416 + case READING: 1.417 + FinishRead(false); 1.418 + break; 1.419 + case BUILDING: 1.420 + case UPDATING: 1.421 + FinishUpdate(false); 1.422 + break; 1.423 + default: 1.424 + MOZ_ASSERT(false, "Implement me!"); 1.425 + } 1.426 + 1.427 + // We should end up in READY state 1.428 + MOZ_ASSERT(mState == READY); 1.429 +} 1.430 + 1.431 +// static 1.432 +nsresult 1.433 +CacheIndex::Shutdown() 1.434 +{ 1.435 + LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance)); 1.436 + 1.437 + MOZ_ASSERT(NS_IsMainThread()); 1.438 + 1.439 + nsRefPtr<CacheIndex> index; 1.440 + index.swap(gInstance); 1.441 + 1.442 + if (!index) { 1.443 + return NS_ERROR_NOT_INITIALIZED; 1.444 + } 1.445 + 1.446 + CacheIndexAutoLock lock(index); 1.447 + 1.448 + bool sanitize = CacheObserver::ClearCacheOnShutdown(); 1.449 + 1.450 + LOG(("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, " 1.451 + "dontMarkIndexClean=%d, sanitize=%d]", index->mState, 1.452 + index->mIndexOnDiskIsValid, index->mDontMarkIndexClean, sanitize)); 1.453 + 1.454 + MOZ_ASSERT(index->mShuttingDown); 1.455 + 1.456 + EState oldState = index->mState; 1.457 + index->ChangeState(SHUTDOWN); 1.458 + 1.459 + if (oldState != READY) { 1.460 + LOG(("CacheIndex::Shutdown() - Unexpected state. Did posting of " 1.461 + "PreShutdownInternal() fail?")); 1.462 + } 1.463 + 1.464 + switch (oldState) { 1.465 + case WRITING: 1.466 + index->FinishWrite(false); 1.467 + // no break 1.468 + case READY: 1.469 + if (index->mIndexOnDiskIsValid && !index->mDontMarkIndexClean) { 1.470 + if (!sanitize && NS_FAILED(index->WriteLogToDisk())) { 1.471 + index->RemoveIndexFromDisk(); 1.472 + } 1.473 + } else { 1.474 + index->RemoveIndexFromDisk(); 1.475 + } 1.476 + break; 1.477 + case READING: 1.478 + index->FinishRead(false); 1.479 + break; 1.480 + case BUILDING: 1.481 + case UPDATING: 1.482 + index->FinishUpdate(false); 1.483 + break; 1.484 + default: 1.485 + MOZ_ASSERT(false, "Unexpected state!"); 1.486 + } 1.487 + 1.488 + if (sanitize) { 1.489 + index->RemoveIndexFromDisk(); 1.490 + } 1.491 + 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +// static 1.496 +nsresult 1.497 +CacheIndex::AddEntry(const SHA1Sum::Hash *aHash) 1.498 +{ 1.499 + LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash))); 1.500 + 1.501 + nsRefPtr<CacheIndex> index = gInstance; 1.502 + 1.503 + if (!index) { 1.504 + return NS_ERROR_NOT_INITIALIZED; 1.505 + } 1.506 + 1.507 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.508 + 1.509 + CacheIndexAutoLock lock(index); 1.510 + 1.511 + if (!index->IsIndexUsable()) { 1.512 + return NS_ERROR_NOT_AVAILABLE; 1.513 + } 1.514 + 1.515 + // Getters in CacheIndexStats assert when mStateLogged is true since the 1.516 + // information is incomplete between calls to BeforeChange() and AfterChange() 1.517 + // (i.e. while CacheIndexEntryAutoManage exists). We need to check whether 1.518 + // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage. 1.519 + bool updateIfNonFreshEntriesExist = false; 1.520 + 1.521 + { 1.522 + CacheIndexEntryAutoManage entryMng(aHash, index); 1.523 + 1.524 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash); 1.525 + bool entryRemoved = entry && entry->IsRemoved(); 1.526 + 1.527 + if (index->mState == READY || index->mState == UPDATING || 1.528 + index->mState == BUILDING) { 1.529 + MOZ_ASSERT(index->mPendingUpdates.Count() == 0); 1.530 + 1.531 + if (entry && !entryRemoved) { 1.532 + // Found entry in index that shouldn't exist. 1.533 + 1.534 + if (entry->IsFresh()) { 1.535 + // Someone removed the file on disk while FF is running. Update 1.536 + // process can fix only non-fresh entries (i.e. entries that were not 1.537 + // added within this session). Start update only if we have such 1.538 + // entries. 1.539 + // 1.540 + // TODO: This should be very rare problem. If it turns out not to be 1.541 + // true, change the update process so that it also iterates all 1.542 + // initialized non-empty entries and checks whether the file exists. 1.543 + 1.544 + LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF " 1.545 + "process!")); 1.546 + 1.547 + updateIfNonFreshEntriesExist = true; 1.548 + } else if (index->mState == READY) { 1.549 + // Index is outdated, update it. 1.550 + LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, " 1.551 + "update is needed")); 1.552 + index->mIndexNeedsUpdate = true; 1.553 + } else { 1.554 + // We cannot be here when building index since all entries are fresh 1.555 + // during building. 1.556 + MOZ_ASSERT(index->mState == UPDATING); 1.557 + } 1.558 + } 1.559 + 1.560 + if (!entry) { 1.561 + entry = index->mIndex.PutEntry(*aHash); 1.562 + } 1.563 + } else { // WRITING, READING 1.564 + CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash); 1.565 + bool updatedRemoved = updated && updated->IsRemoved(); 1.566 + 1.567 + if ((updated && !updatedRemoved) || 1.568 + (!updated && entry && !entryRemoved && entry->IsFresh())) { 1.569 + // Fresh entry found, so the file was removed outside FF 1.570 + LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF " 1.571 + "process!")); 1.572 + 1.573 + updateIfNonFreshEntriesExist = true; 1.574 + } else if (!updated && entry && !entryRemoved) { 1.575 + if (index->mState == WRITING) { 1.576 + LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, " 1.577 + "update is needed")); 1.578 + index->mIndexNeedsUpdate = true; 1.579 + } 1.580 + // Ignore if state is READING since the index information is partial 1.581 + } 1.582 + 1.583 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.584 + entry = updated; 1.585 + } 1.586 + 1.587 + entry->InitNew(); 1.588 + entry->MarkDirty(); 1.589 + entry->MarkFresh(); 1.590 + } 1.591 + 1.592 + if (updateIfNonFreshEntriesExist && 1.593 + index->mIndexStats.Count() != index->mIndexStats.Fresh()) { 1.594 + index->mIndexNeedsUpdate = true; 1.595 + } 1.596 + 1.597 + index->StartUpdatingIndexIfNeeded(); 1.598 + index->WriteIndexToDiskIfNeeded(); 1.599 + 1.600 + return NS_OK; 1.601 +} 1.602 + 1.603 +// static 1.604 +nsresult 1.605 +CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash) 1.606 +{ 1.607 + LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]", 1.608 + LOGSHA1(aHash))); 1.609 + 1.610 + nsRefPtr<CacheIndex> index = gInstance; 1.611 + 1.612 + if (!index) { 1.613 + return NS_ERROR_NOT_INITIALIZED; 1.614 + } 1.615 + 1.616 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.617 + 1.618 + CacheIndexAutoLock lock(index); 1.619 + 1.620 + if (!index->IsIndexUsable()) { 1.621 + return NS_ERROR_NOT_AVAILABLE; 1.622 + } 1.623 + 1.624 + { 1.625 + CacheIndexEntryAutoManage entryMng(aHash, index); 1.626 + 1.627 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash); 1.628 + bool entryRemoved = entry && entry->IsRemoved(); 1.629 + 1.630 + if (index->mState == READY || index->mState == UPDATING || 1.631 + index->mState == BUILDING) { 1.632 + MOZ_ASSERT(index->mPendingUpdates.Count() == 0); 1.633 + 1.634 + if (!entry || entryRemoved) { 1.635 + if (entryRemoved && entry->IsFresh()) { 1.636 + // This could happen only if somebody copies files to the entries 1.637 + // directory while FF is running. 1.638 + LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside " 1.639 + "FF process! Update is needed.")); 1.640 + index->mIndexNeedsUpdate = true; 1.641 + } else if (index->mState == READY || 1.642 + (entryRemoved && !entry->IsFresh())) { 1.643 + // Removed non-fresh entries can be present as a result of 1.644 + // ProcessJournalEntry() 1.645 + LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should" 1.646 + " exist, update is needed")); 1.647 + index->mIndexNeedsUpdate = true; 1.648 + } 1.649 + 1.650 + if (!entry) { 1.651 + entry = index->mIndex.PutEntry(*aHash); 1.652 + } 1.653 + entry->InitNew(); 1.654 + entry->MarkDirty(); 1.655 + } 1.656 + entry->MarkFresh(); 1.657 + } else { // WRITING, READING 1.658 + CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash); 1.659 + bool updatedRemoved = updated && updated->IsRemoved(); 1.660 + 1.661 + if (updatedRemoved || 1.662 + (!updated && entryRemoved && entry->IsFresh())) { 1.663 + // Fresh information about missing entry found. This could happen only 1.664 + // if somebody copies files to the entries directory while FF is running. 1.665 + LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside " 1.666 + "FF process! Update is needed.")); 1.667 + index->mIndexNeedsUpdate = true; 1.668 + } else if (!updated && (!entry || entryRemoved)) { 1.669 + if (index->mState == WRITING) { 1.670 + LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should" 1.671 + " exist, update is needed")); 1.672 + index->mIndexNeedsUpdate = true; 1.673 + } 1.674 + // Ignore if state is READING since the index information is partial 1.675 + } 1.676 + 1.677 + // We don't need entryRemoved and updatedRemoved info anymore 1.678 + if (entryRemoved) entry = nullptr; 1.679 + if (updatedRemoved) updated = nullptr; 1.680 + 1.681 + if (updated) { 1.682 + updated->MarkFresh(); 1.683 + } else { 1.684 + if (!entry) { 1.685 + // Create a new entry 1.686 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.687 + updated->InitNew(); 1.688 + updated->MarkFresh(); 1.689 + updated->MarkDirty(); 1.690 + } else { 1.691 + if (!entry->IsFresh()) { 1.692 + // To mark the entry fresh we must make a copy of index entry 1.693 + // since the index is read-only. 1.694 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.695 + *updated = *entry; 1.696 + updated->MarkFresh(); 1.697 + } 1.698 + } 1.699 + } 1.700 + } 1.701 + } 1.702 + 1.703 + index->StartUpdatingIndexIfNeeded(); 1.704 + index->WriteIndexToDiskIfNeeded(); 1.705 + 1.706 + return NS_OK; 1.707 +} 1.708 + 1.709 +// static 1.710 +nsresult 1.711 +CacheIndex::InitEntry(const SHA1Sum::Hash *aHash, 1.712 + uint32_t aAppId, 1.713 + bool aAnonymous, 1.714 + bool aInBrowser) 1.715 +{ 1.716 + LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, " 1.717 + "anonymous=%d, inBrowser=%d]", LOGSHA1(aHash), aAppId, aAnonymous, 1.718 + aInBrowser)); 1.719 + 1.720 + nsRefPtr<CacheIndex> index = gInstance; 1.721 + 1.722 + if (!index) { 1.723 + return NS_ERROR_NOT_INITIALIZED; 1.724 + } 1.725 + 1.726 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.727 + 1.728 + CacheIndexAutoLock lock(index); 1.729 + 1.730 + if (!index->IsIndexUsable()) { 1.731 + return NS_ERROR_NOT_AVAILABLE; 1.732 + } 1.733 + 1.734 + { 1.735 + CacheIndexEntryAutoManage entryMng(aHash, index); 1.736 + 1.737 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash); 1.738 + bool reinitEntry = false; 1.739 + 1.740 + if (entry && entry->IsRemoved()) { 1.741 + entry = nullptr; 1.742 + } 1.743 + 1.744 + if (index->mState == READY || index->mState == UPDATING || 1.745 + index->mState == BUILDING) { 1.746 + MOZ_ASSERT(index->mPendingUpdates.Count() == 0); 1.747 + MOZ_ASSERT(entry); 1.748 + MOZ_ASSERT(entry->IsFresh()); 1.749 + 1.750 + if (IsCollision(entry, aAppId, aAnonymous, aInBrowser)) { 1.751 + index->mIndexNeedsUpdate = true; // TODO Does this really help in case of collision? 1.752 + reinitEntry = true; 1.753 + } else { 1.754 + if (entry->IsInitialized()) { 1.755 + return NS_OK; 1.756 + } 1.757 + } 1.758 + } else { 1.759 + CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash); 1.760 + DebugOnly<bool> removed = updated && updated->IsRemoved(); 1.761 + 1.762 + MOZ_ASSERT(updated || !removed); 1.763 + MOZ_ASSERT(updated || entry); 1.764 + 1.765 + if (updated) { 1.766 + MOZ_ASSERT(updated->IsFresh()); 1.767 + 1.768 + if (IsCollision(updated, aAppId, aAnonymous, aInBrowser)) { 1.769 + index->mIndexNeedsUpdate = true; 1.770 + reinitEntry = true; 1.771 + } else { 1.772 + if (updated->IsInitialized()) { 1.773 + return NS_OK; 1.774 + } 1.775 + } 1.776 + entry = updated; 1.777 + } else { 1.778 + MOZ_ASSERT(entry->IsFresh()); 1.779 + 1.780 + if (IsCollision(entry, aAppId, aAnonymous, aInBrowser)) { 1.781 + index->mIndexNeedsUpdate = true; 1.782 + reinitEntry = true; 1.783 + } else { 1.784 + if (entry->IsInitialized()) { 1.785 + return NS_OK; 1.786 + } 1.787 + } 1.788 + 1.789 + // make a copy of a read-only entry 1.790 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.791 + *updated = *entry; 1.792 + entry = updated; 1.793 + } 1.794 + } 1.795 + 1.796 + if (reinitEntry) { 1.797 + // There is a collision and we are going to rewrite this entry. Initialize 1.798 + // it as a new entry. 1.799 + entry->InitNew(); 1.800 + entry->MarkFresh(); 1.801 + } 1.802 + entry->Init(aAppId, aAnonymous, aInBrowser); 1.803 + entry->MarkDirty(); 1.804 + } 1.805 + 1.806 + index->StartUpdatingIndexIfNeeded(); 1.807 + index->WriteIndexToDiskIfNeeded(); 1.808 + 1.809 + return NS_OK; 1.810 +} 1.811 + 1.812 +// static 1.813 +nsresult 1.814 +CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash) 1.815 +{ 1.816 + LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]", 1.817 + LOGSHA1(aHash))); 1.818 + 1.819 + nsRefPtr<CacheIndex> index = gInstance; 1.820 + 1.821 + if (!index) { 1.822 + return NS_ERROR_NOT_INITIALIZED; 1.823 + } 1.824 + 1.825 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.826 + 1.827 + CacheIndexAutoLock lock(index); 1.828 + 1.829 + if (!index->IsIndexUsable()) { 1.830 + return NS_ERROR_NOT_AVAILABLE; 1.831 + } 1.832 + 1.833 + { 1.834 + CacheIndexEntryAutoManage entryMng(aHash, index); 1.835 + 1.836 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash); 1.837 + bool entryRemoved = entry && entry->IsRemoved(); 1.838 + 1.839 + if (index->mState == READY || index->mState == UPDATING || 1.840 + index->mState == BUILDING) { 1.841 + MOZ_ASSERT(index->mPendingUpdates.Count() == 0); 1.842 + 1.843 + if (!entry || entryRemoved) { 1.844 + if (entryRemoved && entry->IsFresh()) { 1.845 + // This could happen only if somebody copies files to the entries 1.846 + // directory while FF is running. 1.847 + LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF " 1.848 + "process! Update is needed.")); 1.849 + index->mIndexNeedsUpdate = true; 1.850 + } else if (index->mState == READY || 1.851 + (entryRemoved && !entry->IsFresh())) { 1.852 + // Removed non-fresh entries can be present as a result of 1.853 + // ProcessJournalEntry() 1.854 + LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist" 1.855 + ", update is needed")); 1.856 + index->mIndexNeedsUpdate = true; 1.857 + } 1.858 + } else { 1.859 + if (entry) { 1.860 + if (!entry->IsDirty() && entry->IsFileEmpty()) { 1.861 + index->mIndex.RemoveEntry(*aHash); 1.862 + entry = nullptr; 1.863 + } else { 1.864 + entry->MarkRemoved(); 1.865 + entry->MarkDirty(); 1.866 + entry->MarkFresh(); 1.867 + } 1.868 + } 1.869 + } 1.870 + } else { // WRITING, READING 1.871 + CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash); 1.872 + bool updatedRemoved = updated && updated->IsRemoved(); 1.873 + 1.874 + if (updatedRemoved || 1.875 + (!updated && entryRemoved && entry->IsFresh())) { 1.876 + // Fresh information about missing entry found. This could happen only 1.877 + // if somebody copies files to the entries directory while FF is running. 1.878 + LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF " 1.879 + "process! Update is needed.")); 1.880 + index->mIndexNeedsUpdate = true; 1.881 + } else if (!updated && (!entry || entryRemoved)) { 1.882 + if (index->mState == WRITING) { 1.883 + LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist" 1.884 + ", update is needed")); 1.885 + index->mIndexNeedsUpdate = true; 1.886 + } 1.887 + // Ignore if state is READING since the index information is partial 1.888 + } 1.889 + 1.890 + if (!updated) { 1.891 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.892 + updated->InitNew(); 1.893 + } 1.894 + 1.895 + updated->MarkRemoved(); 1.896 + updated->MarkDirty(); 1.897 + updated->MarkFresh(); 1.898 + } 1.899 + } 1.900 + 1.901 + index->StartUpdatingIndexIfNeeded(); 1.902 + index->WriteIndexToDiskIfNeeded(); 1.903 + 1.904 + return NS_OK; 1.905 +} 1.906 + 1.907 +// static 1.908 +nsresult 1.909 +CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash, 1.910 + const uint32_t *aFrecency, 1.911 + const uint32_t *aExpirationTime, 1.912 + const uint32_t *aSize) 1.913 +{ 1.914 + LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, " 1.915 + "frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash), 1.916 + aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "", 1.917 + aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "", 1.918 + aSize ? nsPrintfCString("%u", *aSize).get() : "")); 1.919 + 1.920 + nsRefPtr<CacheIndex> index = gInstance; 1.921 + 1.922 + if (!index) { 1.923 + return NS_ERROR_NOT_INITIALIZED; 1.924 + } 1.925 + 1.926 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.927 + 1.928 + CacheIndexAutoLock lock(index); 1.929 + 1.930 + if (!index->IsIndexUsable()) { 1.931 + return NS_ERROR_NOT_AVAILABLE; 1.932 + } 1.933 + 1.934 + { 1.935 + CacheIndexEntryAutoManage entryMng(aHash, index); 1.936 + 1.937 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash); 1.938 + 1.939 + if (entry && entry->IsRemoved()) { 1.940 + entry = nullptr; 1.941 + } 1.942 + 1.943 + if (index->mState == READY || index->mState == UPDATING || 1.944 + index->mState == BUILDING) { 1.945 + MOZ_ASSERT(index->mPendingUpdates.Count() == 0); 1.946 + MOZ_ASSERT(entry); 1.947 + 1.948 + if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) { 1.949 + return NS_OK; 1.950 + } 1.951 + } else { 1.952 + CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash); 1.953 + DebugOnly<bool> removed = updated && updated->IsRemoved(); 1.954 + 1.955 + MOZ_ASSERT(updated || !removed); 1.956 + MOZ_ASSERT(updated || entry); 1.957 + 1.958 + if (!updated) { 1.959 + if (entry && 1.960 + HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) { 1.961 + // make a copy of a read-only entry 1.962 + updated = index->mPendingUpdates.PutEntry(*aHash); 1.963 + *updated = *entry; 1.964 + entry = updated; 1.965 + } else { 1.966 + return NS_ERROR_NOT_AVAILABLE; 1.967 + } 1.968 + } else { 1.969 + entry = updated; 1.970 + } 1.971 + } 1.972 + 1.973 + MOZ_ASSERT(entry->IsFresh()); 1.974 + MOZ_ASSERT(entry->IsInitialized()); 1.975 + entry->MarkDirty(); 1.976 + 1.977 + if (aFrecency) { 1.978 + entry->SetFrecency(*aFrecency); 1.979 + } 1.980 + 1.981 + if (aExpirationTime) { 1.982 + entry->SetExpirationTime(*aExpirationTime); 1.983 + } 1.984 + 1.985 + if (aSize) { 1.986 + entry->SetFileSize(*aSize); 1.987 + } 1.988 + } 1.989 + 1.990 + index->WriteIndexToDiskIfNeeded(); 1.991 + 1.992 + return NS_OK; 1.993 +} 1.994 + 1.995 +// static 1.996 +nsresult 1.997 +CacheIndex::RemoveAll() 1.998 +{ 1.999 + LOG(("CacheIndex::RemoveAll()")); 1.1000 + 1.1001 + nsRefPtr<CacheIndex> index = gInstance; 1.1002 + 1.1003 + if (!index) { 1.1004 + return NS_ERROR_NOT_INITIALIZED; 1.1005 + } 1.1006 + 1.1007 + MOZ_ASSERT(CacheFileIOManager::IsOnIOThread()); 1.1008 + 1.1009 + nsCOMPtr<nsIFile> file; 1.1010 + 1.1011 + { 1.1012 + CacheIndexAutoLock lock(index); 1.1013 + 1.1014 + MOZ_ASSERT(!index->mRemovingAll); 1.1015 + 1.1016 + if (!index->IsIndexUsable()) { 1.1017 + return NS_ERROR_NOT_AVAILABLE; 1.1018 + } 1.1019 + 1.1020 + AutoRestore<bool> saveRemovingAll(index->mRemovingAll); 1.1021 + index->mRemovingAll = true; 1.1022 + 1.1023 + // Doom index and journal handles but don't null them out since this will be 1.1024 + // done in FinishWrite/FinishRead methods. 1.1025 + if (index->mIndexHandle) { 1.1026 + CacheFileIOManager::DoomFile(index->mIndexHandle, nullptr); 1.1027 + } else { 1.1028 + // We don't have a handle to index file, so get the file here, but delete 1.1029 + // it outside the lock. Ignore the result since this is not fatal. 1.1030 + index->GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(file)); 1.1031 + } 1.1032 + 1.1033 + if (index->mJournalHandle) { 1.1034 + CacheFileIOManager::DoomFile(index->mJournalHandle, nullptr); 1.1035 + } 1.1036 + 1.1037 + switch (index->mState) { 1.1038 + case WRITING: 1.1039 + index->FinishWrite(false); 1.1040 + break; 1.1041 + case READY: 1.1042 + // nothing to do 1.1043 + break; 1.1044 + case READING: 1.1045 + index->FinishRead(false); 1.1046 + break; 1.1047 + case BUILDING: 1.1048 + case UPDATING: 1.1049 + index->FinishUpdate(false); 1.1050 + break; 1.1051 + default: 1.1052 + MOZ_ASSERT(false, "Unexpected state!"); 1.1053 + } 1.1054 + 1.1055 + // We should end up in READY state 1.1056 + MOZ_ASSERT(index->mState == READY); 1.1057 + 1.1058 + // There should not be any handle 1.1059 + MOZ_ASSERT(!index->mIndexHandle); 1.1060 + MOZ_ASSERT(!index->mJournalHandle); 1.1061 + 1.1062 + index->mIndexOnDiskIsValid = false; 1.1063 + index->mIndexNeedsUpdate = false; 1.1064 + 1.1065 + index->mIndexStats.Clear(); 1.1066 + index->mFrecencyArray.Clear(); 1.1067 + index->mExpirationArray.Clear(); 1.1068 + index->mIndex.Clear(); 1.1069 + } 1.1070 + 1.1071 + if (file) { 1.1072 + // Ignore the result. The file might not exist and the failure is not fatal. 1.1073 + file->Remove(false); 1.1074 + } 1.1075 + 1.1076 + return NS_OK; 1.1077 +} 1.1078 + 1.1079 +// static 1.1080 +nsresult 1.1081 +CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval) 1.1082 +{ 1.1083 + LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey).get())); 1.1084 + 1.1085 + nsRefPtr<CacheIndex> index = gInstance; 1.1086 + 1.1087 + if (!index) { 1.1088 + return NS_ERROR_NOT_INITIALIZED; 1.1089 + } 1.1090 + 1.1091 + CacheIndexAutoLock lock(index); 1.1092 + 1.1093 + if (!index->IsIndexUsable()) { 1.1094 + return NS_ERROR_NOT_AVAILABLE; 1.1095 + } 1.1096 + 1.1097 + SHA1Sum sum; 1.1098 + SHA1Sum::Hash hash; 1.1099 + sum.update(aKey.BeginReading(), aKey.Length()); 1.1100 + sum.finish(hash); 1.1101 + 1.1102 + CacheIndexEntry *entry = nullptr; 1.1103 + 1.1104 + switch (index->mState) { 1.1105 + case READING: 1.1106 + case WRITING: 1.1107 + entry = index->mPendingUpdates.GetEntry(hash); 1.1108 + // no break 1.1109 + case BUILDING: 1.1110 + case UPDATING: 1.1111 + case READY: 1.1112 + if (!entry) { 1.1113 + entry = index->mIndex.GetEntry(hash); 1.1114 + } 1.1115 + break; 1.1116 + case INITIAL: 1.1117 + case SHUTDOWN: 1.1118 + MOZ_ASSERT(false, "Unexpected state!"); 1.1119 + } 1.1120 + 1.1121 + if (!entry) { 1.1122 + if (index->mState == READY || index->mState == WRITING) { 1.1123 + *_retval = DOES_NOT_EXIST; 1.1124 + } else { 1.1125 + *_retval = DO_NOT_KNOW; 1.1126 + } 1.1127 + } else { 1.1128 + if (entry->IsRemoved()) { 1.1129 + if (entry->IsFresh()) { 1.1130 + *_retval = DOES_NOT_EXIST; 1.1131 + } else { 1.1132 + *_retval = DO_NOT_KNOW; 1.1133 + } 1.1134 + } else { 1.1135 + *_retval = EXISTS; 1.1136 + } 1.1137 + } 1.1138 + 1.1139 + LOG(("CacheIndex::HasEntry() - result is %u", *_retval)); 1.1140 + return NS_OK; 1.1141 +} 1.1142 + 1.1143 +// static 1.1144 +nsresult 1.1145 +CacheIndex::GetEntryForEviction(SHA1Sum::Hash *aHash, uint32_t *aCnt) 1.1146 +{ 1.1147 + LOG(("CacheIndex::GetEntryForEviction()")); 1.1148 + 1.1149 + nsRefPtr<CacheIndex> index = gInstance; 1.1150 + 1.1151 + if (!index) 1.1152 + return NS_ERROR_NOT_INITIALIZED; 1.1153 + 1.1154 + CacheIndexAutoLock lock(index); 1.1155 + 1.1156 + if (!index->IsIndexUsable()) { 1.1157 + return NS_ERROR_NOT_AVAILABLE; 1.1158 + } 1.1159 + 1.1160 + MOZ_ASSERT(index->mFrecencyArray.Length() == 1.1161 + index->mExpirationArray.Length()); 1.1162 + 1.1163 + if (index->mExpirationArray.Length() == 0) 1.1164 + return NS_ERROR_NOT_AVAILABLE; 1.1165 + 1.1166 + uint32_t now = PR_Now() / PR_USEC_PER_SEC; 1.1167 + if (index->mExpirationArray[0]->mExpirationTime < now) { 1.1168 + memcpy(aHash, &index->mExpirationArray[0]->mHash, sizeof(SHA1Sum::Hash)); 1.1169 + *aCnt = index->mExpirationArray.Length(); 1.1170 + LOG(("CacheIndex::GetEntryForEviction() - returning entry from expiration " 1.1171 + "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, " 1.1172 + "frecency=%u]", LOGSHA1(aHash), *aCnt, 1.1173 + index->mExpirationArray[0]->mExpirationTime, now, 1.1174 + index->mExpirationArray[0]->mFrecency)); 1.1175 + } 1.1176 + else { 1.1177 + memcpy(aHash, &index->mFrecencyArray[0]->mHash, sizeof(SHA1Sum::Hash)); 1.1178 + *aCnt = index->mFrecencyArray.Length(); 1.1179 + LOG(("CacheIndex::GetEntryForEviction() - returning entry from frecency " 1.1180 + "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, " 1.1181 + "frecency=%u]", LOGSHA1(aHash), *aCnt, 1.1182 + index->mExpirationArray[0]->mExpirationTime, now, 1.1183 + index->mExpirationArray[0]->mFrecency)); 1.1184 + } 1.1185 + 1.1186 + return NS_OK; 1.1187 +} 1.1188 + 1.1189 +// static 1.1190 +nsresult 1.1191 +CacheIndex::GetCacheSize(uint32_t *_retval) 1.1192 +{ 1.1193 + LOG(("CacheIndex::GetCacheSize()")); 1.1194 + 1.1195 + nsRefPtr<CacheIndex> index = gInstance; 1.1196 + 1.1197 + if (!index) 1.1198 + return NS_ERROR_NOT_INITIALIZED; 1.1199 + 1.1200 + CacheIndexAutoLock lock(index); 1.1201 + 1.1202 + if (!index->IsIndexUsable()) { 1.1203 + return NS_ERROR_NOT_AVAILABLE; 1.1204 + } 1.1205 + 1.1206 + *_retval = index->mIndexStats.Size(); 1.1207 + LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval)); 1.1208 + return NS_OK; 1.1209 +} 1.1210 + 1.1211 +// static 1.1212 +nsresult 1.1213 +CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver) 1.1214 +{ 1.1215 + LOG(("CacheIndex::AsyncGetDiskConsumption()")); 1.1216 + 1.1217 + nsRefPtr<CacheIndex> index = gInstance; 1.1218 + 1.1219 + if (!index) { 1.1220 + return NS_ERROR_NOT_INITIALIZED; 1.1221 + } 1.1222 + 1.1223 + CacheIndexAutoLock lock(index); 1.1224 + 1.1225 + if (!index->IsIndexUsable()) { 1.1226 + return NS_ERROR_NOT_AVAILABLE; 1.1227 + } 1.1228 + 1.1229 + nsRefPtr<DiskConsumptionObserver> observer = 1.1230 + DiskConsumptionObserver::Init(aObserver); 1.1231 + 1.1232 + NS_ENSURE_ARG(observer); 1.1233 + 1.1234 + if (index->mState == READY || index->mState == WRITING) { 1.1235 + LOG(("CacheIndex::AsyncGetDiskConsumption - calling immediately")); 1.1236 + // Safe to call the callback under the lock, 1.1237 + // we always post to the main thread. 1.1238 + observer->OnDiskConsumption(index->mIndexStats.Size() << 10); 1.1239 + return NS_OK; 1.1240 + } 1.1241 + 1.1242 + LOG(("CacheIndex::AsyncGetDiskConsumption - remembering callback")); 1.1243 + // Will be called when the index get to the READY state. 1.1244 + index->mDiskConsumptionObservers.AppendElement(observer); 1.1245 + 1.1246 + return NS_OK; 1.1247 +} 1.1248 + 1.1249 +// static 1.1250 +nsresult 1.1251 +CacheIndex::GetIterator(nsILoadContextInfo *aInfo, bool aAddNew, 1.1252 + CacheIndexIterator **_retval) 1.1253 +{ 1.1254 + LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo, aAddNew)); 1.1255 + 1.1256 + nsRefPtr<CacheIndex> index = gInstance; 1.1257 + 1.1258 + if (!index) { 1.1259 + return NS_ERROR_NOT_INITIALIZED; 1.1260 + } 1.1261 + 1.1262 + CacheIndexAutoLock lock(index); 1.1263 + 1.1264 + if (!index->IsIndexUsable()) { 1.1265 + return NS_ERROR_NOT_AVAILABLE; 1.1266 + } 1.1267 + 1.1268 + nsRefPtr<CacheIndexIterator> iter; 1.1269 + if (aInfo) { 1.1270 + iter = new CacheIndexContextIterator(index, aAddNew, aInfo); 1.1271 + } else { 1.1272 + iter = new CacheIndexIterator(index, aAddNew); 1.1273 + } 1.1274 + 1.1275 + iter->AddRecords(index->mFrecencyArray); 1.1276 + 1.1277 + index->mIterators.AppendElement(iter); 1.1278 + iter.swap(*_retval); 1.1279 + return NS_OK; 1.1280 +} 1.1281 + 1.1282 +// static 1.1283 +nsresult 1.1284 +CacheIndex::IsUpToDate(bool *_retval) 1.1285 +{ 1.1286 + LOG(("CacheIndex::IsUpToDate()")); 1.1287 + 1.1288 + nsRefPtr<CacheIndex> index = gInstance; 1.1289 + 1.1290 + if (!index) { 1.1291 + return NS_ERROR_NOT_INITIALIZED; 1.1292 + } 1.1293 + 1.1294 + CacheIndexAutoLock lock(index); 1.1295 + 1.1296 + if (!index->IsIndexUsable()) { 1.1297 + return NS_ERROR_NOT_AVAILABLE; 1.1298 + } 1.1299 + 1.1300 + *_retval = (index->mState == READY || index->mState == WRITING) && 1.1301 + !index->mIndexNeedsUpdate && !index->mShuttingDown; 1.1302 + 1.1303 + LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval)); 1.1304 + return NS_OK; 1.1305 +} 1.1306 + 1.1307 +bool 1.1308 +CacheIndex::IsIndexUsable() 1.1309 +{ 1.1310 + MOZ_ASSERT(mState != INITIAL); 1.1311 + 1.1312 + switch (mState) { 1.1313 + case INITIAL: 1.1314 + case SHUTDOWN: 1.1315 + return false; 1.1316 + 1.1317 + case READING: 1.1318 + case WRITING: 1.1319 + case BUILDING: 1.1320 + case UPDATING: 1.1321 + case READY: 1.1322 + break; 1.1323 + } 1.1324 + 1.1325 + return true; 1.1326 +} 1.1327 + 1.1328 +// static 1.1329 +bool 1.1330 +CacheIndex::IsCollision(CacheIndexEntry *aEntry, 1.1331 + uint32_t aAppId, 1.1332 + bool aAnonymous, 1.1333 + bool aInBrowser) 1.1334 +{ 1.1335 + if (!aEntry->IsInitialized()) { 1.1336 + return false; 1.1337 + } 1.1338 + 1.1339 + if (aEntry->AppId() != aAppId || aEntry->Anonymous() != aAnonymous || 1.1340 + aEntry->InBrowser() != aInBrowser) { 1.1341 + LOG(("CacheIndex::IsCollision() - Collision detected for entry hash=%08x" 1.1342 + "%08x%08x%08x%08x, expected values: appId=%u, anonymous=%d, " 1.1343 + "inBrowser=%d; actual values: appId=%u, anonymous=%d, inBrowser=%d]", 1.1344 + LOGSHA1(aEntry->Hash()), aAppId, aAnonymous, aInBrowser, 1.1345 + aEntry->AppId(), aEntry->Anonymous(), aEntry->InBrowser())); 1.1346 + return true; 1.1347 + } 1.1348 + 1.1349 + return false; 1.1350 +} 1.1351 + 1.1352 +// static 1.1353 +bool 1.1354 +CacheIndex::HasEntryChanged(CacheIndexEntry *aEntry, 1.1355 + const uint32_t *aFrecency, 1.1356 + const uint32_t *aExpirationTime, 1.1357 + const uint32_t *aSize) 1.1358 +{ 1.1359 + if (aFrecency && *aFrecency != aEntry->GetFrecency()) { 1.1360 + return true; 1.1361 + } 1.1362 + 1.1363 + if (aExpirationTime && *aExpirationTime != aEntry->GetExpirationTime()) { 1.1364 + return true; 1.1365 + } 1.1366 + 1.1367 + if (aSize && 1.1368 + (*aSize & CacheIndexEntry::kFileSizeMask) != aEntry->GetFileSize()) { 1.1369 + return true; 1.1370 + } 1.1371 + 1.1372 + return false; 1.1373 +} 1.1374 + 1.1375 +void 1.1376 +CacheIndex::ProcessPendingOperations() 1.1377 +{ 1.1378 + LOG(("CacheIndex::ProcessPendingOperations()")); 1.1379 + 1.1380 + AssertOwnsLock(); 1.1381 + 1.1382 + mPendingUpdates.EnumerateEntries(&CacheIndex::UpdateEntryInIndex, this); 1.1383 + 1.1384 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.1385 + 1.1386 + EnsureCorrectStats(); 1.1387 +} 1.1388 + 1.1389 +// static 1.1390 +PLDHashOperator 1.1391 +CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure) 1.1392 +{ 1.1393 + CacheIndex *index = static_cast<CacheIndex *>(aClosure); 1.1394 + 1.1395 + LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]", 1.1396 + LOGSHA1(aEntry->Hash()))); 1.1397 + 1.1398 + MOZ_ASSERT(aEntry->IsFresh()); 1.1399 + MOZ_ASSERT(aEntry->IsDirty()); 1.1400 + 1.1401 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash()); 1.1402 + 1.1403 + CacheIndexEntryAutoManage emng(aEntry->Hash(), index); 1.1404 + emng.DoNotSearchInUpdates(); 1.1405 + 1.1406 + if (aEntry->IsRemoved()) { 1.1407 + if (entry) { 1.1408 + if (entry->IsRemoved()) { 1.1409 + MOZ_ASSERT(entry->IsFresh()); 1.1410 + MOZ_ASSERT(entry->IsDirty()); 1.1411 + } else if (!entry->IsDirty() && entry->IsFileEmpty()) { 1.1412 + // Entries with empty file are not stored in index on disk. Just remove 1.1413 + // the entry, but only in case the entry is not dirty, i.e. the entry 1.1414 + // file was empty when we wrote the index. 1.1415 + index->mIndex.RemoveEntry(*aEntry->Hash()); 1.1416 + entry = nullptr; 1.1417 + } else { 1.1418 + entry->MarkRemoved(); 1.1419 + entry->MarkDirty(); 1.1420 + entry->MarkFresh(); 1.1421 + } 1.1422 + } 1.1423 + 1.1424 + return PL_DHASH_REMOVE; 1.1425 + } 1.1426 + 1.1427 + entry = index->mIndex.PutEntry(*aEntry->Hash()); 1.1428 + *entry = *aEntry; 1.1429 + 1.1430 + return PL_DHASH_REMOVE; 1.1431 +} 1.1432 + 1.1433 +bool 1.1434 +CacheIndex::WriteIndexToDiskIfNeeded() 1.1435 +{ 1.1436 + if (mState != READY || mShuttingDown) { 1.1437 + return false; 1.1438 + } 1.1439 + 1.1440 + if (!mLastDumpTime.IsNull() && 1.1441 + (TimeStamp::NowLoRes() - mLastDumpTime).ToMilliseconds() < 1.1442 + kMinDumpInterval) { 1.1443 + return false; 1.1444 + } 1.1445 + 1.1446 + if (mIndexStats.Dirty() < kMinUnwrittenChanges) { 1.1447 + return false; 1.1448 + } 1.1449 + 1.1450 + WriteIndexToDisk(); 1.1451 + return true; 1.1452 +} 1.1453 + 1.1454 +void 1.1455 +CacheIndex::WriteIndexToDisk() 1.1456 +{ 1.1457 + LOG(("CacheIndex::WriteIndexToDisk()")); 1.1458 + mIndexStats.Log(); 1.1459 + 1.1460 + nsresult rv; 1.1461 + 1.1462 + AssertOwnsLock(); 1.1463 + MOZ_ASSERT(mState == READY); 1.1464 + MOZ_ASSERT(!mRWBuf); 1.1465 + MOZ_ASSERT(!mRWHash); 1.1466 + 1.1467 + ChangeState(WRITING); 1.1468 + 1.1469 + mProcessEntries = mIndexStats.ActiveEntriesCount(); 1.1470 + 1.1471 + mIndexFileOpener = new FileOpenHelper(this); 1.1472 + rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName), 1.1473 + CacheFileIOManager::SPECIAL_FILE | 1.1474 + CacheFileIOManager::CREATE, 1.1475 + true, 1.1476 + mIndexFileOpener); 1.1477 + if (NS_FAILED(rv)) { 1.1478 + LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08x]", rv)); 1.1479 + FinishWrite(false); 1.1480 + return; 1.1481 + } 1.1482 + 1.1483 + // Write index header to a buffer, it will be written to disk together with 1.1484 + // records in WriteRecords() once we open the file successfully. 1.1485 + AllocBuffer(); 1.1486 + mRWHash = new CacheHash(); 1.1487 + 1.1488 + CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>(mRWBuf); 1.1489 + NetworkEndian::writeUint32(&hdr->mVersion, kIndexVersion); 1.1490 + NetworkEndian::writeUint32(&hdr->mTimeStamp, 1.1491 + static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC)); 1.1492 + NetworkEndian::writeUint32(&hdr->mIsDirty, 1); 1.1493 + 1.1494 + mRWBufPos = sizeof(CacheIndexHeader); 1.1495 + mSkipEntries = 0; 1.1496 +} 1.1497 + 1.1498 +namespace { // anon 1.1499 + 1.1500 +struct WriteRecordsHelper 1.1501 +{ 1.1502 + char *mBuf; 1.1503 + uint32_t mSkip; 1.1504 + uint32_t mProcessMax; 1.1505 + uint32_t mProcessed; 1.1506 +#ifdef DEBUG 1.1507 + bool mHasMore; 1.1508 +#endif 1.1509 +}; 1.1510 + 1.1511 +} // anon 1.1512 + 1.1513 +void 1.1514 +CacheIndex::WriteRecords() 1.1515 +{ 1.1516 + LOG(("CacheIndex::WriteRecords()")); 1.1517 + 1.1518 + nsresult rv; 1.1519 + 1.1520 + AssertOwnsLock(); 1.1521 + MOZ_ASSERT(mState == WRITING); 1.1522 + 1.1523 + int64_t fileOffset; 1.1524 + 1.1525 + if (mSkipEntries) { 1.1526 + MOZ_ASSERT(mRWBufPos == 0); 1.1527 + fileOffset = sizeof(CacheIndexHeader); 1.1528 + fileOffset += sizeof(CacheIndexRecord) * mSkipEntries; 1.1529 + } else { 1.1530 + MOZ_ASSERT(mRWBufPos == sizeof(CacheIndexHeader)); 1.1531 + fileOffset = 0; 1.1532 + } 1.1533 + uint32_t hashOffset = mRWBufPos; 1.1534 + 1.1535 + WriteRecordsHelper data; 1.1536 + data.mBuf = mRWBuf + mRWBufPos; 1.1537 + data.mSkip = mSkipEntries; 1.1538 + data.mProcessMax = (mRWBufSize - mRWBufPos) / sizeof(CacheIndexRecord); 1.1539 + MOZ_ASSERT(data.mProcessMax != 0 || mProcessEntries == 0); // TODO make sure we can write an empty index 1.1540 + data.mProcessed = 0; 1.1541 +#ifdef DEBUG 1.1542 + data.mHasMore = false; 1.1543 +#endif 1.1544 + 1.1545 + mIndex.EnumerateEntries(&CacheIndex::CopyRecordsToRWBuf, &data); 1.1546 + MOZ_ASSERT(mRWBufPos != static_cast<uint32_t>(data.mBuf - mRWBuf) || 1.1547 + mProcessEntries == 0); 1.1548 + mRWBufPos = data.mBuf - mRWBuf; 1.1549 + mSkipEntries += data.mProcessed; 1.1550 + MOZ_ASSERT(mSkipEntries <= mProcessEntries); 1.1551 + 1.1552 + mRWHash->Update(mRWBuf + hashOffset, mRWBufPos - hashOffset); 1.1553 + 1.1554 + if (mSkipEntries == mProcessEntries) { 1.1555 + MOZ_ASSERT(!data.mHasMore); 1.1556 + 1.1557 + // We've processed all records 1.1558 + if (mRWBufPos + sizeof(CacheHash::Hash32_t) > mRWBufSize) { 1.1559 + // realloc buffer to spare another write cycle 1.1560 + mRWBufSize = mRWBufPos + sizeof(CacheHash::Hash32_t); 1.1561 + mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mRWBufSize)); 1.1562 + } 1.1563 + 1.1564 + NetworkEndian::writeUint32(mRWBuf + mRWBufPos, mRWHash->GetHash()); 1.1565 + mRWBufPos += sizeof(CacheHash::Hash32_t); 1.1566 + } else { 1.1567 + MOZ_ASSERT(data.mHasMore); 1.1568 + } 1.1569 + 1.1570 + rv = CacheFileIOManager::Write(mIndexHandle, fileOffset, mRWBuf, mRWBufPos, 1.1571 + mSkipEntries == mProcessEntries, this); 1.1572 + if (NS_FAILED(rv)) { 1.1573 + LOG(("CacheIndex::WriteRecords() - CacheFileIOManager::Write() failed " 1.1574 + "synchronously [rv=0x%08x]", rv)); 1.1575 + FinishWrite(false); 1.1576 + } 1.1577 + 1.1578 + mRWBufPos = 0; 1.1579 +} 1.1580 + 1.1581 +void 1.1582 +CacheIndex::FinishWrite(bool aSucceeded) 1.1583 +{ 1.1584 + LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded)); 1.1585 + 1.1586 + MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == WRITING); 1.1587 + 1.1588 + AssertOwnsLock(); 1.1589 + 1.1590 + mIndexHandle = nullptr; 1.1591 + mRWHash = nullptr; 1.1592 + ReleaseBuffer(); 1.1593 + 1.1594 + if (aSucceeded) { 1.1595 + // Opening of the file must not be in progress if writing succeeded. 1.1596 + MOZ_ASSERT(!mIndexFileOpener); 1.1597 + 1.1598 + mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this); 1.1599 + mIndexOnDiskIsValid = true; 1.1600 + } else { 1.1601 + if (mIndexFileOpener) { 1.1602 + // If opening of the file is still in progress (e.g. WRITE process was 1.1603 + // canceled by RemoveAll()) then we need to cancel the opener to make sure 1.1604 + // that OnFileOpenedInternal() won't be called. 1.1605 + mIndexFileOpener->Cancel(); 1.1606 + mIndexFileOpener = nullptr; 1.1607 + } 1.1608 + } 1.1609 + 1.1610 + ProcessPendingOperations(); 1.1611 + mIndexStats.Log(); 1.1612 + 1.1613 + if (mState == WRITING) { 1.1614 + ChangeState(READY); 1.1615 + mLastDumpTime = TimeStamp::NowLoRes(); 1.1616 + } 1.1617 +} 1.1618 + 1.1619 +// static 1.1620 +PLDHashOperator 1.1621 +CacheIndex::CopyRecordsToRWBuf(CacheIndexEntry *aEntry, void* aClosure) 1.1622 +{ 1.1623 + if (aEntry->IsRemoved()) { 1.1624 + return PL_DHASH_NEXT; 1.1625 + } 1.1626 + 1.1627 + if (!aEntry->IsInitialized()) { 1.1628 + return PL_DHASH_NEXT; 1.1629 + } 1.1630 + 1.1631 + if (aEntry->IsFileEmpty()) { 1.1632 + return PL_DHASH_NEXT; 1.1633 + } 1.1634 + 1.1635 + WriteRecordsHelper *data = static_cast<WriteRecordsHelper *>(aClosure); 1.1636 + if (data->mSkip) { 1.1637 + data->mSkip--; 1.1638 + return PL_DHASH_NEXT; 1.1639 + } 1.1640 + 1.1641 + if (data->mProcessed == data->mProcessMax) { 1.1642 +#ifdef DEBUG 1.1643 + data->mHasMore = true; 1.1644 +#endif 1.1645 + return PL_DHASH_STOP; 1.1646 + } 1.1647 + 1.1648 + aEntry->WriteToBuf(data->mBuf); 1.1649 + data->mBuf += sizeof(CacheIndexRecord); 1.1650 + data->mProcessed++; 1.1651 + 1.1652 + return PL_DHASH_NEXT; 1.1653 +} 1.1654 + 1.1655 +// static 1.1656 +PLDHashOperator 1.1657 +CacheIndex::ApplyIndexChanges(CacheIndexEntry *aEntry, void* aClosure) 1.1658 +{ 1.1659 + CacheIndex *index = static_cast<CacheIndex *>(aClosure); 1.1660 + 1.1661 + CacheIndexEntryAutoManage emng(aEntry->Hash(), index); 1.1662 + 1.1663 + if (aEntry->IsRemoved()) { 1.1664 + emng.DoNotSearchInIndex(); 1.1665 + return PL_DHASH_REMOVE; 1.1666 + } 1.1667 + 1.1668 + if (aEntry->IsDirty()) { 1.1669 + aEntry->ClearDirty(); 1.1670 + } 1.1671 + 1.1672 + return PL_DHASH_NEXT; 1.1673 +} 1.1674 + 1.1675 +nsresult 1.1676 +CacheIndex::GetFile(const nsACString &aName, nsIFile **_retval) 1.1677 +{ 1.1678 + nsresult rv; 1.1679 + 1.1680 + nsCOMPtr<nsIFile> file; 1.1681 + rv = mCacheDirectory->Clone(getter_AddRefs(file)); 1.1682 + NS_ENSURE_SUCCESS(rv, rv); 1.1683 + 1.1684 + rv = file->AppendNative(aName); 1.1685 + NS_ENSURE_SUCCESS(rv, rv); 1.1686 + 1.1687 + file.swap(*_retval); 1.1688 + return NS_OK; 1.1689 +} 1.1690 + 1.1691 +nsresult 1.1692 +CacheIndex::RemoveFile(const nsACString &aName) 1.1693 +{ 1.1694 + MOZ_ASSERT(mState == SHUTDOWN); 1.1695 + 1.1696 + nsresult rv; 1.1697 + 1.1698 + nsCOMPtr<nsIFile> file; 1.1699 + rv = GetFile(aName, getter_AddRefs(file)); 1.1700 + NS_ENSURE_SUCCESS(rv, rv); 1.1701 + 1.1702 + bool exists; 1.1703 + rv = file->Exists(&exists); 1.1704 + NS_ENSURE_SUCCESS(rv, rv); 1.1705 + 1.1706 + if (exists) { 1.1707 + rv = file->Remove(false); 1.1708 + if (NS_FAILED(rv)) { 1.1709 + LOG(("CacheIndex::RemoveFile() - Cannot remove old entry file from disk." 1.1710 + "[name=%s]", PromiseFlatCString(aName).get())); 1.1711 + NS_WARNING("Cannot remove old entry file from the disk"); 1.1712 + return rv; 1.1713 + } 1.1714 + } 1.1715 + 1.1716 + return NS_OK; 1.1717 +} 1.1718 + 1.1719 +void 1.1720 +CacheIndex::RemoveIndexFromDisk() 1.1721 +{ 1.1722 + LOG(("CacheIndex::RemoveIndexFromDisk()")); 1.1723 + 1.1724 + RemoveFile(NS_LITERAL_CSTRING(kIndexName)); 1.1725 + RemoveFile(NS_LITERAL_CSTRING(kTempIndexName)); 1.1726 + RemoveFile(NS_LITERAL_CSTRING(kJournalName)); 1.1727 +} 1.1728 + 1.1729 +class WriteLogHelper 1.1730 +{ 1.1731 +public: 1.1732 + WriteLogHelper(PRFileDesc *aFD) 1.1733 + : mStatus(NS_OK) 1.1734 + , mFD(aFD) 1.1735 + , mBufSize(kMaxBufSize) 1.1736 + , mBufPos(0) 1.1737 + { 1.1738 + mHash = new CacheHash(); 1.1739 + mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); 1.1740 + } 1.1741 + 1.1742 + ~WriteLogHelper() { 1.1743 + free(mBuf); 1.1744 + } 1.1745 + 1.1746 + nsresult AddEntry(CacheIndexEntry *aEntry); 1.1747 + nsresult Finish(); 1.1748 + 1.1749 +private: 1.1750 + 1.1751 + nsresult FlushBuffer(); 1.1752 + 1.1753 + nsresult mStatus; 1.1754 + PRFileDesc *mFD; 1.1755 + char *mBuf; 1.1756 + uint32_t mBufSize; 1.1757 + int32_t mBufPos; 1.1758 + nsRefPtr<CacheHash> mHash; 1.1759 +}; 1.1760 + 1.1761 +nsresult 1.1762 +WriteLogHelper::AddEntry(CacheIndexEntry *aEntry) 1.1763 +{ 1.1764 + nsresult rv; 1.1765 + 1.1766 + if (NS_FAILED(mStatus)) { 1.1767 + return mStatus; 1.1768 + } 1.1769 + 1.1770 + if (mBufPos + sizeof(CacheIndexRecord) > mBufSize) { 1.1771 + mHash->Update(mBuf, mBufPos); 1.1772 + 1.1773 + rv = FlushBuffer(); 1.1774 + if (NS_FAILED(rv)) { 1.1775 + mStatus = rv; 1.1776 + return rv; 1.1777 + } 1.1778 + MOZ_ASSERT(mBufPos + sizeof(CacheIndexRecord) <= mBufSize); 1.1779 + } 1.1780 + 1.1781 + aEntry->WriteToBuf(mBuf + mBufPos); 1.1782 + mBufPos += sizeof(CacheIndexRecord); 1.1783 + 1.1784 + return NS_OK; 1.1785 +} 1.1786 + 1.1787 +nsresult 1.1788 +WriteLogHelper::Finish() 1.1789 +{ 1.1790 + nsresult rv; 1.1791 + 1.1792 + if (NS_FAILED(mStatus)) { 1.1793 + return mStatus; 1.1794 + } 1.1795 + 1.1796 + mHash->Update(mBuf, mBufPos); 1.1797 + if (mBufPos + sizeof(CacheHash::Hash32_t) > mBufSize) { 1.1798 + rv = FlushBuffer(); 1.1799 + if (NS_FAILED(rv)) { 1.1800 + mStatus = rv; 1.1801 + return rv; 1.1802 + } 1.1803 + MOZ_ASSERT(mBufPos + sizeof(CacheHash::Hash32_t) <= mBufSize); 1.1804 + } 1.1805 + 1.1806 + NetworkEndian::writeUint32(mBuf + mBufPos, mHash->GetHash()); 1.1807 + mBufPos += sizeof(CacheHash::Hash32_t); 1.1808 + 1.1809 + rv = FlushBuffer(); 1.1810 + NS_ENSURE_SUCCESS(rv, rv); 1.1811 + 1.1812 + mStatus = NS_ERROR_UNEXPECTED; // Don't allow any other operation 1.1813 + return NS_OK; 1.1814 +} 1.1815 + 1.1816 +nsresult 1.1817 +WriteLogHelper::FlushBuffer() 1.1818 +{ 1.1819 + MOZ_ASSERT(NS_SUCCEEDED(mStatus)); 1.1820 + 1.1821 + int32_t bytesWritten = PR_Write(mFD, mBuf, mBufPos); 1.1822 + 1.1823 + if (bytesWritten != mBufPos) { 1.1824 + return NS_ERROR_FAILURE; 1.1825 + } 1.1826 + 1.1827 + mBufPos = 0; 1.1828 + return NS_OK; 1.1829 +} 1.1830 + 1.1831 +nsresult 1.1832 +CacheIndex::WriteLogToDisk() 1.1833 +{ 1.1834 + LOG(("CacheIndex::WriteLogToDisk()")); 1.1835 + 1.1836 + nsresult rv; 1.1837 + 1.1838 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.1839 + MOZ_ASSERT(mState == SHUTDOWN); 1.1840 + 1.1841 + RemoveFile(NS_LITERAL_CSTRING(kTempIndexName)); 1.1842 + 1.1843 + nsCOMPtr<nsIFile> indexFile; 1.1844 + rv = GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(indexFile)); 1.1845 + NS_ENSURE_SUCCESS(rv, rv); 1.1846 + 1.1847 + nsCOMPtr<nsIFile> logFile; 1.1848 + rv = GetFile(NS_LITERAL_CSTRING(kJournalName), getter_AddRefs(logFile)); 1.1849 + NS_ENSURE_SUCCESS(rv, rv); 1.1850 + 1.1851 + mIndexStats.Log(); 1.1852 + 1.1853 + PRFileDesc *fd = nullptr; 1.1854 + rv = logFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 1.1855 + 0600, &fd); 1.1856 + NS_ENSURE_SUCCESS(rv, rv); 1.1857 + 1.1858 + WriteLogHelper wlh(fd); 1.1859 + mIndex.EnumerateEntries(&CacheIndex::WriteEntryToLog, &wlh); 1.1860 + 1.1861 + rv = wlh.Finish(); 1.1862 + PR_Close(fd); 1.1863 + NS_ENSURE_SUCCESS(rv, rv); 1.1864 + 1.1865 + rv = indexFile->OpenNSPRFileDesc(PR_RDWR, 0600, &fd); 1.1866 + NS_ENSURE_SUCCESS(rv, rv); 1.1867 + 1.1868 + CacheIndexHeader header; 1.1869 + int32_t bytesRead = PR_Read(fd, &header, sizeof(CacheIndexHeader)); 1.1870 + if (bytesRead != sizeof(CacheIndexHeader)) { 1.1871 + PR_Close(fd); 1.1872 + return NS_ERROR_FAILURE; 1.1873 + } 1.1874 + 1.1875 + NetworkEndian::writeUint32(&header.mIsDirty, 0); 1.1876 + 1.1877 + int64_t offset = PR_Seek64(fd, 0, PR_SEEK_SET); 1.1878 + if (offset == -1) { 1.1879 + PR_Close(fd); 1.1880 + return NS_ERROR_FAILURE; 1.1881 + } 1.1882 + 1.1883 + int32_t bytesWritten = PR_Write(fd, &header, sizeof(CacheIndexHeader)); 1.1884 + PR_Close(fd); 1.1885 + if (bytesWritten != sizeof(CacheIndexHeader)) { 1.1886 + return NS_ERROR_FAILURE; 1.1887 + } 1.1888 + 1.1889 + return NS_OK; 1.1890 +} 1.1891 + 1.1892 +// static 1.1893 +PLDHashOperator 1.1894 +CacheIndex::WriteEntryToLog(CacheIndexEntry *aEntry, void* aClosure) 1.1895 +{ 1.1896 + WriteLogHelper *wlh = static_cast<WriteLogHelper *>(aClosure); 1.1897 + 1.1898 + if (aEntry->IsRemoved() || aEntry->IsDirty()) { 1.1899 + wlh->AddEntry(aEntry); 1.1900 + } 1.1901 + 1.1902 + return PL_DHASH_REMOVE; 1.1903 +} 1.1904 + 1.1905 +void 1.1906 +CacheIndex::ReadIndexFromDisk() 1.1907 +{ 1.1908 + LOG(("CacheIndex::ReadIndexFromDisk()")); 1.1909 + 1.1910 + nsresult rv; 1.1911 + 1.1912 + AssertOwnsLock(); 1.1913 + MOZ_ASSERT(mState == INITIAL); 1.1914 + 1.1915 + ChangeState(READING); 1.1916 + 1.1917 + mIndexFileOpener = new FileOpenHelper(this); 1.1918 + rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kIndexName), 1.1919 + CacheFileIOManager::SPECIAL_FILE | 1.1920 + CacheFileIOManager::OPEN, 1.1921 + true, 1.1922 + mIndexFileOpener); 1.1923 + if (NS_FAILED(rv)) { 1.1924 + LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() " 1.1925 + "failed [rv=0x%08x, file=%s]", rv, kIndexName)); 1.1926 + FinishRead(false); 1.1927 + return; 1.1928 + } 1.1929 + 1.1930 + mJournalFileOpener = new FileOpenHelper(this); 1.1931 + rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kJournalName), 1.1932 + CacheFileIOManager::SPECIAL_FILE | 1.1933 + CacheFileIOManager::OPEN, 1.1934 + true, 1.1935 + mJournalFileOpener); 1.1936 + if (NS_FAILED(rv)) { 1.1937 + LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() " 1.1938 + "failed [rv=0x%08x, file=%s]", rv, kJournalName)); 1.1939 + FinishRead(false); 1.1940 + } 1.1941 + 1.1942 + mTmpFileOpener = new FileOpenHelper(this); 1.1943 + rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName), 1.1944 + CacheFileIOManager::SPECIAL_FILE | 1.1945 + CacheFileIOManager::OPEN, 1.1946 + true, 1.1947 + mTmpFileOpener); 1.1948 + if (NS_FAILED(rv)) { 1.1949 + LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() " 1.1950 + "failed [rv=0x%08x, file=%s]", rv, kTempIndexName)); 1.1951 + FinishRead(false); 1.1952 + } 1.1953 +} 1.1954 + 1.1955 +void 1.1956 +CacheIndex::StartReadingIndex() 1.1957 +{ 1.1958 + LOG(("CacheIndex::StartReadingIndex()")); 1.1959 + 1.1960 + nsresult rv; 1.1961 + 1.1962 + AssertOwnsLock(); 1.1963 + 1.1964 + MOZ_ASSERT(mIndexHandle); 1.1965 + MOZ_ASSERT(mState == READING); 1.1966 + MOZ_ASSERT(!mIndexOnDiskIsValid); 1.1967 + MOZ_ASSERT(!mDontMarkIndexClean); 1.1968 + MOZ_ASSERT(!mJournalReadSuccessfully); 1.1969 + MOZ_ASSERT(mIndexHandle->FileSize() >= 0); 1.1970 + 1.1971 + int64_t entriesSize = mIndexHandle->FileSize() - sizeof(CacheIndexHeader) - 1.1972 + sizeof(CacheHash::Hash32_t); 1.1973 + 1.1974 + if (entriesSize < 0 || entriesSize % sizeof(CacheIndexRecord)) { 1.1975 + LOG(("CacheIndex::StartReadingIndex() - Index is corrupted")); 1.1976 + FinishRead(false); 1.1977 + return; 1.1978 + } 1.1979 + 1.1980 + AllocBuffer(); 1.1981 + mSkipEntries = 0; 1.1982 + mRWHash = new CacheHash(); 1.1983 + 1.1984 + mRWBufPos = std::min(mRWBufSize, 1.1985 + static_cast<uint32_t>(mIndexHandle->FileSize())); 1.1986 + 1.1987 + rv = CacheFileIOManager::Read(mIndexHandle, 0, mRWBuf, mRWBufPos, true, this); 1.1988 + if (NS_FAILED(rv)) { 1.1989 + LOG(("CacheIndex::StartReadingIndex() - CacheFileIOManager::Read() failed " 1.1990 + "synchronously [rv=0x%08x]", rv)); 1.1991 + FinishRead(false); 1.1992 + } 1.1993 +} 1.1994 + 1.1995 +void 1.1996 +CacheIndex::ParseRecords() 1.1997 +{ 1.1998 + LOG(("CacheIndex::ParseRecords()")); 1.1999 + 1.2000 + nsresult rv; 1.2001 + 1.2002 + AssertOwnsLock(); 1.2003 + 1.2004 + uint32_t entryCnt = (mIndexHandle->FileSize() - sizeof(CacheIndexHeader) - 1.2005 + sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord); 1.2006 + uint32_t pos = 0; 1.2007 + 1.2008 + if (!mSkipEntries) { 1.2009 + CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>( 1.2010 + moz_xmalloc(sizeof(CacheIndexHeader))); 1.2011 + memcpy(hdr, mRWBuf, sizeof(CacheIndexHeader)); 1.2012 + 1.2013 + if (NetworkEndian::readUint32(&hdr->mVersion) != kIndexVersion) { 1.2014 + free(hdr); 1.2015 + FinishRead(false); 1.2016 + return; 1.2017 + } 1.2018 + 1.2019 + mIndexTimeStamp = NetworkEndian::readUint32(&hdr->mTimeStamp); 1.2020 + 1.2021 + if (NetworkEndian::readUint32(&hdr->mIsDirty)) { 1.2022 + if (mJournalHandle) { 1.2023 + CacheFileIOManager::DoomFile(mJournalHandle, nullptr); 1.2024 + mJournalHandle = nullptr; 1.2025 + } 1.2026 + free(hdr); 1.2027 + } else { 1.2028 + NetworkEndian::writeUint32(&hdr->mIsDirty, 1); 1.2029 + 1.2030 + // Mark index dirty. The buffer is freed by CacheFileIOManager when 1.2031 + // nullptr is passed as the listener and the call doesn't fail 1.2032 + // synchronously. 1.2033 + rv = CacheFileIOManager::Write(mIndexHandle, 0, 1.2034 + reinterpret_cast<char *>(hdr), 1.2035 + sizeof(CacheIndexHeader), true, nullptr); 1.2036 + if (NS_FAILED(rv)) { 1.2037 + // This is not fatal, just free the memory 1.2038 + free(hdr); 1.2039 + } 1.2040 + } 1.2041 + 1.2042 + pos += sizeof(CacheIndexHeader); 1.2043 + } 1.2044 + 1.2045 + uint32_t hashOffset = pos; 1.2046 + 1.2047 + while (pos + sizeof(CacheIndexRecord) <= mRWBufPos && 1.2048 + mSkipEntries != entryCnt) { 1.2049 + CacheIndexRecord *rec = reinterpret_cast<CacheIndexRecord *>(mRWBuf + pos); 1.2050 + CacheIndexEntry tmpEntry(&rec->mHash); 1.2051 + tmpEntry.ReadFromBuf(mRWBuf + pos); 1.2052 + 1.2053 + if (tmpEntry.IsDirty() || !tmpEntry.IsInitialized() || 1.2054 + tmpEntry.IsFileEmpty() || tmpEntry.IsFresh() || tmpEntry.IsRemoved()) { 1.2055 + LOG(("CacheIndex::ParseRecords() - Invalid entry found in index, removing" 1.2056 + " whole index [dirty=%d, initialized=%d, fileEmpty=%d, fresh=%d, " 1.2057 + "removed=%d]", tmpEntry.IsDirty(), tmpEntry.IsInitialized(), 1.2058 + tmpEntry.IsFileEmpty(), tmpEntry.IsFresh(), tmpEntry.IsRemoved())); 1.2059 + FinishRead(false); 1.2060 + return; 1.2061 + } 1.2062 + 1.2063 + CacheIndexEntryAutoManage emng(tmpEntry.Hash(), this); 1.2064 + 1.2065 + CacheIndexEntry *entry = mIndex.PutEntry(*tmpEntry.Hash()); 1.2066 + *entry = tmpEntry; 1.2067 + 1.2068 + pos += sizeof(CacheIndexRecord); 1.2069 + mSkipEntries++; 1.2070 + } 1.2071 + 1.2072 + mRWHash->Update(mRWBuf + hashOffset, pos - hashOffset); 1.2073 + 1.2074 + if (pos != mRWBufPos) { 1.2075 + memmove(mRWBuf, mRWBuf + pos, mRWBufPos - pos); 1.2076 + mRWBufPos -= pos; 1.2077 + pos = 0; 1.2078 + } 1.2079 + 1.2080 + int64_t fileOffset = sizeof(CacheIndexHeader) + 1.2081 + mSkipEntries * sizeof(CacheIndexRecord) + mRWBufPos; 1.2082 + 1.2083 + MOZ_ASSERT(fileOffset <= mIndexHandle->FileSize()); 1.2084 + if (fileOffset == mIndexHandle->FileSize()) { 1.2085 + if (mRWHash->GetHash() != NetworkEndian::readUint32(mRWBuf)) { 1.2086 + LOG(("CacheIndex::ParseRecords() - Hash mismatch, [is %x, should be %x]", 1.2087 + mRWHash->GetHash(), 1.2088 + NetworkEndian::readUint32(mRWBuf))); 1.2089 + FinishRead(false); 1.2090 + return; 1.2091 + } 1.2092 + 1.2093 + mIndexOnDiskIsValid = true; 1.2094 + mJournalReadSuccessfully = false; 1.2095 + 1.2096 + if (mJournalHandle) { 1.2097 + StartReadingJournal(); 1.2098 + } else { 1.2099 + FinishRead(false); 1.2100 + } 1.2101 + 1.2102 + return; 1.2103 + } 1.2104 + 1.2105 + pos = mRWBufPos; 1.2106 + uint32_t toRead = std::min(mRWBufSize - pos, 1.2107 + static_cast<uint32_t>(mIndexHandle->FileSize() - 1.2108 + fileOffset)); 1.2109 + mRWBufPos = pos + toRead; 1.2110 + 1.2111 + rv = CacheFileIOManager::Read(mIndexHandle, fileOffset, mRWBuf + pos, toRead, 1.2112 + true, this); 1.2113 + if (NS_FAILED(rv)) { 1.2114 + LOG(("CacheIndex::ParseRecords() - CacheFileIOManager::Read() failed " 1.2115 + "synchronously [rv=0x%08x]", rv)); 1.2116 + FinishRead(false); 1.2117 + return; 1.2118 + } 1.2119 +} 1.2120 + 1.2121 +void 1.2122 +CacheIndex::StartReadingJournal() 1.2123 +{ 1.2124 + LOG(("CacheIndex::StartReadingJournal()")); 1.2125 + 1.2126 + nsresult rv; 1.2127 + 1.2128 + AssertOwnsLock(); 1.2129 + 1.2130 + MOZ_ASSERT(mJournalHandle); 1.2131 + MOZ_ASSERT(mIndexOnDiskIsValid); 1.2132 + MOZ_ASSERT(mTmpJournal.Count() == 0); 1.2133 + MOZ_ASSERT(mJournalHandle->FileSize() >= 0); 1.2134 + 1.2135 + int64_t entriesSize = mJournalHandle->FileSize() - 1.2136 + sizeof(CacheHash::Hash32_t); 1.2137 + 1.2138 + if (entriesSize < 0 || entriesSize % sizeof(CacheIndexRecord)) { 1.2139 + LOG(("CacheIndex::StartReadingJournal() - Journal is corrupted")); 1.2140 + FinishRead(false); 1.2141 + return; 1.2142 + } 1.2143 + 1.2144 + mSkipEntries = 0; 1.2145 + mRWHash = new CacheHash(); 1.2146 + 1.2147 + mRWBufPos = std::min(mRWBufSize, 1.2148 + static_cast<uint32_t>(mJournalHandle->FileSize())); 1.2149 + 1.2150 + rv = CacheFileIOManager::Read(mJournalHandle, 0, mRWBuf, mRWBufPos, true, this); 1.2151 + if (NS_FAILED(rv)) { 1.2152 + LOG(("CacheIndex::StartReadingJournal() - CacheFileIOManager::Read() failed" 1.2153 + " synchronously [rv=0x%08x]", rv)); 1.2154 + FinishRead(false); 1.2155 + } 1.2156 +} 1.2157 + 1.2158 +void 1.2159 +CacheIndex::ParseJournal() 1.2160 +{ 1.2161 + LOG(("CacheIndex::ParseRecords()")); 1.2162 + 1.2163 + nsresult rv; 1.2164 + 1.2165 + AssertOwnsLock(); 1.2166 + 1.2167 + uint32_t entryCnt = (mJournalHandle->FileSize() - 1.2168 + sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord); 1.2169 + 1.2170 + uint32_t pos = 0; 1.2171 + 1.2172 + while (pos + sizeof(CacheIndexRecord) <= mRWBufPos && 1.2173 + mSkipEntries != entryCnt) { 1.2174 + CacheIndexRecord *rec = reinterpret_cast<CacheIndexRecord *>(mRWBuf + pos); 1.2175 + CacheIndexEntry tmpEntry(&rec->mHash); 1.2176 + tmpEntry.ReadFromBuf(mRWBuf + pos); 1.2177 + 1.2178 + CacheIndexEntry *entry = mTmpJournal.PutEntry(*tmpEntry.Hash()); 1.2179 + *entry = tmpEntry; 1.2180 + 1.2181 + if (entry->IsDirty() || entry->IsFresh()) { 1.2182 + LOG(("CacheIndex::ParseJournal() - Invalid entry found in journal, " 1.2183 + "ignoring whole journal [dirty=%d, fresh=%d]", entry->IsDirty(), 1.2184 + entry->IsFresh())); 1.2185 + FinishRead(false); 1.2186 + return; 1.2187 + } 1.2188 + 1.2189 + pos += sizeof(CacheIndexRecord); 1.2190 + mSkipEntries++; 1.2191 + } 1.2192 + 1.2193 + mRWHash->Update(mRWBuf, pos); 1.2194 + 1.2195 + if (pos != mRWBufPos) { 1.2196 + memmove(mRWBuf, mRWBuf + pos, mRWBufPos - pos); 1.2197 + mRWBufPos -= pos; 1.2198 + pos = 0; 1.2199 + } 1.2200 + 1.2201 + int64_t fileOffset = mSkipEntries * sizeof(CacheIndexRecord) + mRWBufPos; 1.2202 + 1.2203 + MOZ_ASSERT(fileOffset <= mJournalHandle->FileSize()); 1.2204 + if (fileOffset == mJournalHandle->FileSize()) { 1.2205 + if (mRWHash->GetHash() != NetworkEndian::readUint32(mRWBuf)) { 1.2206 + LOG(("CacheIndex::ParseJournal() - Hash mismatch, [is %x, should be %x]", 1.2207 + mRWHash->GetHash(), 1.2208 + NetworkEndian::readUint32(mRWBuf))); 1.2209 + FinishRead(false); 1.2210 + return; 1.2211 + } 1.2212 + 1.2213 + mJournalReadSuccessfully = true; 1.2214 + FinishRead(true); 1.2215 + return; 1.2216 + } 1.2217 + 1.2218 + pos = mRWBufPos; 1.2219 + uint32_t toRead = std::min(mRWBufSize - pos, 1.2220 + static_cast<uint32_t>(mJournalHandle->FileSize() - 1.2221 + fileOffset)); 1.2222 + mRWBufPos = pos + toRead; 1.2223 + 1.2224 + rv = CacheFileIOManager::Read(mJournalHandle, fileOffset, mRWBuf + pos, 1.2225 + toRead, true, this); 1.2226 + if (NS_FAILED(rv)) { 1.2227 + LOG(("CacheIndex::ParseJournal() - CacheFileIOManager::Read() failed " 1.2228 + "synchronously [rv=0x%08x]", rv)); 1.2229 + FinishRead(false); 1.2230 + return; 1.2231 + } 1.2232 +} 1.2233 + 1.2234 +void 1.2235 +CacheIndex::MergeJournal() 1.2236 +{ 1.2237 + LOG(("CacheIndex::MergeJournal()")); 1.2238 + 1.2239 + AssertOwnsLock(); 1.2240 + 1.2241 + mTmpJournal.EnumerateEntries(&CacheIndex::ProcessJournalEntry, this); 1.2242 + 1.2243 + MOZ_ASSERT(mTmpJournal.Count() == 0); 1.2244 +} 1.2245 + 1.2246 +// static 1.2247 +PLDHashOperator 1.2248 +CacheIndex::ProcessJournalEntry(CacheIndexEntry *aEntry, void* aClosure) 1.2249 +{ 1.2250 + CacheIndex *index = static_cast<CacheIndex *>(aClosure); 1.2251 + 1.2252 + LOG(("CacheFile::ProcessJournalEntry() [hash=%08x%08x%08x%08x%08x]", 1.2253 + LOGSHA1(aEntry->Hash()))); 1.2254 + 1.2255 + CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash()); 1.2256 + 1.2257 + CacheIndexEntryAutoManage emng(aEntry->Hash(), index); 1.2258 + 1.2259 + if (aEntry->IsRemoved()) { 1.2260 + if (entry) { 1.2261 + entry->MarkRemoved(); 1.2262 + entry->MarkDirty(); 1.2263 + } 1.2264 + } else { 1.2265 + if (!entry) { 1.2266 + entry = index->mIndex.PutEntry(*aEntry->Hash()); 1.2267 + } 1.2268 + 1.2269 + *entry = *aEntry; 1.2270 + entry->MarkDirty(); 1.2271 + } 1.2272 + 1.2273 + return PL_DHASH_REMOVE; 1.2274 +} 1.2275 + 1.2276 +void 1.2277 +CacheIndex::EnsureNoFreshEntry() 1.2278 +{ 1.2279 +#ifdef DEBUG_STATS 1.2280 + CacheIndexStats debugStats; 1.2281 + debugStats.DisableLogging(); 1.2282 + mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats); 1.2283 + MOZ_ASSERT(debugStats.Fresh() == 0); 1.2284 +#endif 1.2285 +} 1.2286 + 1.2287 +void 1.2288 +CacheIndex::EnsureCorrectStats() 1.2289 +{ 1.2290 +#ifdef DEBUG_STATS 1.2291 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.2292 + CacheIndexStats debugStats; 1.2293 + debugStats.DisableLogging(); 1.2294 + mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats); 1.2295 + MOZ_ASSERT(debugStats == mIndexStats); 1.2296 +#endif 1.2297 +} 1.2298 + 1.2299 +// static 1.2300 +PLDHashOperator 1.2301 +CacheIndex::SumIndexStats(CacheIndexEntry *aEntry, void* aClosure) 1.2302 +{ 1.2303 + CacheIndexStats *stats = static_cast<CacheIndexStats *>(aClosure); 1.2304 + stats->BeforeChange(nullptr); 1.2305 + stats->AfterChange(aEntry); 1.2306 + return PL_DHASH_NEXT; 1.2307 +} 1.2308 + 1.2309 +void 1.2310 +CacheIndex::FinishRead(bool aSucceeded) 1.2311 +{ 1.2312 + LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded)); 1.2313 + AssertOwnsLock(); 1.2314 + 1.2315 + MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING); 1.2316 + 1.2317 + MOZ_ASSERT( 1.2318 + // -> rebuild 1.2319 + (!aSucceeded && !mIndexOnDiskIsValid && !mJournalReadSuccessfully) || 1.2320 + // -> update 1.2321 + (!aSucceeded && mIndexOnDiskIsValid && !mJournalReadSuccessfully) || 1.2322 + // -> ready 1.2323 + (aSucceeded && mIndexOnDiskIsValid && mJournalReadSuccessfully)); 1.2324 + 1.2325 + if (mState == SHUTDOWN) { 1.2326 + RemoveFile(NS_LITERAL_CSTRING(kTempIndexName)); 1.2327 + RemoveFile(NS_LITERAL_CSTRING(kJournalName)); 1.2328 + } else { 1.2329 + if (mIndexHandle && !mIndexOnDiskIsValid) { 1.2330 + CacheFileIOManager::DoomFile(mIndexHandle, nullptr); 1.2331 + } 1.2332 + 1.2333 + if (mJournalHandle) { 1.2334 + CacheFileIOManager::DoomFile(mJournalHandle, nullptr); 1.2335 + } 1.2336 + } 1.2337 + 1.2338 + if (mIndexFileOpener) { 1.2339 + mIndexFileOpener->Cancel(); 1.2340 + mIndexFileOpener = nullptr; 1.2341 + } 1.2342 + if (mJournalFileOpener) { 1.2343 + mJournalFileOpener->Cancel(); 1.2344 + mJournalFileOpener = nullptr; 1.2345 + } 1.2346 + if (mTmpFileOpener) { 1.2347 + mTmpFileOpener->Cancel(); 1.2348 + mTmpFileOpener = nullptr; 1.2349 + } 1.2350 + 1.2351 + mIndexHandle = nullptr; 1.2352 + mJournalHandle = nullptr; 1.2353 + mRWHash = nullptr; 1.2354 + ReleaseBuffer(); 1.2355 + 1.2356 + if (mState == SHUTDOWN) { 1.2357 + return; 1.2358 + } 1.2359 + 1.2360 + if (!mIndexOnDiskIsValid) { 1.2361 + MOZ_ASSERT(mTmpJournal.Count() == 0); 1.2362 + EnsureNoFreshEntry(); 1.2363 + ProcessPendingOperations(); 1.2364 + // Remove all entries that we haven't seen during this session 1.2365 + mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this); 1.2366 + StartUpdatingIndex(true); 1.2367 + return; 1.2368 + } 1.2369 + 1.2370 + if (!mJournalReadSuccessfully) { 1.2371 + mTmpJournal.Clear(); 1.2372 + EnsureNoFreshEntry(); 1.2373 + ProcessPendingOperations(); 1.2374 + StartUpdatingIndex(false); 1.2375 + return; 1.2376 + } 1.2377 + 1.2378 + MergeJournal(); 1.2379 + EnsureNoFreshEntry(); 1.2380 + ProcessPendingOperations(); 1.2381 + mIndexStats.Log(); 1.2382 + 1.2383 + ChangeState(READY); 1.2384 + mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately 1.2385 +} 1.2386 + 1.2387 +// static 1.2388 +void 1.2389 +CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure) 1.2390 +{ 1.2391 + LOG(("CacheIndex::DelayedUpdate()")); 1.2392 + 1.2393 + nsresult rv; 1.2394 + nsRefPtr<CacheIndex> index = gInstance; 1.2395 + 1.2396 + if (!index) { 1.2397 + return; 1.2398 + } 1.2399 + 1.2400 + CacheIndexAutoLock lock(index); 1.2401 + 1.2402 + index->mUpdateTimer = nullptr; 1.2403 + 1.2404 + if (!index->IsIndexUsable()) { 1.2405 + return; 1.2406 + } 1.2407 + 1.2408 + if (index->mState == READY && index->mShuttingDown) { 1.2409 + return; 1.2410 + } 1.2411 + 1.2412 + // mUpdateEventPending must be false here since StartUpdatingIndex() won't 1.2413 + // schedule timer if it is true. 1.2414 + MOZ_ASSERT(!index->mUpdateEventPending); 1.2415 + if (index->mState != BUILDING && index->mState != UPDATING) { 1.2416 + LOG(("CacheIndex::DelayedUpdate() - Update was canceled")); 1.2417 + return; 1.2418 + } 1.2419 + 1.2420 + // We need to redispatch to run with lower priority 1.2421 + nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread(); 1.2422 + MOZ_ASSERT(ioThread); 1.2423 + 1.2424 + index->mUpdateEventPending = true; 1.2425 + rv = ioThread->Dispatch(index, CacheIOThread::INDEX); 1.2426 + if (NS_FAILED(rv)) { 1.2427 + index->mUpdateEventPending = false; 1.2428 + NS_WARNING("CacheIndex::DelayedUpdate() - Can't dispatch event"); 1.2429 + LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event" )); 1.2430 + index->FinishUpdate(false); 1.2431 + } 1.2432 +} 1.2433 + 1.2434 +nsresult 1.2435 +CacheIndex::ScheduleUpdateTimer(uint32_t aDelay) 1.2436 +{ 1.2437 + LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay)); 1.2438 + 1.2439 + MOZ_ASSERT(!mUpdateTimer); 1.2440 + 1.2441 + nsresult rv; 1.2442 + 1.2443 + nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv); 1.2444 + NS_ENSURE_SUCCESS(rv, rv); 1.2445 + 1.2446 + nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget(); 1.2447 + MOZ_ASSERT(ioTarget); 1.2448 + 1.2449 + rv = timer->SetTarget(ioTarget); 1.2450 + NS_ENSURE_SUCCESS(rv, rv); 1.2451 + 1.2452 + rv = timer->InitWithFuncCallback(CacheIndex::DelayedUpdate, nullptr, 1.2453 + aDelay, nsITimer::TYPE_ONE_SHOT); 1.2454 + NS_ENSURE_SUCCESS(rv, rv); 1.2455 + 1.2456 + mUpdateTimer.swap(timer); 1.2457 + return NS_OK; 1.2458 +} 1.2459 + 1.2460 +nsresult 1.2461 +CacheIndex::SetupDirectoryEnumerator() 1.2462 +{ 1.2463 + MOZ_ASSERT(!NS_IsMainThread()); 1.2464 + MOZ_ASSERT(!mDirEnumerator); 1.2465 + 1.2466 + nsresult rv; 1.2467 + nsCOMPtr<nsIFile> file; 1.2468 + 1.2469 + rv = mCacheDirectory->Clone(getter_AddRefs(file)); 1.2470 + NS_ENSURE_SUCCESS(rv, rv); 1.2471 + 1.2472 + rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir)); 1.2473 + NS_ENSURE_SUCCESS(rv, rv); 1.2474 + 1.2475 + bool exists; 1.2476 + rv = file->Exists(&exists); 1.2477 + NS_ENSURE_SUCCESS(rv, rv); 1.2478 + 1.2479 + if (!exists) { 1.2480 + NS_WARNING("CacheIndex::SetupDirectoryEnumerator() - Entries directory " 1.2481 + "doesn't exist!"); 1.2482 + LOG(("CacheIndex::SetupDirectoryEnumerator() - Entries directory doesn't " 1.2483 + "exist!" )); 1.2484 + return NS_ERROR_UNEXPECTED; 1.2485 + } 1.2486 + 1.2487 + nsCOMPtr<nsISimpleEnumerator> enumerator; 1.2488 + rv = file->GetDirectoryEntries(getter_AddRefs(enumerator)); 1.2489 + NS_ENSURE_SUCCESS(rv, rv); 1.2490 + 1.2491 + mDirEnumerator = do_QueryInterface(enumerator, &rv); 1.2492 + NS_ENSURE_SUCCESS(rv, rv); 1.2493 + 1.2494 + return NS_OK; 1.2495 +} 1.2496 + 1.2497 +void 1.2498 +CacheIndex::InitEntryFromDiskData(CacheIndexEntry *aEntry, 1.2499 + CacheFileMetadata *aMetaData, 1.2500 + int64_t aFileSize) 1.2501 +{ 1.2502 + aEntry->InitNew(); 1.2503 + aEntry->MarkDirty(); 1.2504 + aEntry->MarkFresh(); 1.2505 + aEntry->Init(aMetaData->AppId(), aMetaData->IsAnonymous(), 1.2506 + aMetaData->IsInBrowser()); 1.2507 + 1.2508 + uint32_t expirationTime; 1.2509 + aMetaData->GetExpirationTime(&expirationTime); 1.2510 + aEntry->SetExpirationTime(expirationTime); 1.2511 + 1.2512 + uint32_t frecency; 1.2513 + aMetaData->GetFrecency(&frecency); 1.2514 + aEntry->SetFrecency(frecency); 1.2515 + 1.2516 + aEntry->SetFileSize(static_cast<uint32_t>( 1.2517 + std::min(static_cast<int64_t>(PR_UINT32_MAX), 1.2518 + (aFileSize + 0x3FF) >> 10))); 1.2519 +} 1.2520 + 1.2521 +bool 1.2522 +CacheIndex::IsUpdatePending() 1.2523 +{ 1.2524 + AssertOwnsLock(); 1.2525 + 1.2526 + if (mUpdateTimer || mUpdateEventPending) { 1.2527 + return true; 1.2528 + } 1.2529 + 1.2530 + return false; 1.2531 +} 1.2532 + 1.2533 +void 1.2534 +CacheIndex::BuildIndex() 1.2535 +{ 1.2536 + LOG(("CacheIndex::BuildIndex()")); 1.2537 + 1.2538 + AssertOwnsLock(); 1.2539 + 1.2540 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.2541 + 1.2542 + nsresult rv; 1.2543 + 1.2544 + if (!mDirEnumerator) { 1.2545 + { 1.2546 + // Do not do IO under the lock. 1.2547 + CacheIndexAutoUnlock unlock(this); 1.2548 + rv = SetupDirectoryEnumerator(); 1.2549 + } 1.2550 + if (mState == SHUTDOWN) { 1.2551 + // The index was shut down while we released the lock. FinishUpdate() was 1.2552 + // already called from Shutdown(), so just simply return here. 1.2553 + return; 1.2554 + } 1.2555 + 1.2556 + if (NS_FAILED(rv)) { 1.2557 + FinishUpdate(false); 1.2558 + return; 1.2559 + } 1.2560 + } 1.2561 + 1.2562 + while (true) { 1.2563 + if (CacheIOThread::YieldAndRerun()) { 1.2564 + LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events.")); 1.2565 + mUpdateEventPending = true; 1.2566 + return; 1.2567 + } 1.2568 + 1.2569 + nsCOMPtr<nsIFile> file; 1.2570 + { 1.2571 + // Do not do IO under the lock. 1.2572 + CacheIndexAutoUnlock unlock(this); 1.2573 + rv = mDirEnumerator->GetNextFile(getter_AddRefs(file)); 1.2574 + } 1.2575 + if (mState == SHUTDOWN) { 1.2576 + return; 1.2577 + } 1.2578 + if (!file) { 1.2579 + FinishUpdate(NS_SUCCEEDED(rv)); 1.2580 + return; 1.2581 + } 1.2582 + 1.2583 + nsAutoCString leaf; 1.2584 + rv = file->GetNativeLeafName(leaf); 1.2585 + if (NS_FAILED(rv)) { 1.2586 + LOG(("CacheIndex::BuildIndex() - GetNativeLeafName() failed! Skipping " 1.2587 + "file.")); 1.2588 + mDontMarkIndexClean = true; 1.2589 + continue; 1.2590 + } 1.2591 + 1.2592 + SHA1Sum::Hash hash; 1.2593 + rv = CacheFileIOManager::StrToHash(leaf, &hash); 1.2594 + if (NS_FAILED(rv)) { 1.2595 + LOG(("CacheIndex::BuildIndex() - Filename is not a hash, removing file. " 1.2596 + "[name=%s]", leaf.get())); 1.2597 + file->Remove(false); 1.2598 + continue; 1.2599 + } 1.2600 + 1.2601 + CacheIndexEntry *entry = mIndex.GetEntry(hash); 1.2602 + if (entry && entry->IsRemoved()) { 1.2603 + LOG(("CacheIndex::BuildIndex() - Found file that should not exist. " 1.2604 + "[name=%s]", leaf.get())); 1.2605 + entry->Log(); 1.2606 + MOZ_ASSERT(entry->IsFresh()); 1.2607 + entry = nullptr; 1.2608 + } 1.2609 + 1.2610 +#ifdef DEBUG 1.2611 + nsRefPtr<CacheFileHandle> handle; 1.2612 + CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false, 1.2613 + getter_AddRefs(handle)); 1.2614 +#endif 1.2615 + 1.2616 + if (entry) { 1.2617 + // the entry is up to date 1.2618 + LOG(("CacheIndex::BuildIndex() - Skipping file because the entry is up to" 1.2619 + " date. [name=%s]", leaf.get())); 1.2620 + entry->Log(); 1.2621 + MOZ_ASSERT(entry->IsFresh()); // The entry must be from this session 1.2622 + // there must be an active CacheFile if the entry is not initialized 1.2623 + MOZ_ASSERT(entry->IsInitialized() || handle); 1.2624 + continue; 1.2625 + } 1.2626 + 1.2627 + MOZ_ASSERT(!handle); 1.2628 + 1.2629 + nsRefPtr<CacheFileMetadata> meta = new CacheFileMetadata(); 1.2630 + int64_t size = 0; 1.2631 + 1.2632 + { 1.2633 + // Do not do IO under the lock. 1.2634 + CacheIndexAutoUnlock unlock(this); 1.2635 + rv = meta->SyncReadMetadata(file); 1.2636 + 1.2637 + if (NS_SUCCEEDED(rv)) { 1.2638 + rv = file->GetFileSize(&size); 1.2639 + if (NS_FAILED(rv)) { 1.2640 + LOG(("CacheIndex::BuildIndex() - Cannot get filesize of file that was" 1.2641 + " successfully parsed. [name=%s]", leaf.get())); 1.2642 + } 1.2643 + } 1.2644 + } 1.2645 + if (mState == SHUTDOWN) { 1.2646 + return; 1.2647 + } 1.2648 + 1.2649 + // Nobody could add the entry while the lock was released since we modify 1.2650 + // the index only on IO thread and this loop is executed on IO thread too. 1.2651 + entry = mIndex.GetEntry(hash); 1.2652 + MOZ_ASSERT(!entry || entry->IsRemoved()); 1.2653 + 1.2654 + if (NS_FAILED(rv)) { 1.2655 + LOG(("CacheIndex::BuildIndex() - CacheFileMetadata::SyncReadMetadata() " 1.2656 + "failed, removing file. [name=%s]", leaf.get())); 1.2657 + file->Remove(false); 1.2658 + } else { 1.2659 + CacheIndexEntryAutoManage entryMng(&hash, this); 1.2660 + entry = mIndex.PutEntry(hash); 1.2661 + InitEntryFromDiskData(entry, meta, size); 1.2662 + LOG(("CacheIndex::BuildIndex() - Added entry to index. [hash=%s]", 1.2663 + leaf.get())); 1.2664 + entry->Log(); 1.2665 + } 1.2666 + } 1.2667 + 1.2668 + NS_NOTREACHED("We should never get here"); 1.2669 +} 1.2670 + 1.2671 +bool 1.2672 +CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState) 1.2673 +{ 1.2674 + // Start updating process when we are in or we are switching to READY state 1.2675 + // and index needs update, but not during shutdown or when removing all 1.2676 + // entries. 1.2677 + if ((mState == READY || aSwitchingToReadyState) && mIndexNeedsUpdate && 1.2678 + !mShuttingDown && !mRemovingAll) { 1.2679 + LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process")); 1.2680 + mIndexNeedsUpdate = false; 1.2681 + StartUpdatingIndex(false); 1.2682 + return true; 1.2683 + } 1.2684 + 1.2685 + return false; 1.2686 +} 1.2687 + 1.2688 +void 1.2689 +CacheIndex::StartUpdatingIndex(bool aRebuild) 1.2690 +{ 1.2691 + LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild)); 1.2692 + 1.2693 + AssertOwnsLock(); 1.2694 + 1.2695 + nsresult rv; 1.2696 + 1.2697 + mIndexStats.Log(); 1.2698 + 1.2699 + ChangeState(aRebuild ? BUILDING : UPDATING); 1.2700 + mDontMarkIndexClean = false; 1.2701 + 1.2702 + if (mShuttingDown || mRemovingAll) { 1.2703 + FinishUpdate(false); 1.2704 + return; 1.2705 + } 1.2706 + 1.2707 + if (IsUpdatePending()) { 1.2708 + LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending")); 1.2709 + return; 1.2710 + } 1.2711 + 1.2712 + uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds(); 1.2713 + if (elapsed < kUpdateIndexStartDelay) { 1.2714 + LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, " 1.2715 + "scheduling timer to fire in %u ms.", elapsed, 1.2716 + kUpdateIndexStartDelay - elapsed)); 1.2717 + rv = ScheduleUpdateTimer(kUpdateIndexStartDelay - elapsed); 1.2718 + if (NS_SUCCEEDED(rv)) { 1.2719 + return; 1.2720 + } 1.2721 + 1.2722 + LOG(("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. " 1.2723 + "Starting update immediately.")); 1.2724 + } else { 1.2725 + LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, " 1.2726 + "starting update now.", elapsed)); 1.2727 + } 1.2728 + 1.2729 + nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread(); 1.2730 + MOZ_ASSERT(ioThread); 1.2731 + 1.2732 + // We need to dispatch an event even if we are on IO thread since we need to 1.2733 + // update the index with the correct priority. 1.2734 + mUpdateEventPending = true; 1.2735 + rv = ioThread->Dispatch(this, CacheIOThread::INDEX); 1.2736 + if (NS_FAILED(rv)) { 1.2737 + mUpdateEventPending = false; 1.2738 + NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event"); 1.2739 + LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event" )); 1.2740 + FinishUpdate(false); 1.2741 + } 1.2742 +} 1.2743 + 1.2744 +void 1.2745 +CacheIndex::UpdateIndex() 1.2746 +{ 1.2747 + LOG(("CacheIndex::UpdateIndex()")); 1.2748 + 1.2749 + AssertOwnsLock(); 1.2750 + 1.2751 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.2752 + 1.2753 + nsresult rv; 1.2754 + 1.2755 + if (!mDirEnumerator) { 1.2756 + { 1.2757 + // Do not do IO under the lock. 1.2758 + CacheIndexAutoUnlock unlock(this); 1.2759 + rv = SetupDirectoryEnumerator(); 1.2760 + } 1.2761 + if (mState == SHUTDOWN) { 1.2762 + // The index was shut down while we released the lock. FinishUpdate() was 1.2763 + // already called from Shutdown(), so just simply return here. 1.2764 + return; 1.2765 + } 1.2766 + 1.2767 + if (NS_FAILED(rv)) { 1.2768 + FinishUpdate(false); 1.2769 + return; 1.2770 + } 1.2771 + } 1.2772 + 1.2773 + while (true) { 1.2774 + if (CacheIOThread::YieldAndRerun()) { 1.2775 + LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level " 1.2776 + "events.")); 1.2777 + mUpdateEventPending = true; 1.2778 + return; 1.2779 + } 1.2780 + 1.2781 + nsCOMPtr<nsIFile> file; 1.2782 + { 1.2783 + // Do not do IO under the lock. 1.2784 + CacheIndexAutoUnlock unlock(this); 1.2785 + rv = mDirEnumerator->GetNextFile(getter_AddRefs(file)); 1.2786 + } 1.2787 + if (mState == SHUTDOWN) { 1.2788 + return; 1.2789 + } 1.2790 + if (!file) { 1.2791 + FinishUpdate(NS_SUCCEEDED(rv)); 1.2792 + return; 1.2793 + } 1.2794 + 1.2795 + nsAutoCString leaf; 1.2796 + rv = file->GetNativeLeafName(leaf); 1.2797 + if (NS_FAILED(rv)) { 1.2798 + LOG(("CacheIndex::UpdateIndex() - GetNativeLeafName() failed! Skipping " 1.2799 + "file.")); 1.2800 + mDontMarkIndexClean = true; 1.2801 + continue; 1.2802 + } 1.2803 + 1.2804 + SHA1Sum::Hash hash; 1.2805 + rv = CacheFileIOManager::StrToHash(leaf, &hash); 1.2806 + if (NS_FAILED(rv)) { 1.2807 + LOG(("CacheIndex::UpdateIndex() - Filename is not a hash, removing file. " 1.2808 + "[name=%s]", leaf.get())); 1.2809 + file->Remove(false); 1.2810 + continue; 1.2811 + } 1.2812 + 1.2813 + CacheIndexEntry *entry = mIndex.GetEntry(hash); 1.2814 + if (entry && entry->IsRemoved()) { 1.2815 + if (entry->IsFresh()) { 1.2816 + LOG(("CacheIndex::UpdateIndex() - Found file that should not exist. " 1.2817 + "[name=%s]", leaf.get())); 1.2818 + entry->Log(); 1.2819 + } 1.2820 + entry = nullptr; 1.2821 + } 1.2822 + 1.2823 +#ifdef DEBUG 1.2824 + nsRefPtr<CacheFileHandle> handle; 1.2825 + CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false, 1.2826 + getter_AddRefs(handle)); 1.2827 +#endif 1.2828 + 1.2829 + if (entry && entry->IsFresh()) { 1.2830 + // the entry is up to date 1.2831 + LOG(("CacheIndex::UpdateIndex() - Skipping file because the entry is up " 1.2832 + " to date. [name=%s]", leaf.get())); 1.2833 + entry->Log(); 1.2834 + // there must be an active CacheFile if the entry is not initialized 1.2835 + MOZ_ASSERT(entry->IsInitialized() || handle); 1.2836 + continue; 1.2837 + } 1.2838 + 1.2839 + MOZ_ASSERT(!handle); 1.2840 + 1.2841 + if (entry) { 1.2842 + PRTime lastModifiedTime; 1.2843 + { 1.2844 + // Do not do IO under the lock. 1.2845 + CacheIndexAutoUnlock unlock(this); 1.2846 + rv = file->GetLastModifiedTime(&lastModifiedTime); 1.2847 + } 1.2848 + if (mState == SHUTDOWN) { 1.2849 + return; 1.2850 + } 1.2851 + if (NS_FAILED(rv)) { 1.2852 + LOG(("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. " 1.2853 + "[name=%s]", leaf.get())); 1.2854 + // Assume the file is newer than index 1.2855 + } else { 1.2856 + if (mIndexTimeStamp > (lastModifiedTime / PR_MSEC_PER_SEC)) { 1.2857 + LOG(("CacheIndex::UpdateIndex() - Skipping file because of last " 1.2858 + "modified time. [name=%s, indexTimeStamp=%u, " 1.2859 + "lastModifiedTime=%u]", leaf.get(), mIndexTimeStamp, 1.2860 + lastModifiedTime / PR_MSEC_PER_SEC)); 1.2861 + 1.2862 + CacheIndexEntryAutoManage entryMng(&hash, this); 1.2863 + entry->MarkFresh(); 1.2864 + continue; 1.2865 + } 1.2866 + } 1.2867 + } 1.2868 + 1.2869 + nsRefPtr<CacheFileMetadata> meta = new CacheFileMetadata(); 1.2870 + int64_t size = 0; 1.2871 + 1.2872 + { 1.2873 + // Do not do IO under the lock. 1.2874 + CacheIndexAutoUnlock unlock(this); 1.2875 + rv = meta->SyncReadMetadata(file); 1.2876 + 1.2877 + if (NS_SUCCEEDED(rv)) { 1.2878 + rv = file->GetFileSize(&size); 1.2879 + if (NS_FAILED(rv)) { 1.2880 + LOG(("CacheIndex::UpdateIndex() - Cannot get filesize of file that " 1.2881 + "was successfully parsed. [name=%s]", leaf.get())); 1.2882 + } 1.2883 + } 1.2884 + } 1.2885 + if (mState == SHUTDOWN) { 1.2886 + return; 1.2887 + } 1.2888 + 1.2889 + // Nobody could add the entry while the lock was released since we modify 1.2890 + // the index only on IO thread and this loop is executed on IO thread too. 1.2891 + entry = mIndex.GetEntry(hash); 1.2892 + MOZ_ASSERT(!entry || !entry->IsFresh()); 1.2893 + 1.2894 + CacheIndexEntryAutoManage entryMng(&hash, this); 1.2895 + 1.2896 + if (NS_FAILED(rv)) { 1.2897 + LOG(("CacheIndex::UpdateIndex() - CacheFileMetadata::SyncReadMetadata() " 1.2898 + "failed, removing file. [name=%s]", leaf.get())); 1.2899 + file->Remove(false); 1.2900 + if (entry) { 1.2901 + entry->MarkRemoved(); 1.2902 + entry->MarkFresh(); 1.2903 + entry->MarkDirty(); 1.2904 + } 1.2905 + } else { 1.2906 + entry = mIndex.PutEntry(hash); 1.2907 + InitEntryFromDiskData(entry, meta, size); 1.2908 + LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. " 1.2909 + "[hash=%s]", leaf.get())); 1.2910 + entry->Log(); 1.2911 + } 1.2912 + } 1.2913 + 1.2914 + NS_NOTREACHED("We should never get here"); 1.2915 +} 1.2916 + 1.2917 +void 1.2918 +CacheIndex::FinishUpdate(bool aSucceeded) 1.2919 +{ 1.2920 + LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded)); 1.2921 + 1.2922 + MOZ_ASSERT(mState == UPDATING || mState == BUILDING || 1.2923 + (!aSucceeded && mState == SHUTDOWN)); 1.2924 + 1.2925 + AssertOwnsLock(); 1.2926 + 1.2927 + if (mDirEnumerator) { 1.2928 + if (NS_IsMainThread()) { 1.2929 + LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?" 1.2930 + " Cannot safely release mDirEnumerator, leaking it!")); 1.2931 + NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!")); 1.2932 + // This can happen only in case dispatching event to IO thread failed in 1.2933 + // CacheIndex::PreShutdown(). 1.2934 + mDirEnumerator.forget(); // Leak it since dir enumerator is not threadsafe 1.2935 + } else { 1.2936 + mDirEnumerator->Close(); 1.2937 + mDirEnumerator = nullptr; 1.2938 + } 1.2939 + } 1.2940 + 1.2941 + if (!aSucceeded) { 1.2942 + mDontMarkIndexClean = true; 1.2943 + } 1.2944 + 1.2945 + if (mState == SHUTDOWN) { 1.2946 + return; 1.2947 + } 1.2948 + 1.2949 + if (mState == UPDATING && aSucceeded) { 1.2950 + // If we've iterated over all entries successfully then all entries that 1.2951 + // really exist on the disk are now marked as fresh. All non-fresh entries 1.2952 + // don't exist anymore and must be removed from the index. 1.2953 + mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this); 1.2954 + } 1.2955 + 1.2956 + // Make sure we won't start update. If the build or update failed, there is no 1.2957 + // reason to believe that it will succeed next time. 1.2958 + mIndexNeedsUpdate = false; 1.2959 + 1.2960 + ChangeState(READY); 1.2961 + mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately 1.2962 +} 1.2963 + 1.2964 +// static 1.2965 +PLDHashOperator 1.2966 +CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure) 1.2967 +{ 1.2968 + if (aEntry->IsFresh()) { 1.2969 + return PL_DHASH_NEXT; 1.2970 + } 1.2971 + 1.2972 + LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. " 1.2973 + "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry->Hash()))); 1.2974 + 1.2975 + CacheIndex *index = static_cast<CacheIndex *>(aClosure); 1.2976 + 1.2977 + CacheIndexEntryAutoManage emng(aEntry->Hash(), index); 1.2978 + emng.DoNotSearchInIndex(); 1.2979 + 1.2980 + return PL_DHASH_REMOVE; 1.2981 +} 1.2982 + 1.2983 +#ifdef PR_LOGGING 1.2984 +// static 1.2985 +char const * 1.2986 +CacheIndex::StateString(EState aState) 1.2987 +{ 1.2988 + switch (aState) { 1.2989 + case INITIAL: return "INITIAL"; 1.2990 + case READING: return "READING"; 1.2991 + case WRITING: return "WRITING"; 1.2992 + case BUILDING: return "BUILDING"; 1.2993 + case UPDATING: return "UPDATING"; 1.2994 + case READY: return "READY"; 1.2995 + case SHUTDOWN: return "SHUTDOWN"; 1.2996 + } 1.2997 + 1.2998 + MOZ_ASSERT(false, "Unexpected state!"); 1.2999 + return "?"; 1.3000 +} 1.3001 +#endif 1.3002 + 1.3003 +void 1.3004 +CacheIndex::ChangeState(EState aNewState) 1.3005 +{ 1.3006 + LOG(("CacheIndex::ChangeState() changing state %s -> %s", StateString(mState), 1.3007 + StateString(aNewState))); 1.3008 + 1.3009 + // All pending updates should be processed before changing state 1.3010 + MOZ_ASSERT(mPendingUpdates.Count() == 0); 1.3011 + 1.3012 + // PreShutdownInternal() should change the state to READY from every state. It 1.3013 + // may go through different states, but once we are in READY state the only 1.3014 + // possible transition is to SHUTDOWN state. 1.3015 + MOZ_ASSERT(!mShuttingDown || mState != READY || aNewState == SHUTDOWN); 1.3016 + 1.3017 + // Start updating process when switching to READY state if needed 1.3018 + if (aNewState == READY && StartUpdatingIndexIfNeeded(true)) { 1.3019 + return; 1.3020 + } 1.3021 + 1.3022 + // Try to evict entries over limit everytime we're leaving state READING, 1.3023 + // BUILDING or UPDATING, but not during shutdown or when removing all 1.3024 + // entries. 1.3025 + if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN && 1.3026 + (mState == READING || mState == BUILDING || mState == UPDATING)) { 1.3027 + CacheFileIOManager::EvictIfOverLimit(); 1.3028 + } 1.3029 + 1.3030 + mState = aNewState; 1.3031 + 1.3032 + if (mState != SHUTDOWN) { 1.3033 + CacheFileIOManager::CacheIndexStateChanged(); 1.3034 + } 1.3035 + 1.3036 + if (mState == READY && mDiskConsumptionObservers.Length()) { 1.3037 + for (uint32_t i = 0; i < mDiskConsumptionObservers.Length(); ++i) { 1.3038 + DiskConsumptionObserver* o = mDiskConsumptionObservers[i]; 1.3039 + // Safe to call under the lock. We always post to the main thread. 1.3040 + o->OnDiskConsumption(mIndexStats.Size() << 10); 1.3041 + } 1.3042 + 1.3043 + mDiskConsumptionObservers.Clear(); 1.3044 + } 1.3045 +} 1.3046 + 1.3047 +void 1.3048 +CacheIndex::AllocBuffer() 1.3049 +{ 1.3050 + switch (mState) { 1.3051 + case WRITING: 1.3052 + mRWBufSize = sizeof(CacheIndexHeader) + sizeof(CacheHash::Hash32_t) + 1.3053 + mProcessEntries * sizeof(CacheIndexRecord); 1.3054 + if (mRWBufSize > kMaxBufSize) { 1.3055 + mRWBufSize = kMaxBufSize; 1.3056 + } 1.3057 + break; 1.3058 + case READING: 1.3059 + mRWBufSize = kMaxBufSize; 1.3060 + break; 1.3061 + default: 1.3062 + MOZ_ASSERT(false, "Unexpected state!"); 1.3063 + } 1.3064 + 1.3065 + mRWBuf = static_cast<char *>(moz_xmalloc(mRWBufSize)); 1.3066 +} 1.3067 + 1.3068 +void 1.3069 +CacheIndex::ReleaseBuffer() 1.3070 +{ 1.3071 + if (!mRWBuf) { 1.3072 + return; 1.3073 + } 1.3074 + 1.3075 + free(mRWBuf); 1.3076 + mRWBuf = nullptr; 1.3077 + mRWBufSize = 0; 1.3078 + mRWBufPos = 0; 1.3079 +} 1.3080 + 1.3081 +namespace { // anon 1.3082 + 1.3083 +class FrecencyComparator 1.3084 +{ 1.3085 +public: 1.3086 + bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const { 1.3087 + return a->mFrecency == b->mFrecency; 1.3088 + } 1.3089 + bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const { 1.3090 + // Place entries with frecency 0 at the end of the array. 1.3091 + if (a->mFrecency == 0) { 1.3092 + return false; 1.3093 + } 1.3094 + if (b->mFrecency == 0) { 1.3095 + return true; 1.3096 + } 1.3097 + return a->mFrecency < b->mFrecency; 1.3098 + } 1.3099 +}; 1.3100 + 1.3101 +class ExpirationComparator 1.3102 +{ 1.3103 +public: 1.3104 + bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const { 1.3105 + return a->mExpirationTime == b->mExpirationTime; 1.3106 + } 1.3107 + bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const { 1.3108 + return a->mExpirationTime < b->mExpirationTime; 1.3109 + } 1.3110 +}; 1.3111 + 1.3112 +} // anon 1.3113 + 1.3114 +void 1.3115 +CacheIndex::InsertRecordToFrecencyArray(CacheIndexRecord *aRecord) 1.3116 +{ 1.3117 + LOG(("CacheIndex::InsertRecordToFrecencyArray() [record=%p, hash=%08x%08x%08x" 1.3118 + "%08x%08x]", aRecord, LOGSHA1(aRecord->mHash))); 1.3119 + 1.3120 + MOZ_ASSERT(!mFrecencyArray.Contains(aRecord)); 1.3121 + mFrecencyArray.InsertElementSorted(aRecord, FrecencyComparator()); 1.3122 +} 1.3123 + 1.3124 +void 1.3125 +CacheIndex::InsertRecordToExpirationArray(CacheIndexRecord *aRecord) 1.3126 +{ 1.3127 + LOG(("CacheIndex::InsertRecordToExpirationArray() [record=%p, hash=%08x%08x" 1.3128 + "%08x%08x%08x]", aRecord, LOGSHA1(aRecord->mHash))); 1.3129 + 1.3130 + MOZ_ASSERT(!mExpirationArray.Contains(aRecord)); 1.3131 + mExpirationArray.InsertElementSorted(aRecord, ExpirationComparator()); 1.3132 +} 1.3133 + 1.3134 +void 1.3135 +CacheIndex::RemoveRecordFromFrecencyArray(CacheIndexRecord *aRecord) 1.3136 +{ 1.3137 + LOG(("CacheIndex::RemoveRecordFromFrecencyArray() [record=%p]", aRecord)); 1.3138 + 1.3139 + DebugOnly<bool> removed; 1.3140 + removed = mFrecencyArray.RemoveElement(aRecord); 1.3141 + MOZ_ASSERT(removed); 1.3142 +} 1.3143 + 1.3144 +void 1.3145 +CacheIndex::RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord) 1.3146 +{ 1.3147 + LOG(("CacheIndex::RemoveRecordFromExpirationArray() [record=%p]", aRecord)); 1.3148 + 1.3149 + DebugOnly<bool> removed; 1.3150 + removed = mExpirationArray.RemoveElement(aRecord); 1.3151 + MOZ_ASSERT(removed); 1.3152 +} 1.3153 + 1.3154 +void 1.3155 +CacheIndex::AddRecordToIterators(CacheIndexRecord *aRecord) 1.3156 +{ 1.3157 + AssertOwnsLock(); 1.3158 + 1.3159 + for (uint32_t i = 0; i < mIterators.Length(); ++i) { 1.3160 + // Add a new record only when iterator is supposed to be updated. 1.3161 + if (mIterators[i]->ShouldBeNewAdded()) { 1.3162 + mIterators[i]->AddRecord(aRecord); 1.3163 + } 1.3164 + } 1.3165 +} 1.3166 + 1.3167 +void 1.3168 +CacheIndex::RemoveRecordFromIterators(CacheIndexRecord *aRecord) 1.3169 +{ 1.3170 + AssertOwnsLock(); 1.3171 + 1.3172 + for (uint32_t i = 0; i < mIterators.Length(); ++i) { 1.3173 + // Remove the record from iterator always, it makes no sence to return 1.3174 + // non-existing entries. Also the pointer to the record is no longer valid 1.3175 + // once the entry is removed from index. 1.3176 + mIterators[i]->RemoveRecord(aRecord); 1.3177 + } 1.3178 +} 1.3179 + 1.3180 +void 1.3181 +CacheIndex::ReplaceRecordInIterators(CacheIndexRecord *aOldRecord, 1.3182 + CacheIndexRecord *aNewRecord) 1.3183 +{ 1.3184 + AssertOwnsLock(); 1.3185 + 1.3186 + for (uint32_t i = 0; i < mIterators.Length(); ++i) { 1.3187 + // We have to replace the record always since the pointer is no longer 1.3188 + // valid after this point. NOTE: Replacing the record doesn't mean that 1.3189 + // a new entry was added, it just means that the data in the entry was 1.3190 + // changed (e.g. a file size) and we had to track this change in 1.3191 + // mPendingUpdates since mIndex was read-only. 1.3192 + mIterators[i]->ReplaceRecord(aOldRecord, aNewRecord); 1.3193 + } 1.3194 +} 1.3195 + 1.3196 +nsresult 1.3197 +CacheIndex::Run() 1.3198 +{ 1.3199 + LOG(("CacheIndex::Run()")); 1.3200 + 1.3201 + CacheIndexAutoLock lock(this); 1.3202 + 1.3203 + if (!IsIndexUsable()) { 1.3204 + return NS_ERROR_NOT_AVAILABLE; 1.3205 + } 1.3206 + 1.3207 + if (mState == READY && mShuttingDown) { 1.3208 + return NS_OK; 1.3209 + } 1.3210 + 1.3211 + mUpdateEventPending = false; 1.3212 + 1.3213 + switch (mState) { 1.3214 + case BUILDING: 1.3215 + BuildIndex(); 1.3216 + break; 1.3217 + case UPDATING: 1.3218 + UpdateIndex(); 1.3219 + break; 1.3220 + default: 1.3221 + LOG(("CacheIndex::Run() - Update/Build was canceled")); 1.3222 + } 1.3223 + 1.3224 + return NS_OK; 1.3225 +} 1.3226 + 1.3227 +nsresult 1.3228 +CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener, 1.3229 + CacheFileHandle *aHandle, nsresult aResult) 1.3230 +{ 1.3231 + LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, " 1.3232 + "result=0x%08x]", aOpener, aHandle, aResult)); 1.3233 + 1.3234 + nsresult rv; 1.3235 + 1.3236 + AssertOwnsLock(); 1.3237 + 1.3238 + if (!IsIndexUsable()) { 1.3239 + return NS_ERROR_NOT_AVAILABLE; 1.3240 + } 1.3241 + 1.3242 + if (mState == READY && mShuttingDown) { 1.3243 + return NS_OK; 1.3244 + } 1.3245 + 1.3246 + switch (mState) { 1.3247 + case WRITING: 1.3248 + MOZ_ASSERT(aOpener == mIndexFileOpener); 1.3249 + mIndexFileOpener = nullptr; 1.3250 + 1.3251 + if (NS_FAILED(aResult)) { 1.3252 + LOG(("CacheIndex::OnFileOpenedInternal() - Can't open index file for " 1.3253 + "writing [rv=0x%08x]", aResult)); 1.3254 + FinishWrite(false); 1.3255 + } else { 1.3256 + mIndexHandle = aHandle; 1.3257 + WriteRecords(); 1.3258 + } 1.3259 + break; 1.3260 + case READING: 1.3261 + if (aOpener == mIndexFileOpener) { 1.3262 + mIndexFileOpener = nullptr; 1.3263 + 1.3264 + if (NS_SUCCEEDED(aResult)) { 1.3265 + if (aHandle->FileSize() == 0) { 1.3266 + FinishRead(false); 1.3267 + CacheFileIOManager::DoomFile(aHandle, nullptr); 1.3268 + break; 1.3269 + } else { 1.3270 + mIndexHandle = aHandle; 1.3271 + } 1.3272 + } else { 1.3273 + FinishRead(false); 1.3274 + break; 1.3275 + } 1.3276 + } else if (aOpener == mJournalFileOpener) { 1.3277 + mJournalFileOpener = nullptr; 1.3278 + mJournalHandle = aHandle; 1.3279 + } else if (aOpener == mTmpFileOpener) { 1.3280 + mTmpFileOpener = nullptr; 1.3281 + mTmpHandle = aHandle; 1.3282 + } else { 1.3283 + MOZ_ASSERT(false, "Unexpected state!"); 1.3284 + } 1.3285 + 1.3286 + if (mIndexFileOpener || mJournalFileOpener || mTmpFileOpener) { 1.3287 + // Some opener still didn't finish 1.3288 + break; 1.3289 + } 1.3290 + 1.3291 + // We fail and cancel all other openers when we opening index file fails. 1.3292 + MOZ_ASSERT(mIndexHandle); 1.3293 + 1.3294 + if (mTmpHandle) { 1.3295 + CacheFileIOManager::DoomFile(mTmpHandle, nullptr); 1.3296 + mTmpHandle = nullptr; 1.3297 + 1.3298 + if (mJournalHandle) { // this shouldn't normally happen 1.3299 + LOG(("CacheIndex::OnFileOpenedInternal() - Unexpected state, all " 1.3300 + "files [%s, %s, %s] should never exist. Removing whole index.", 1.3301 + kIndexName, kJournalName, kTempIndexName)); 1.3302 + FinishRead(false); 1.3303 + break; 1.3304 + } 1.3305 + } 1.3306 + 1.3307 + if (mJournalHandle) { 1.3308 + // Rename journal to make sure we update index on next start in case 1.3309 + // firefox crashes 1.3310 + rv = CacheFileIOManager::RenameFile( 1.3311 + mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this); 1.3312 + if (NS_FAILED(rv)) { 1.3313 + LOG(("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::" 1.3314 + "RenameFile() failed synchronously [rv=0x%08x]", rv)); 1.3315 + FinishRead(false); 1.3316 + break; 1.3317 + } 1.3318 + } else { 1.3319 + StartReadingIndex(); 1.3320 + } 1.3321 + 1.3322 + break; 1.3323 + default: 1.3324 + MOZ_ASSERT(false, "Unexpected state!"); 1.3325 + } 1.3326 + 1.3327 + return NS_OK; 1.3328 +} 1.3329 + 1.3330 +nsresult 1.3331 +CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) 1.3332 +{ 1.3333 + MOZ_CRASH("CacheIndex::OnFileOpened should not be called!"); 1.3334 + return NS_ERROR_UNEXPECTED; 1.3335 +} 1.3336 + 1.3337 +nsresult 1.3338 +CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, 1.3339 + nsresult aResult) 1.3340 +{ 1.3341 + LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle, 1.3342 + aResult)); 1.3343 + 1.3344 + nsresult rv; 1.3345 + 1.3346 + CacheIndexAutoLock lock(this); 1.3347 + 1.3348 + if (!IsIndexUsable()) { 1.3349 + return NS_ERROR_NOT_AVAILABLE; 1.3350 + } 1.3351 + 1.3352 + if (mState == READY && mShuttingDown) { 1.3353 + return NS_OK; 1.3354 + } 1.3355 + 1.3356 + switch (mState) { 1.3357 + case WRITING: 1.3358 + if (mIndexHandle != aHandle) { 1.3359 + LOG(("CacheIndex::OnDataWritten() - ignoring notification since it " 1.3360 + "belongs to previously canceled operation [state=%d]", mState)); 1.3361 + break; 1.3362 + } 1.3363 + 1.3364 + if (NS_FAILED(aResult)) { 1.3365 + FinishWrite(false); 1.3366 + } else { 1.3367 + if (mSkipEntries == mProcessEntries) { 1.3368 + rv = CacheFileIOManager::RenameFile(mIndexHandle, 1.3369 + NS_LITERAL_CSTRING(kIndexName), 1.3370 + this); 1.3371 + if (NS_FAILED(rv)) { 1.3372 + LOG(("CacheIndex::OnDataWritten() - CacheFileIOManager::" 1.3373 + "RenameFile() failed synchronously [rv=0x%08x]", rv)); 1.3374 + FinishWrite(false); 1.3375 + } 1.3376 + } else { 1.3377 + WriteRecords(); 1.3378 + } 1.3379 + } 1.3380 + break; 1.3381 + default: 1.3382 + // Writing was canceled. 1.3383 + LOG(("CacheIndex::OnDataWritten() - ignoring notification since the " 1.3384 + "operation was previously canceled [state=%d]", mState)); 1.3385 + } 1.3386 + 1.3387 + return NS_OK; 1.3388 +} 1.3389 + 1.3390 +nsresult 1.3391 +CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) 1.3392 +{ 1.3393 + LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08x]", aHandle, 1.3394 + aResult)); 1.3395 + 1.3396 + CacheIndexAutoLock lock(this); 1.3397 + 1.3398 + if (!IsIndexUsable()) { 1.3399 + return NS_ERROR_NOT_AVAILABLE; 1.3400 + } 1.3401 + 1.3402 + switch (mState) { 1.3403 + case READING: 1.3404 + MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle); 1.3405 + 1.3406 + if (NS_FAILED(aResult)) { 1.3407 + FinishRead(false); 1.3408 + } else { 1.3409 + if (!mIndexOnDiskIsValid) { 1.3410 + ParseRecords(); 1.3411 + } else { 1.3412 + ParseJournal(); 1.3413 + } 1.3414 + } 1.3415 + break; 1.3416 + default: 1.3417 + // Reading was canceled. 1.3418 + LOG(("CacheIndex::OnDataRead() - ignoring notification since the " 1.3419 + "operation was previously canceled [state=%d]", mState)); 1.3420 + } 1.3421 + 1.3422 + return NS_OK; 1.3423 +} 1.3424 + 1.3425 +nsresult 1.3426 +CacheIndex::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) 1.3427 +{ 1.3428 + MOZ_CRASH("CacheIndex::OnFileDoomed should not be called!"); 1.3429 + return NS_ERROR_UNEXPECTED; 1.3430 +} 1.3431 + 1.3432 +nsresult 1.3433 +CacheIndex::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) 1.3434 +{ 1.3435 + MOZ_CRASH("CacheIndex::OnEOFSet should not be called!"); 1.3436 + return NS_ERROR_UNEXPECTED; 1.3437 +} 1.3438 + 1.3439 +nsresult 1.3440 +CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) 1.3441 +{ 1.3442 + LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08x]", aHandle, 1.3443 + aResult)); 1.3444 + 1.3445 + CacheIndexAutoLock lock(this); 1.3446 + 1.3447 + if (!IsIndexUsable()) { 1.3448 + return NS_ERROR_NOT_AVAILABLE; 1.3449 + } 1.3450 + 1.3451 + if (mState == READY && mShuttingDown) { 1.3452 + return NS_OK; 1.3453 + } 1.3454 + 1.3455 + switch (mState) { 1.3456 + case WRITING: 1.3457 + // This is a result of renaming the new index written to tmpfile to index 1.3458 + // file. This is the last step when writing the index and the whole 1.3459 + // writing process is successful iff renaming was successful. 1.3460 + 1.3461 + if (mIndexHandle != aHandle) { 1.3462 + LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it " 1.3463 + "belongs to previously canceled operation [state=%d]", mState)); 1.3464 + break; 1.3465 + } 1.3466 + 1.3467 + FinishWrite(NS_SUCCEEDED(aResult)); 1.3468 + break; 1.3469 + case READING: 1.3470 + // This is a result of renaming journal file to tmpfile. It is renamed 1.3471 + // before we start reading index and journal file and it should normally 1.3472 + // succeed. If it fails give up reading of index. 1.3473 + 1.3474 + if (mJournalHandle != aHandle) { 1.3475 + LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it " 1.3476 + "belongs to previously canceled operation [state=%d]", mState)); 1.3477 + break; 1.3478 + } 1.3479 + 1.3480 + if (NS_FAILED(aResult)) { 1.3481 + FinishRead(false); 1.3482 + } else { 1.3483 + StartReadingIndex(); 1.3484 + } 1.3485 + break; 1.3486 + default: 1.3487 + // Reading/writing was canceled. 1.3488 + LOG(("CacheIndex::OnFileRenamed() - ignoring notification since the " 1.3489 + "operation was previously canceled [state=%d]", mState)); 1.3490 + } 1.3491 + 1.3492 + return NS_OK; 1.3493 +} 1.3494 + 1.3495 +// Memory reporting 1.3496 + 1.3497 +namespace { // anon 1.3498 + 1.3499 +size_t 1.3500 +CollectIndexEntryMemory(CacheIndexEntry* aEntry, 1.3501 + mozilla::MallocSizeOf mallocSizeOf, 1.3502 + void *arg) 1.3503 +{ 1.3504 + return aEntry->SizeOfExcludingThis(mallocSizeOf); 1.3505 +} 1.3506 + 1.3507 +} // anon 1.3508 + 1.3509 +size_t 1.3510 +CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const 1.3511 +{ 1.3512 + CacheIndexAutoLock lock(const_cast<CacheIndex*>(this)); 1.3513 + 1.3514 + size_t n = 0; 1.3515 + nsCOMPtr<nsISizeOf> sizeOf; 1.3516 + 1.3517 + // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable 1.3518 + // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special 1.3519 + // handles array. 1.3520 + 1.3521 + sizeOf = do_QueryInterface(mCacheDirectory); 1.3522 + if (sizeOf) { 1.3523 + n += sizeOf->SizeOfIncludingThis(mallocSizeOf); 1.3524 + } 1.3525 + 1.3526 + sizeOf = do_QueryInterface(mUpdateTimer); 1.3527 + if (sizeOf) { 1.3528 + n += sizeOf->SizeOfIncludingThis(mallocSizeOf); 1.3529 + } 1.3530 + 1.3531 + n += mallocSizeOf(mRWBuf); 1.3532 + n += mallocSizeOf(mRWHash); 1.3533 + 1.3534 + n += mIndex.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf); 1.3535 + n += mPendingUpdates.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf); 1.3536 + n += mTmpJournal.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf); 1.3537 + 1.3538 + // mFrecencyArray and mExpirationArray items are reported by 1.3539 + // mIndex/mPendingUpdates 1.3540 + n += mFrecencyArray.SizeOfExcludingThis(mallocSizeOf); 1.3541 + n += mExpirationArray.SizeOfExcludingThis(mallocSizeOf); 1.3542 + n += mDiskConsumptionObservers.SizeOfExcludingThis(mallocSizeOf); 1.3543 + 1.3544 + return n; 1.3545 +} 1.3546 + 1.3547 +// static 1.3548 +size_t 1.3549 +CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.3550 +{ 1.3551 + if (!gInstance) 1.3552 + return 0; 1.3553 + 1.3554 + return gInstance->SizeOfExcludingThisInternal(mallocSizeOf); 1.3555 +} 1.3556 + 1.3557 +// static 1.3558 +size_t 1.3559 +CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.3560 +{ 1.3561 + return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf); 1.3562 +} 1.3563 + 1.3564 +} // net 1.3565 +} // mozilla