netwerk/cache2/CacheIndex.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CacheIndex.h"
michael@0 6
michael@0 7 #include "CacheLog.h"
michael@0 8 #include "CacheFileIOManager.h"
michael@0 9 #include "CacheFileMetadata.h"
michael@0 10 #include "CacheIndexIterator.h"
michael@0 11 #include "CacheIndexContextIterator.h"
michael@0 12 #include "nsThreadUtils.h"
michael@0 13 #include "nsISimpleEnumerator.h"
michael@0 14 #include "nsIDirectoryEnumerator.h"
michael@0 15 #include "nsISizeOf.h"
michael@0 16 #include "nsPrintfCString.h"
michael@0 17 #include "mozilla/DebugOnly.h"
michael@0 18 #include "prinrval.h"
michael@0 19 #include "nsIFile.h"
michael@0 20 #include "nsITimer.h"
michael@0 21 #include "mozilla/AutoRestore.h"
michael@0 22 #include <algorithm>
michael@0 23
michael@0 24
michael@0 25 #define kMinUnwrittenChanges 300
michael@0 26 #define kMinDumpInterval 20000 // in milliseconds
michael@0 27 #define kMaxBufSize 16384
michael@0 28 #define kIndexVersion 0x00000001
michael@0 29 #define kUpdateIndexStartDelay 50000 // in milliseconds
michael@0 30
michael@0 31 const char kIndexName[] = "index";
michael@0 32 const char kTempIndexName[] = "index.tmp";
michael@0 33 const char kJournalName[] = "index.log";
michael@0 34
michael@0 35 namespace mozilla {
michael@0 36 namespace net {
michael@0 37
michael@0 38 /**
michael@0 39 * This helper class is responsible for keeping CacheIndex::mIndexStats,
michael@0 40 * CacheIndex::mFrecencyArray and CacheIndex::mExpirationArray up to date.
michael@0 41 */
michael@0 42 class CacheIndexEntryAutoManage
michael@0 43 {
michael@0 44 public:
michael@0 45 CacheIndexEntryAutoManage(const SHA1Sum::Hash *aHash, CacheIndex *aIndex)
michael@0 46 : mIndex(aIndex)
michael@0 47 , mOldRecord(nullptr)
michael@0 48 , mOldFrecency(0)
michael@0 49 , mOldExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
michael@0 50 , mDoNotSearchInIndex(false)
michael@0 51 , mDoNotSearchInUpdates(false)
michael@0 52 {
michael@0 53 mIndex->AssertOwnsLock();
michael@0 54
michael@0 55 mHash = aHash;
michael@0 56 CacheIndexEntry *entry = FindEntry();
michael@0 57 mIndex->mIndexStats.BeforeChange(entry);
michael@0 58 if (entry && entry->IsInitialized() && !entry->IsRemoved()) {
michael@0 59 mOldRecord = entry->mRec;
michael@0 60 mOldFrecency = entry->mRec->mFrecency;
michael@0 61 mOldExpirationTime = entry->mRec->mExpirationTime;
michael@0 62 }
michael@0 63 }
michael@0 64
michael@0 65 ~CacheIndexEntryAutoManage()
michael@0 66 {
michael@0 67 mIndex->AssertOwnsLock();
michael@0 68
michael@0 69 CacheIndexEntry *entry = FindEntry();
michael@0 70 mIndex->mIndexStats.AfterChange(entry);
michael@0 71 if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
michael@0 72 entry = nullptr;
michael@0 73 }
michael@0 74
michael@0 75 if (entry && !mOldRecord) {
michael@0 76 mIndex->InsertRecordToFrecencyArray(entry->mRec);
michael@0 77 mIndex->InsertRecordToExpirationArray(entry->mRec);
michael@0 78 mIndex->AddRecordToIterators(entry->mRec);
michael@0 79 } else if (!entry && mOldRecord) {
michael@0 80 mIndex->RemoveRecordFromFrecencyArray(mOldRecord);
michael@0 81 mIndex->RemoveRecordFromExpirationArray(mOldRecord);
michael@0 82 mIndex->RemoveRecordFromIterators(mOldRecord);
michael@0 83 } else if (entry && mOldRecord) {
michael@0 84 bool replaceFrecency = false;
michael@0 85 bool replaceExpiration = false;
michael@0 86
michael@0 87 if (entry->mRec != mOldRecord) {
michael@0 88 // record has a different address, we have to replace it
michael@0 89 replaceFrecency = replaceExpiration = true;
michael@0 90 mIndex->ReplaceRecordInIterators(mOldRecord, entry->mRec);
michael@0 91 } else {
michael@0 92 if (entry->mRec->mFrecency == 0 &&
michael@0 93 entry->mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME) {
michael@0 94 // This is a special case when we want to make sure that the entry is
michael@0 95 // placed at the end of the lists even when the values didn't change.
michael@0 96 replaceFrecency = replaceExpiration = true;
michael@0 97 } else {
michael@0 98 if (entry->mRec->mFrecency != mOldFrecency) {
michael@0 99 replaceFrecency = true;
michael@0 100 }
michael@0 101 if (entry->mRec->mExpirationTime != mOldExpirationTime) {
michael@0 102 replaceExpiration = true;
michael@0 103 }
michael@0 104 }
michael@0 105 }
michael@0 106
michael@0 107 if (replaceFrecency) {
michael@0 108 mIndex->RemoveRecordFromFrecencyArray(mOldRecord);
michael@0 109 mIndex->InsertRecordToFrecencyArray(entry->mRec);
michael@0 110 }
michael@0 111 if (replaceExpiration) {
michael@0 112 mIndex->RemoveRecordFromExpirationArray(mOldRecord);
michael@0 113 mIndex->InsertRecordToExpirationArray(entry->mRec);
michael@0 114 }
michael@0 115 } else {
michael@0 116 // both entries were removed or not initialized, do nothing
michael@0 117 }
michael@0 118 }
michael@0 119
michael@0 120 // We cannot rely on nsTHashtable::GetEntry() in case we are enumerating the
michael@0 121 // entries and returning PL_DHASH_REMOVE. Destructor is called before the
michael@0 122 // entry is removed. Caller must call one of following methods to skip
michael@0 123 // lookup in the hashtable.
michael@0 124 void DoNotSearchInIndex() { mDoNotSearchInIndex = true; }
michael@0 125 void DoNotSearchInUpdates() { mDoNotSearchInUpdates = true; }
michael@0 126
michael@0 127 private:
michael@0 128 CacheIndexEntry * FindEntry()
michael@0 129 {
michael@0 130 CacheIndexEntry *entry = nullptr;
michael@0 131
michael@0 132 switch (mIndex->mState) {
michael@0 133 case CacheIndex::READING:
michael@0 134 case CacheIndex::WRITING:
michael@0 135 if (!mDoNotSearchInUpdates) {
michael@0 136 entry = mIndex->mPendingUpdates.GetEntry(*mHash);
michael@0 137 }
michael@0 138 // no break
michael@0 139 case CacheIndex::BUILDING:
michael@0 140 case CacheIndex::UPDATING:
michael@0 141 case CacheIndex::READY:
michael@0 142 if (!entry && !mDoNotSearchInIndex) {
michael@0 143 entry = mIndex->mIndex.GetEntry(*mHash);
michael@0 144 }
michael@0 145 break;
michael@0 146 case CacheIndex::INITIAL:
michael@0 147 case CacheIndex::SHUTDOWN:
michael@0 148 default:
michael@0 149 MOZ_ASSERT(false, "Unexpected state!");
michael@0 150 }
michael@0 151
michael@0 152 return entry;
michael@0 153 }
michael@0 154
michael@0 155 const SHA1Sum::Hash *mHash;
michael@0 156 nsRefPtr<CacheIndex> mIndex;
michael@0 157 CacheIndexRecord *mOldRecord;
michael@0 158 uint32_t mOldFrecency;
michael@0 159 uint32_t mOldExpirationTime;
michael@0 160 bool mDoNotSearchInIndex;
michael@0 161 bool mDoNotSearchInUpdates;
michael@0 162 };
michael@0 163
michael@0 164 class FileOpenHelper : public CacheFileIOListener
michael@0 165 {
michael@0 166 public:
michael@0 167 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 168
michael@0 169 FileOpenHelper(CacheIndex* aIndex)
michael@0 170 : mIndex(aIndex)
michael@0 171 , mCanceled(false)
michael@0 172 {}
michael@0 173
michael@0 174 virtual ~FileOpenHelper() {}
michael@0 175
michael@0 176 void Cancel() {
michael@0 177 mIndex->AssertOwnsLock();
michael@0 178 mCanceled = true;
michael@0 179 }
michael@0 180
michael@0 181 private:
michael@0 182 NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
michael@0 183 NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
michael@0 184 nsresult aResult) {
michael@0 185 MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!");
michael@0 186 return NS_ERROR_UNEXPECTED;
michael@0 187 }
michael@0 188 NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf,
michael@0 189 nsresult aResult) {
michael@0 190 MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!");
michael@0 191 return NS_ERROR_UNEXPECTED;
michael@0 192 }
michael@0 193 NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) {
michael@0 194 MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!");
michael@0 195 return NS_ERROR_UNEXPECTED;
michael@0 196 }
michael@0 197 NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) {
michael@0 198 MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!");
michael@0 199 return NS_ERROR_UNEXPECTED;
michael@0 200 }
michael@0 201 NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) {
michael@0 202 MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!");
michael@0 203 return NS_ERROR_UNEXPECTED;
michael@0 204 }
michael@0 205
michael@0 206 nsRefPtr<CacheIndex> mIndex;
michael@0 207 bool mCanceled;
michael@0 208 };
michael@0 209
michael@0 210 NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle,
michael@0 211 nsresult aResult)
michael@0 212 {
michael@0 213 CacheIndexAutoLock lock(mIndex);
michael@0 214
michael@0 215 if (mCanceled) {
michael@0 216 if (aHandle) {
michael@0 217 CacheFileIOManager::DoomFile(aHandle, nullptr);
michael@0 218 }
michael@0 219
michael@0 220 return NS_OK;
michael@0 221 }
michael@0 222
michael@0 223 mIndex->OnFileOpenedInternal(this, aHandle, aResult);
michael@0 224
michael@0 225 return NS_OK;
michael@0 226 }
michael@0 227
michael@0 228 NS_IMPL_ISUPPORTS(FileOpenHelper, CacheFileIOListener);
michael@0 229
michael@0 230
michael@0 231 CacheIndex * CacheIndex::gInstance = nullptr;
michael@0 232
michael@0 233
michael@0 234 NS_IMPL_ADDREF(CacheIndex)
michael@0 235 NS_IMPL_RELEASE(CacheIndex)
michael@0 236
michael@0 237 NS_INTERFACE_MAP_BEGIN(CacheIndex)
michael@0 238 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
michael@0 239 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
michael@0 240 NS_INTERFACE_MAP_END_THREADSAFE
michael@0 241
michael@0 242
michael@0 243 CacheIndex::CacheIndex()
michael@0 244 : mLock("CacheFile.mLock")
michael@0 245 , mState(INITIAL)
michael@0 246 , mShuttingDown(false)
michael@0 247 , mIndexNeedsUpdate(false)
michael@0 248 , mRemovingAll(false)
michael@0 249 , mIndexOnDiskIsValid(false)
michael@0 250 , mDontMarkIndexClean(false)
michael@0 251 , mIndexTimeStamp(0)
michael@0 252 , mUpdateEventPending(false)
michael@0 253 , mSkipEntries(0)
michael@0 254 , mProcessEntries(0)
michael@0 255 , mRWBuf(nullptr)
michael@0 256 , mRWBufSize(0)
michael@0 257 , mRWBufPos(0)
michael@0 258 , mJournalReadSuccessfully(false)
michael@0 259 {
michael@0 260 LOG(("CacheIndex::CacheIndex [this=%p]", this));
michael@0 261 MOZ_COUNT_CTOR(CacheIndex);
michael@0 262 MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!");
michael@0 263 }
michael@0 264
michael@0 265 CacheIndex::~CacheIndex()
michael@0 266 {
michael@0 267 LOG(("CacheIndex::~CacheIndex [this=%p]", this));
michael@0 268 MOZ_COUNT_DTOR(CacheIndex);
michael@0 269
michael@0 270 ReleaseBuffer();
michael@0 271 }
michael@0 272
michael@0 273 void
michael@0 274 CacheIndex::Lock()
michael@0 275 {
michael@0 276 mLock.Lock();
michael@0 277
michael@0 278 MOZ_ASSERT(!mIndexStats.StateLogged());
michael@0 279 }
michael@0 280
michael@0 281 void
michael@0 282 CacheIndex::Unlock()
michael@0 283 {
michael@0 284 MOZ_ASSERT(!mIndexStats.StateLogged());
michael@0 285
michael@0 286 mLock.Unlock();
michael@0 287 }
michael@0 288
michael@0 289 inline void
michael@0 290 CacheIndex::AssertOwnsLock()
michael@0 291 {
michael@0 292 mLock.AssertCurrentThreadOwns();
michael@0 293 }
michael@0 294
michael@0 295 // static
michael@0 296 nsresult
michael@0 297 CacheIndex::Init(nsIFile *aCacheDirectory)
michael@0 298 {
michael@0 299 LOG(("CacheIndex::Init()"));
michael@0 300
michael@0 301 MOZ_ASSERT(NS_IsMainThread());
michael@0 302
michael@0 303 if (gInstance) {
michael@0 304 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 305 }
michael@0 306
michael@0 307 nsRefPtr<CacheIndex> idx = new CacheIndex();
michael@0 308
michael@0 309 CacheIndexAutoLock lock(idx);
michael@0 310
michael@0 311 nsresult rv = idx->InitInternal(aCacheDirectory);
michael@0 312 NS_ENSURE_SUCCESS(rv, rv);
michael@0 313
michael@0 314 idx.swap(gInstance);
michael@0 315 return NS_OK;
michael@0 316 }
michael@0 317
michael@0 318 nsresult
michael@0 319 CacheIndex::InitInternal(nsIFile *aCacheDirectory)
michael@0 320 {
michael@0 321 nsresult rv;
michael@0 322
michael@0 323 rv = aCacheDirectory->Clone(getter_AddRefs(mCacheDirectory));
michael@0 324 NS_ENSURE_SUCCESS(rv, rv);
michael@0 325
michael@0 326 mStartTime = TimeStamp::NowLoRes();
michael@0 327
michael@0 328 ReadIndexFromDisk();
michael@0 329
michael@0 330 return NS_OK;
michael@0 331 }
michael@0 332
michael@0 333 // static
michael@0 334 nsresult
michael@0 335 CacheIndex::PreShutdown()
michael@0 336 {
michael@0 337 LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance));
michael@0 338
michael@0 339 MOZ_ASSERT(NS_IsMainThread());
michael@0 340
michael@0 341 nsresult rv;
michael@0 342 nsRefPtr<CacheIndex> index = gInstance;
michael@0 343
michael@0 344 if (!index) {
michael@0 345 return NS_ERROR_NOT_INITIALIZED;
michael@0 346 }
michael@0 347
michael@0 348 CacheIndexAutoLock lock(index);
michael@0 349
michael@0 350 LOG(("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, "
michael@0 351 "dontMarkIndexClean=%d]", index->mState, index->mIndexOnDiskIsValid,
michael@0 352 index->mDontMarkIndexClean));
michael@0 353
michael@0 354 LOG(("CacheIndex::PreShutdown() - Closing iterators."));
michael@0 355 for (uint32_t i = 0; i < index->mIterators.Length(); ) {
michael@0 356 rv = index->mIterators[i]->CloseInternal(NS_ERROR_FAILURE);
michael@0 357 if (NS_FAILED(rv)) {
michael@0 358 // CacheIndexIterator::CloseInternal() removes itself from mIteratos iff
michael@0 359 // it returns success.
michael@0 360 LOG(("CacheIndex::PreShutdown() - Failed to remove iterator %p. "
michael@0 361 "[rv=0x%08x]", rv));
michael@0 362 i++;
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 index->mShuttingDown = true;
michael@0 367
michael@0 368 if (index->mState == READY) {
michael@0 369 return NS_OK; // nothing to do
michael@0 370 }
michael@0 371
michael@0 372 nsCOMPtr<nsIRunnable> event;
michael@0 373 event = NS_NewRunnableMethod(index, &CacheIndex::PreShutdownInternal);
michael@0 374
michael@0 375 nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
michael@0 376 MOZ_ASSERT(ioTarget);
michael@0 377
michael@0 378 // PreShutdownInternal() will be executed before any queued event on INDEX
michael@0 379 // level. That's OK since we don't want to wait for any operation in progess.
michael@0 380 // We need to interrupt it and save journal as quickly as possible.
michael@0 381 rv = ioTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
michael@0 382 if (NS_FAILED(rv)) {
michael@0 383 NS_WARNING("CacheIndex::PreShutdown() - Can't dispatch event");
michael@0 384 LOG(("CacheIndex::PreShutdown() - Can't dispatch event" ));
michael@0 385 return rv;
michael@0 386 }
michael@0 387
michael@0 388 return NS_OK;
michael@0 389 }
michael@0 390
michael@0 391 void
michael@0 392 CacheIndex::PreShutdownInternal()
michael@0 393 {
michael@0 394 CacheIndexAutoLock lock(this);
michael@0 395
michael@0 396 LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
michael@0 397 "dontMarkIndexClean=%d]", mState, mIndexOnDiskIsValid,
michael@0 398 mDontMarkIndexClean));
michael@0 399
michael@0 400 MOZ_ASSERT(mShuttingDown);
michael@0 401
michael@0 402 if (mUpdateTimer) {
michael@0 403 mUpdateTimer = nullptr;
michael@0 404 }
michael@0 405
michael@0 406 switch (mState) {
michael@0 407 case WRITING:
michael@0 408 FinishWrite(false);
michael@0 409 break;
michael@0 410 case READY:
michael@0 411 // nothing to do, write the journal in Shutdown()
michael@0 412 break;
michael@0 413 case READING:
michael@0 414 FinishRead(false);
michael@0 415 break;
michael@0 416 case BUILDING:
michael@0 417 case UPDATING:
michael@0 418 FinishUpdate(false);
michael@0 419 break;
michael@0 420 default:
michael@0 421 MOZ_ASSERT(false, "Implement me!");
michael@0 422 }
michael@0 423
michael@0 424 // We should end up in READY state
michael@0 425 MOZ_ASSERT(mState == READY);
michael@0 426 }
michael@0 427
michael@0 428 // static
michael@0 429 nsresult
michael@0 430 CacheIndex::Shutdown()
michael@0 431 {
michael@0 432 LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance));
michael@0 433
michael@0 434 MOZ_ASSERT(NS_IsMainThread());
michael@0 435
michael@0 436 nsRefPtr<CacheIndex> index;
michael@0 437 index.swap(gInstance);
michael@0 438
michael@0 439 if (!index) {
michael@0 440 return NS_ERROR_NOT_INITIALIZED;
michael@0 441 }
michael@0 442
michael@0 443 CacheIndexAutoLock lock(index);
michael@0 444
michael@0 445 bool sanitize = CacheObserver::ClearCacheOnShutdown();
michael@0 446
michael@0 447 LOG(("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, "
michael@0 448 "dontMarkIndexClean=%d, sanitize=%d]", index->mState,
michael@0 449 index->mIndexOnDiskIsValid, index->mDontMarkIndexClean, sanitize));
michael@0 450
michael@0 451 MOZ_ASSERT(index->mShuttingDown);
michael@0 452
michael@0 453 EState oldState = index->mState;
michael@0 454 index->ChangeState(SHUTDOWN);
michael@0 455
michael@0 456 if (oldState != READY) {
michael@0 457 LOG(("CacheIndex::Shutdown() - Unexpected state. Did posting of "
michael@0 458 "PreShutdownInternal() fail?"));
michael@0 459 }
michael@0 460
michael@0 461 switch (oldState) {
michael@0 462 case WRITING:
michael@0 463 index->FinishWrite(false);
michael@0 464 // no break
michael@0 465 case READY:
michael@0 466 if (index->mIndexOnDiskIsValid && !index->mDontMarkIndexClean) {
michael@0 467 if (!sanitize && NS_FAILED(index->WriteLogToDisk())) {
michael@0 468 index->RemoveIndexFromDisk();
michael@0 469 }
michael@0 470 } else {
michael@0 471 index->RemoveIndexFromDisk();
michael@0 472 }
michael@0 473 break;
michael@0 474 case READING:
michael@0 475 index->FinishRead(false);
michael@0 476 break;
michael@0 477 case BUILDING:
michael@0 478 case UPDATING:
michael@0 479 index->FinishUpdate(false);
michael@0 480 break;
michael@0 481 default:
michael@0 482 MOZ_ASSERT(false, "Unexpected state!");
michael@0 483 }
michael@0 484
michael@0 485 if (sanitize) {
michael@0 486 index->RemoveIndexFromDisk();
michael@0 487 }
michael@0 488
michael@0 489 return NS_OK;
michael@0 490 }
michael@0 491
michael@0 492 // static
michael@0 493 nsresult
michael@0 494 CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
michael@0 495 {
michael@0 496 LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
michael@0 497
michael@0 498 nsRefPtr<CacheIndex> index = gInstance;
michael@0 499
michael@0 500 if (!index) {
michael@0 501 return NS_ERROR_NOT_INITIALIZED;
michael@0 502 }
michael@0 503
michael@0 504 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 505
michael@0 506 CacheIndexAutoLock lock(index);
michael@0 507
michael@0 508 if (!index->IsIndexUsable()) {
michael@0 509 return NS_ERROR_NOT_AVAILABLE;
michael@0 510 }
michael@0 511
michael@0 512 // Getters in CacheIndexStats assert when mStateLogged is true since the
michael@0 513 // information is incomplete between calls to BeforeChange() and AfterChange()
michael@0 514 // (i.e. while CacheIndexEntryAutoManage exists). We need to check whether
michael@0 515 // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
michael@0 516 bool updateIfNonFreshEntriesExist = false;
michael@0 517
michael@0 518 {
michael@0 519 CacheIndexEntryAutoManage entryMng(aHash, index);
michael@0 520
michael@0 521 CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
michael@0 522 bool entryRemoved = entry && entry->IsRemoved();
michael@0 523
michael@0 524 if (index->mState == READY || index->mState == UPDATING ||
michael@0 525 index->mState == BUILDING) {
michael@0 526 MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
michael@0 527
michael@0 528 if (entry && !entryRemoved) {
michael@0 529 // Found entry in index that shouldn't exist.
michael@0 530
michael@0 531 if (entry->IsFresh()) {
michael@0 532 // Someone removed the file on disk while FF is running. Update
michael@0 533 // process can fix only non-fresh entries (i.e. entries that were not
michael@0 534 // added within this session). Start update only if we have such
michael@0 535 // entries.
michael@0 536 //
michael@0 537 // TODO: This should be very rare problem. If it turns out not to be
michael@0 538 // true, change the update process so that it also iterates all
michael@0 539 // initialized non-empty entries and checks whether the file exists.
michael@0 540
michael@0 541 LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF "
michael@0 542 "process!"));
michael@0 543
michael@0 544 updateIfNonFreshEntriesExist = true;
michael@0 545 } else if (index->mState == READY) {
michael@0 546 // Index is outdated, update it.
michael@0 547 LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
michael@0 548 "update is needed"));
michael@0 549 index->mIndexNeedsUpdate = true;
michael@0 550 } else {
michael@0 551 // We cannot be here when building index since all entries are fresh
michael@0 552 // during building.
michael@0 553 MOZ_ASSERT(index->mState == UPDATING);
michael@0 554 }
michael@0 555 }
michael@0 556
michael@0 557 if (!entry) {
michael@0 558 entry = index->mIndex.PutEntry(*aHash);
michael@0 559 }
michael@0 560 } else { // WRITING, READING
michael@0 561 CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
michael@0 562 bool updatedRemoved = updated && updated->IsRemoved();
michael@0 563
michael@0 564 if ((updated && !updatedRemoved) ||
michael@0 565 (!updated && entry && !entryRemoved && entry->IsFresh())) {
michael@0 566 // Fresh entry found, so the file was removed outside FF
michael@0 567 LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF "
michael@0 568 "process!"));
michael@0 569
michael@0 570 updateIfNonFreshEntriesExist = true;
michael@0 571 } else if (!updated && entry && !entryRemoved) {
michael@0 572 if (index->mState == WRITING) {
michael@0 573 LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
michael@0 574 "update is needed"));
michael@0 575 index->mIndexNeedsUpdate = true;
michael@0 576 }
michael@0 577 // Ignore if state is READING since the index information is partial
michael@0 578 }
michael@0 579
michael@0 580 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 581 entry = updated;
michael@0 582 }
michael@0 583
michael@0 584 entry->InitNew();
michael@0 585 entry->MarkDirty();
michael@0 586 entry->MarkFresh();
michael@0 587 }
michael@0 588
michael@0 589 if (updateIfNonFreshEntriesExist &&
michael@0 590 index->mIndexStats.Count() != index->mIndexStats.Fresh()) {
michael@0 591 index->mIndexNeedsUpdate = true;
michael@0 592 }
michael@0 593
michael@0 594 index->StartUpdatingIndexIfNeeded();
michael@0 595 index->WriteIndexToDiskIfNeeded();
michael@0 596
michael@0 597 return NS_OK;
michael@0 598 }
michael@0 599
michael@0 600 // static
michael@0 601 nsresult
michael@0 602 CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash)
michael@0 603 {
michael@0 604 LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]",
michael@0 605 LOGSHA1(aHash)));
michael@0 606
michael@0 607 nsRefPtr<CacheIndex> index = gInstance;
michael@0 608
michael@0 609 if (!index) {
michael@0 610 return NS_ERROR_NOT_INITIALIZED;
michael@0 611 }
michael@0 612
michael@0 613 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 614
michael@0 615 CacheIndexAutoLock lock(index);
michael@0 616
michael@0 617 if (!index->IsIndexUsable()) {
michael@0 618 return NS_ERROR_NOT_AVAILABLE;
michael@0 619 }
michael@0 620
michael@0 621 {
michael@0 622 CacheIndexEntryAutoManage entryMng(aHash, index);
michael@0 623
michael@0 624 CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
michael@0 625 bool entryRemoved = entry && entry->IsRemoved();
michael@0 626
michael@0 627 if (index->mState == READY || index->mState == UPDATING ||
michael@0 628 index->mState == BUILDING) {
michael@0 629 MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
michael@0 630
michael@0 631 if (!entry || entryRemoved) {
michael@0 632 if (entryRemoved && entry->IsFresh()) {
michael@0 633 // This could happen only if somebody copies files to the entries
michael@0 634 // directory while FF is running.
michael@0 635 LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside "
michael@0 636 "FF process! Update is needed."));
michael@0 637 index->mIndexNeedsUpdate = true;
michael@0 638 } else if (index->mState == READY ||
michael@0 639 (entryRemoved && !entry->IsFresh())) {
michael@0 640 // Removed non-fresh entries can be present as a result of
michael@0 641 // ProcessJournalEntry()
michael@0 642 LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
michael@0 643 " exist, update is needed"));
michael@0 644 index->mIndexNeedsUpdate = true;
michael@0 645 }
michael@0 646
michael@0 647 if (!entry) {
michael@0 648 entry = index->mIndex.PutEntry(*aHash);
michael@0 649 }
michael@0 650 entry->InitNew();
michael@0 651 entry->MarkDirty();
michael@0 652 }
michael@0 653 entry->MarkFresh();
michael@0 654 } else { // WRITING, READING
michael@0 655 CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
michael@0 656 bool updatedRemoved = updated && updated->IsRemoved();
michael@0 657
michael@0 658 if (updatedRemoved ||
michael@0 659 (!updated && entryRemoved && entry->IsFresh())) {
michael@0 660 // Fresh information about missing entry found. This could happen only
michael@0 661 // if somebody copies files to the entries directory while FF is running.
michael@0 662 LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside "
michael@0 663 "FF process! Update is needed."));
michael@0 664 index->mIndexNeedsUpdate = true;
michael@0 665 } else if (!updated && (!entry || entryRemoved)) {
michael@0 666 if (index->mState == WRITING) {
michael@0 667 LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
michael@0 668 " exist, update is needed"));
michael@0 669 index->mIndexNeedsUpdate = true;
michael@0 670 }
michael@0 671 // Ignore if state is READING since the index information is partial
michael@0 672 }
michael@0 673
michael@0 674 // We don't need entryRemoved and updatedRemoved info anymore
michael@0 675 if (entryRemoved) entry = nullptr;
michael@0 676 if (updatedRemoved) updated = nullptr;
michael@0 677
michael@0 678 if (updated) {
michael@0 679 updated->MarkFresh();
michael@0 680 } else {
michael@0 681 if (!entry) {
michael@0 682 // Create a new entry
michael@0 683 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 684 updated->InitNew();
michael@0 685 updated->MarkFresh();
michael@0 686 updated->MarkDirty();
michael@0 687 } else {
michael@0 688 if (!entry->IsFresh()) {
michael@0 689 // To mark the entry fresh we must make a copy of index entry
michael@0 690 // since the index is read-only.
michael@0 691 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 692 *updated = *entry;
michael@0 693 updated->MarkFresh();
michael@0 694 }
michael@0 695 }
michael@0 696 }
michael@0 697 }
michael@0 698 }
michael@0 699
michael@0 700 index->StartUpdatingIndexIfNeeded();
michael@0 701 index->WriteIndexToDiskIfNeeded();
michael@0 702
michael@0 703 return NS_OK;
michael@0 704 }
michael@0 705
michael@0 706 // static
michael@0 707 nsresult
michael@0 708 CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
michael@0 709 uint32_t aAppId,
michael@0 710 bool aAnonymous,
michael@0 711 bool aInBrowser)
michael@0 712 {
michael@0 713 LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, "
michael@0 714 "anonymous=%d, inBrowser=%d]", LOGSHA1(aHash), aAppId, aAnonymous,
michael@0 715 aInBrowser));
michael@0 716
michael@0 717 nsRefPtr<CacheIndex> index = gInstance;
michael@0 718
michael@0 719 if (!index) {
michael@0 720 return NS_ERROR_NOT_INITIALIZED;
michael@0 721 }
michael@0 722
michael@0 723 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 724
michael@0 725 CacheIndexAutoLock lock(index);
michael@0 726
michael@0 727 if (!index->IsIndexUsable()) {
michael@0 728 return NS_ERROR_NOT_AVAILABLE;
michael@0 729 }
michael@0 730
michael@0 731 {
michael@0 732 CacheIndexEntryAutoManage entryMng(aHash, index);
michael@0 733
michael@0 734 CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
michael@0 735 bool reinitEntry = false;
michael@0 736
michael@0 737 if (entry && entry->IsRemoved()) {
michael@0 738 entry = nullptr;
michael@0 739 }
michael@0 740
michael@0 741 if (index->mState == READY || index->mState == UPDATING ||
michael@0 742 index->mState == BUILDING) {
michael@0 743 MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
michael@0 744 MOZ_ASSERT(entry);
michael@0 745 MOZ_ASSERT(entry->IsFresh());
michael@0 746
michael@0 747 if (IsCollision(entry, aAppId, aAnonymous, aInBrowser)) {
michael@0 748 index->mIndexNeedsUpdate = true; // TODO Does this really help in case of collision?
michael@0 749 reinitEntry = true;
michael@0 750 } else {
michael@0 751 if (entry->IsInitialized()) {
michael@0 752 return NS_OK;
michael@0 753 }
michael@0 754 }
michael@0 755 } else {
michael@0 756 CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
michael@0 757 DebugOnly<bool> removed = updated && updated->IsRemoved();
michael@0 758
michael@0 759 MOZ_ASSERT(updated || !removed);
michael@0 760 MOZ_ASSERT(updated || entry);
michael@0 761
michael@0 762 if (updated) {
michael@0 763 MOZ_ASSERT(updated->IsFresh());
michael@0 764
michael@0 765 if (IsCollision(updated, aAppId, aAnonymous, aInBrowser)) {
michael@0 766 index->mIndexNeedsUpdate = true;
michael@0 767 reinitEntry = true;
michael@0 768 } else {
michael@0 769 if (updated->IsInitialized()) {
michael@0 770 return NS_OK;
michael@0 771 }
michael@0 772 }
michael@0 773 entry = updated;
michael@0 774 } else {
michael@0 775 MOZ_ASSERT(entry->IsFresh());
michael@0 776
michael@0 777 if (IsCollision(entry, aAppId, aAnonymous, aInBrowser)) {
michael@0 778 index->mIndexNeedsUpdate = true;
michael@0 779 reinitEntry = true;
michael@0 780 } else {
michael@0 781 if (entry->IsInitialized()) {
michael@0 782 return NS_OK;
michael@0 783 }
michael@0 784 }
michael@0 785
michael@0 786 // make a copy of a read-only entry
michael@0 787 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 788 *updated = *entry;
michael@0 789 entry = updated;
michael@0 790 }
michael@0 791 }
michael@0 792
michael@0 793 if (reinitEntry) {
michael@0 794 // There is a collision and we are going to rewrite this entry. Initialize
michael@0 795 // it as a new entry.
michael@0 796 entry->InitNew();
michael@0 797 entry->MarkFresh();
michael@0 798 }
michael@0 799 entry->Init(aAppId, aAnonymous, aInBrowser);
michael@0 800 entry->MarkDirty();
michael@0 801 }
michael@0 802
michael@0 803 index->StartUpdatingIndexIfNeeded();
michael@0 804 index->WriteIndexToDiskIfNeeded();
michael@0 805
michael@0 806 return NS_OK;
michael@0 807 }
michael@0 808
michael@0 809 // static
michael@0 810 nsresult
michael@0 811 CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash)
michael@0 812 {
michael@0 813 LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]",
michael@0 814 LOGSHA1(aHash)));
michael@0 815
michael@0 816 nsRefPtr<CacheIndex> index = gInstance;
michael@0 817
michael@0 818 if (!index) {
michael@0 819 return NS_ERROR_NOT_INITIALIZED;
michael@0 820 }
michael@0 821
michael@0 822 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 823
michael@0 824 CacheIndexAutoLock lock(index);
michael@0 825
michael@0 826 if (!index->IsIndexUsable()) {
michael@0 827 return NS_ERROR_NOT_AVAILABLE;
michael@0 828 }
michael@0 829
michael@0 830 {
michael@0 831 CacheIndexEntryAutoManage entryMng(aHash, index);
michael@0 832
michael@0 833 CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
michael@0 834 bool entryRemoved = entry && entry->IsRemoved();
michael@0 835
michael@0 836 if (index->mState == READY || index->mState == UPDATING ||
michael@0 837 index->mState == BUILDING) {
michael@0 838 MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
michael@0 839
michael@0 840 if (!entry || entryRemoved) {
michael@0 841 if (entryRemoved && entry->IsFresh()) {
michael@0 842 // This could happen only if somebody copies files to the entries
michael@0 843 // directory while FF is running.
michael@0 844 LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF "
michael@0 845 "process! Update is needed."));
michael@0 846 index->mIndexNeedsUpdate = true;
michael@0 847 } else if (index->mState == READY ||
michael@0 848 (entryRemoved && !entry->IsFresh())) {
michael@0 849 // Removed non-fresh entries can be present as a result of
michael@0 850 // ProcessJournalEntry()
michael@0 851 LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
michael@0 852 ", update is needed"));
michael@0 853 index->mIndexNeedsUpdate = true;
michael@0 854 }
michael@0 855 } else {
michael@0 856 if (entry) {
michael@0 857 if (!entry->IsDirty() && entry->IsFileEmpty()) {
michael@0 858 index->mIndex.RemoveEntry(*aHash);
michael@0 859 entry = nullptr;
michael@0 860 } else {
michael@0 861 entry->MarkRemoved();
michael@0 862 entry->MarkDirty();
michael@0 863 entry->MarkFresh();
michael@0 864 }
michael@0 865 }
michael@0 866 }
michael@0 867 } else { // WRITING, READING
michael@0 868 CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
michael@0 869 bool updatedRemoved = updated && updated->IsRemoved();
michael@0 870
michael@0 871 if (updatedRemoved ||
michael@0 872 (!updated && entryRemoved && entry->IsFresh())) {
michael@0 873 // Fresh information about missing entry found. This could happen only
michael@0 874 // if somebody copies files to the entries directory while FF is running.
michael@0 875 LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF "
michael@0 876 "process! Update is needed."));
michael@0 877 index->mIndexNeedsUpdate = true;
michael@0 878 } else if (!updated && (!entry || entryRemoved)) {
michael@0 879 if (index->mState == WRITING) {
michael@0 880 LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
michael@0 881 ", update is needed"));
michael@0 882 index->mIndexNeedsUpdate = true;
michael@0 883 }
michael@0 884 // Ignore if state is READING since the index information is partial
michael@0 885 }
michael@0 886
michael@0 887 if (!updated) {
michael@0 888 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 889 updated->InitNew();
michael@0 890 }
michael@0 891
michael@0 892 updated->MarkRemoved();
michael@0 893 updated->MarkDirty();
michael@0 894 updated->MarkFresh();
michael@0 895 }
michael@0 896 }
michael@0 897
michael@0 898 index->StartUpdatingIndexIfNeeded();
michael@0 899 index->WriteIndexToDiskIfNeeded();
michael@0 900
michael@0 901 return NS_OK;
michael@0 902 }
michael@0 903
michael@0 904 // static
michael@0 905 nsresult
michael@0 906 CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
michael@0 907 const uint32_t *aFrecency,
michael@0 908 const uint32_t *aExpirationTime,
michael@0 909 const uint32_t *aSize)
michael@0 910 {
michael@0 911 LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
michael@0 912 "frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash),
michael@0 913 aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
michael@0 914 aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
michael@0 915 aSize ? nsPrintfCString("%u", *aSize).get() : ""));
michael@0 916
michael@0 917 nsRefPtr<CacheIndex> index = gInstance;
michael@0 918
michael@0 919 if (!index) {
michael@0 920 return NS_ERROR_NOT_INITIALIZED;
michael@0 921 }
michael@0 922
michael@0 923 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 924
michael@0 925 CacheIndexAutoLock lock(index);
michael@0 926
michael@0 927 if (!index->IsIndexUsable()) {
michael@0 928 return NS_ERROR_NOT_AVAILABLE;
michael@0 929 }
michael@0 930
michael@0 931 {
michael@0 932 CacheIndexEntryAutoManage entryMng(aHash, index);
michael@0 933
michael@0 934 CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
michael@0 935
michael@0 936 if (entry && entry->IsRemoved()) {
michael@0 937 entry = nullptr;
michael@0 938 }
michael@0 939
michael@0 940 if (index->mState == READY || index->mState == UPDATING ||
michael@0 941 index->mState == BUILDING) {
michael@0 942 MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
michael@0 943 MOZ_ASSERT(entry);
michael@0 944
michael@0 945 if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
michael@0 946 return NS_OK;
michael@0 947 }
michael@0 948 } else {
michael@0 949 CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
michael@0 950 DebugOnly<bool> removed = updated && updated->IsRemoved();
michael@0 951
michael@0 952 MOZ_ASSERT(updated || !removed);
michael@0 953 MOZ_ASSERT(updated || entry);
michael@0 954
michael@0 955 if (!updated) {
michael@0 956 if (entry &&
michael@0 957 HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
michael@0 958 // make a copy of a read-only entry
michael@0 959 updated = index->mPendingUpdates.PutEntry(*aHash);
michael@0 960 *updated = *entry;
michael@0 961 entry = updated;
michael@0 962 } else {
michael@0 963 return NS_ERROR_NOT_AVAILABLE;
michael@0 964 }
michael@0 965 } else {
michael@0 966 entry = updated;
michael@0 967 }
michael@0 968 }
michael@0 969
michael@0 970 MOZ_ASSERT(entry->IsFresh());
michael@0 971 MOZ_ASSERT(entry->IsInitialized());
michael@0 972 entry->MarkDirty();
michael@0 973
michael@0 974 if (aFrecency) {
michael@0 975 entry->SetFrecency(*aFrecency);
michael@0 976 }
michael@0 977
michael@0 978 if (aExpirationTime) {
michael@0 979 entry->SetExpirationTime(*aExpirationTime);
michael@0 980 }
michael@0 981
michael@0 982 if (aSize) {
michael@0 983 entry->SetFileSize(*aSize);
michael@0 984 }
michael@0 985 }
michael@0 986
michael@0 987 index->WriteIndexToDiskIfNeeded();
michael@0 988
michael@0 989 return NS_OK;
michael@0 990 }
michael@0 991
michael@0 992 // static
michael@0 993 nsresult
michael@0 994 CacheIndex::RemoveAll()
michael@0 995 {
michael@0 996 LOG(("CacheIndex::RemoveAll()"));
michael@0 997
michael@0 998 nsRefPtr<CacheIndex> index = gInstance;
michael@0 999
michael@0 1000 if (!index) {
michael@0 1001 return NS_ERROR_NOT_INITIALIZED;
michael@0 1002 }
michael@0 1003
michael@0 1004 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
michael@0 1005
michael@0 1006 nsCOMPtr<nsIFile> file;
michael@0 1007
michael@0 1008 {
michael@0 1009 CacheIndexAutoLock lock(index);
michael@0 1010
michael@0 1011 MOZ_ASSERT(!index->mRemovingAll);
michael@0 1012
michael@0 1013 if (!index->IsIndexUsable()) {
michael@0 1014 return NS_ERROR_NOT_AVAILABLE;
michael@0 1015 }
michael@0 1016
michael@0 1017 AutoRestore<bool> saveRemovingAll(index->mRemovingAll);
michael@0 1018 index->mRemovingAll = true;
michael@0 1019
michael@0 1020 // Doom index and journal handles but don't null them out since this will be
michael@0 1021 // done in FinishWrite/FinishRead methods.
michael@0 1022 if (index->mIndexHandle) {
michael@0 1023 CacheFileIOManager::DoomFile(index->mIndexHandle, nullptr);
michael@0 1024 } else {
michael@0 1025 // We don't have a handle to index file, so get the file here, but delete
michael@0 1026 // it outside the lock. Ignore the result since this is not fatal.
michael@0 1027 index->GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(file));
michael@0 1028 }
michael@0 1029
michael@0 1030 if (index->mJournalHandle) {
michael@0 1031 CacheFileIOManager::DoomFile(index->mJournalHandle, nullptr);
michael@0 1032 }
michael@0 1033
michael@0 1034 switch (index->mState) {
michael@0 1035 case WRITING:
michael@0 1036 index->FinishWrite(false);
michael@0 1037 break;
michael@0 1038 case READY:
michael@0 1039 // nothing to do
michael@0 1040 break;
michael@0 1041 case READING:
michael@0 1042 index->FinishRead(false);
michael@0 1043 break;
michael@0 1044 case BUILDING:
michael@0 1045 case UPDATING:
michael@0 1046 index->FinishUpdate(false);
michael@0 1047 break;
michael@0 1048 default:
michael@0 1049 MOZ_ASSERT(false, "Unexpected state!");
michael@0 1050 }
michael@0 1051
michael@0 1052 // We should end up in READY state
michael@0 1053 MOZ_ASSERT(index->mState == READY);
michael@0 1054
michael@0 1055 // There should not be any handle
michael@0 1056 MOZ_ASSERT(!index->mIndexHandle);
michael@0 1057 MOZ_ASSERT(!index->mJournalHandle);
michael@0 1058
michael@0 1059 index->mIndexOnDiskIsValid = false;
michael@0 1060 index->mIndexNeedsUpdate = false;
michael@0 1061
michael@0 1062 index->mIndexStats.Clear();
michael@0 1063 index->mFrecencyArray.Clear();
michael@0 1064 index->mExpirationArray.Clear();
michael@0 1065 index->mIndex.Clear();
michael@0 1066 }
michael@0 1067
michael@0 1068 if (file) {
michael@0 1069 // Ignore the result. The file might not exist and the failure is not fatal.
michael@0 1070 file->Remove(false);
michael@0 1071 }
michael@0 1072
michael@0 1073 return NS_OK;
michael@0 1074 }
michael@0 1075
michael@0 1076 // static
michael@0 1077 nsresult
michael@0 1078 CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
michael@0 1079 {
michael@0 1080 LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey).get()));
michael@0 1081
michael@0 1082 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1083
michael@0 1084 if (!index) {
michael@0 1085 return NS_ERROR_NOT_INITIALIZED;
michael@0 1086 }
michael@0 1087
michael@0 1088 CacheIndexAutoLock lock(index);
michael@0 1089
michael@0 1090 if (!index->IsIndexUsable()) {
michael@0 1091 return NS_ERROR_NOT_AVAILABLE;
michael@0 1092 }
michael@0 1093
michael@0 1094 SHA1Sum sum;
michael@0 1095 SHA1Sum::Hash hash;
michael@0 1096 sum.update(aKey.BeginReading(), aKey.Length());
michael@0 1097 sum.finish(hash);
michael@0 1098
michael@0 1099 CacheIndexEntry *entry = nullptr;
michael@0 1100
michael@0 1101 switch (index->mState) {
michael@0 1102 case READING:
michael@0 1103 case WRITING:
michael@0 1104 entry = index->mPendingUpdates.GetEntry(hash);
michael@0 1105 // no break
michael@0 1106 case BUILDING:
michael@0 1107 case UPDATING:
michael@0 1108 case READY:
michael@0 1109 if (!entry) {
michael@0 1110 entry = index->mIndex.GetEntry(hash);
michael@0 1111 }
michael@0 1112 break;
michael@0 1113 case INITIAL:
michael@0 1114 case SHUTDOWN:
michael@0 1115 MOZ_ASSERT(false, "Unexpected state!");
michael@0 1116 }
michael@0 1117
michael@0 1118 if (!entry) {
michael@0 1119 if (index->mState == READY || index->mState == WRITING) {
michael@0 1120 *_retval = DOES_NOT_EXIST;
michael@0 1121 } else {
michael@0 1122 *_retval = DO_NOT_KNOW;
michael@0 1123 }
michael@0 1124 } else {
michael@0 1125 if (entry->IsRemoved()) {
michael@0 1126 if (entry->IsFresh()) {
michael@0 1127 *_retval = DOES_NOT_EXIST;
michael@0 1128 } else {
michael@0 1129 *_retval = DO_NOT_KNOW;
michael@0 1130 }
michael@0 1131 } else {
michael@0 1132 *_retval = EXISTS;
michael@0 1133 }
michael@0 1134 }
michael@0 1135
michael@0 1136 LOG(("CacheIndex::HasEntry() - result is %u", *_retval));
michael@0 1137 return NS_OK;
michael@0 1138 }
michael@0 1139
michael@0 1140 // static
michael@0 1141 nsresult
michael@0 1142 CacheIndex::GetEntryForEviction(SHA1Sum::Hash *aHash, uint32_t *aCnt)
michael@0 1143 {
michael@0 1144 LOG(("CacheIndex::GetEntryForEviction()"));
michael@0 1145
michael@0 1146 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1147
michael@0 1148 if (!index)
michael@0 1149 return NS_ERROR_NOT_INITIALIZED;
michael@0 1150
michael@0 1151 CacheIndexAutoLock lock(index);
michael@0 1152
michael@0 1153 if (!index->IsIndexUsable()) {
michael@0 1154 return NS_ERROR_NOT_AVAILABLE;
michael@0 1155 }
michael@0 1156
michael@0 1157 MOZ_ASSERT(index->mFrecencyArray.Length() ==
michael@0 1158 index->mExpirationArray.Length());
michael@0 1159
michael@0 1160 if (index->mExpirationArray.Length() == 0)
michael@0 1161 return NS_ERROR_NOT_AVAILABLE;
michael@0 1162
michael@0 1163 uint32_t now = PR_Now() / PR_USEC_PER_SEC;
michael@0 1164 if (index->mExpirationArray[0]->mExpirationTime < now) {
michael@0 1165 memcpy(aHash, &index->mExpirationArray[0]->mHash, sizeof(SHA1Sum::Hash));
michael@0 1166 *aCnt = index->mExpirationArray.Length();
michael@0 1167 LOG(("CacheIndex::GetEntryForEviction() - returning entry from expiration "
michael@0 1168 "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, "
michael@0 1169 "frecency=%u]", LOGSHA1(aHash), *aCnt,
michael@0 1170 index->mExpirationArray[0]->mExpirationTime, now,
michael@0 1171 index->mExpirationArray[0]->mFrecency));
michael@0 1172 }
michael@0 1173 else {
michael@0 1174 memcpy(aHash, &index->mFrecencyArray[0]->mHash, sizeof(SHA1Sum::Hash));
michael@0 1175 *aCnt = index->mFrecencyArray.Length();
michael@0 1176 LOG(("CacheIndex::GetEntryForEviction() - returning entry from frecency "
michael@0 1177 "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, "
michael@0 1178 "frecency=%u]", LOGSHA1(aHash), *aCnt,
michael@0 1179 index->mExpirationArray[0]->mExpirationTime, now,
michael@0 1180 index->mExpirationArray[0]->mFrecency));
michael@0 1181 }
michael@0 1182
michael@0 1183 return NS_OK;
michael@0 1184 }
michael@0 1185
michael@0 1186 // static
michael@0 1187 nsresult
michael@0 1188 CacheIndex::GetCacheSize(uint32_t *_retval)
michael@0 1189 {
michael@0 1190 LOG(("CacheIndex::GetCacheSize()"));
michael@0 1191
michael@0 1192 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1193
michael@0 1194 if (!index)
michael@0 1195 return NS_ERROR_NOT_INITIALIZED;
michael@0 1196
michael@0 1197 CacheIndexAutoLock lock(index);
michael@0 1198
michael@0 1199 if (!index->IsIndexUsable()) {
michael@0 1200 return NS_ERROR_NOT_AVAILABLE;
michael@0 1201 }
michael@0 1202
michael@0 1203 *_retval = index->mIndexStats.Size();
michael@0 1204 LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval));
michael@0 1205 return NS_OK;
michael@0 1206 }
michael@0 1207
michael@0 1208 // static
michael@0 1209 nsresult
michael@0 1210 CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver)
michael@0 1211 {
michael@0 1212 LOG(("CacheIndex::AsyncGetDiskConsumption()"));
michael@0 1213
michael@0 1214 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1215
michael@0 1216 if (!index) {
michael@0 1217 return NS_ERROR_NOT_INITIALIZED;
michael@0 1218 }
michael@0 1219
michael@0 1220 CacheIndexAutoLock lock(index);
michael@0 1221
michael@0 1222 if (!index->IsIndexUsable()) {
michael@0 1223 return NS_ERROR_NOT_AVAILABLE;
michael@0 1224 }
michael@0 1225
michael@0 1226 nsRefPtr<DiskConsumptionObserver> observer =
michael@0 1227 DiskConsumptionObserver::Init(aObserver);
michael@0 1228
michael@0 1229 NS_ENSURE_ARG(observer);
michael@0 1230
michael@0 1231 if (index->mState == READY || index->mState == WRITING) {
michael@0 1232 LOG(("CacheIndex::AsyncGetDiskConsumption - calling immediately"));
michael@0 1233 // Safe to call the callback under the lock,
michael@0 1234 // we always post to the main thread.
michael@0 1235 observer->OnDiskConsumption(index->mIndexStats.Size() << 10);
michael@0 1236 return NS_OK;
michael@0 1237 }
michael@0 1238
michael@0 1239 LOG(("CacheIndex::AsyncGetDiskConsumption - remembering callback"));
michael@0 1240 // Will be called when the index get to the READY state.
michael@0 1241 index->mDiskConsumptionObservers.AppendElement(observer);
michael@0 1242
michael@0 1243 return NS_OK;
michael@0 1244 }
michael@0 1245
michael@0 1246 // static
michael@0 1247 nsresult
michael@0 1248 CacheIndex::GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
michael@0 1249 CacheIndexIterator **_retval)
michael@0 1250 {
michael@0 1251 LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo, aAddNew));
michael@0 1252
michael@0 1253 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1254
michael@0 1255 if (!index) {
michael@0 1256 return NS_ERROR_NOT_INITIALIZED;
michael@0 1257 }
michael@0 1258
michael@0 1259 CacheIndexAutoLock lock(index);
michael@0 1260
michael@0 1261 if (!index->IsIndexUsable()) {
michael@0 1262 return NS_ERROR_NOT_AVAILABLE;
michael@0 1263 }
michael@0 1264
michael@0 1265 nsRefPtr<CacheIndexIterator> iter;
michael@0 1266 if (aInfo) {
michael@0 1267 iter = new CacheIndexContextIterator(index, aAddNew, aInfo);
michael@0 1268 } else {
michael@0 1269 iter = new CacheIndexIterator(index, aAddNew);
michael@0 1270 }
michael@0 1271
michael@0 1272 iter->AddRecords(index->mFrecencyArray);
michael@0 1273
michael@0 1274 index->mIterators.AppendElement(iter);
michael@0 1275 iter.swap(*_retval);
michael@0 1276 return NS_OK;
michael@0 1277 }
michael@0 1278
michael@0 1279 // static
michael@0 1280 nsresult
michael@0 1281 CacheIndex::IsUpToDate(bool *_retval)
michael@0 1282 {
michael@0 1283 LOG(("CacheIndex::IsUpToDate()"));
michael@0 1284
michael@0 1285 nsRefPtr<CacheIndex> index = gInstance;
michael@0 1286
michael@0 1287 if (!index) {
michael@0 1288 return NS_ERROR_NOT_INITIALIZED;
michael@0 1289 }
michael@0 1290
michael@0 1291 CacheIndexAutoLock lock(index);
michael@0 1292
michael@0 1293 if (!index->IsIndexUsable()) {
michael@0 1294 return NS_ERROR_NOT_AVAILABLE;
michael@0 1295 }
michael@0 1296
michael@0 1297 *_retval = (index->mState == READY || index->mState == WRITING) &&
michael@0 1298 !index->mIndexNeedsUpdate && !index->mShuttingDown;
michael@0 1299
michael@0 1300 LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval));
michael@0 1301 return NS_OK;
michael@0 1302 }
michael@0 1303
michael@0 1304 bool
michael@0 1305 CacheIndex::IsIndexUsable()
michael@0 1306 {
michael@0 1307 MOZ_ASSERT(mState != INITIAL);
michael@0 1308
michael@0 1309 switch (mState) {
michael@0 1310 case INITIAL:
michael@0 1311 case SHUTDOWN:
michael@0 1312 return false;
michael@0 1313
michael@0 1314 case READING:
michael@0 1315 case WRITING:
michael@0 1316 case BUILDING:
michael@0 1317 case UPDATING:
michael@0 1318 case READY:
michael@0 1319 break;
michael@0 1320 }
michael@0 1321
michael@0 1322 return true;
michael@0 1323 }
michael@0 1324
michael@0 1325 // static
michael@0 1326 bool
michael@0 1327 CacheIndex::IsCollision(CacheIndexEntry *aEntry,
michael@0 1328 uint32_t aAppId,
michael@0 1329 bool aAnonymous,
michael@0 1330 bool aInBrowser)
michael@0 1331 {
michael@0 1332 if (!aEntry->IsInitialized()) {
michael@0 1333 return false;
michael@0 1334 }
michael@0 1335
michael@0 1336 if (aEntry->AppId() != aAppId || aEntry->Anonymous() != aAnonymous ||
michael@0 1337 aEntry->InBrowser() != aInBrowser) {
michael@0 1338 LOG(("CacheIndex::IsCollision() - Collision detected for entry hash=%08x"
michael@0 1339 "%08x%08x%08x%08x, expected values: appId=%u, anonymous=%d, "
michael@0 1340 "inBrowser=%d; actual values: appId=%u, anonymous=%d, inBrowser=%d]",
michael@0 1341 LOGSHA1(aEntry->Hash()), aAppId, aAnonymous, aInBrowser,
michael@0 1342 aEntry->AppId(), aEntry->Anonymous(), aEntry->InBrowser()));
michael@0 1343 return true;
michael@0 1344 }
michael@0 1345
michael@0 1346 return false;
michael@0 1347 }
michael@0 1348
michael@0 1349 // static
michael@0 1350 bool
michael@0 1351 CacheIndex::HasEntryChanged(CacheIndexEntry *aEntry,
michael@0 1352 const uint32_t *aFrecency,
michael@0 1353 const uint32_t *aExpirationTime,
michael@0 1354 const uint32_t *aSize)
michael@0 1355 {
michael@0 1356 if (aFrecency && *aFrecency != aEntry->GetFrecency()) {
michael@0 1357 return true;
michael@0 1358 }
michael@0 1359
michael@0 1360 if (aExpirationTime && *aExpirationTime != aEntry->GetExpirationTime()) {
michael@0 1361 return true;
michael@0 1362 }
michael@0 1363
michael@0 1364 if (aSize &&
michael@0 1365 (*aSize & CacheIndexEntry::kFileSizeMask) != aEntry->GetFileSize()) {
michael@0 1366 return true;
michael@0 1367 }
michael@0 1368
michael@0 1369 return false;
michael@0 1370 }
michael@0 1371
michael@0 1372 void
michael@0 1373 CacheIndex::ProcessPendingOperations()
michael@0 1374 {
michael@0 1375 LOG(("CacheIndex::ProcessPendingOperations()"));
michael@0 1376
michael@0 1377 AssertOwnsLock();
michael@0 1378
michael@0 1379 mPendingUpdates.EnumerateEntries(&CacheIndex::UpdateEntryInIndex, this);
michael@0 1380
michael@0 1381 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 1382
michael@0 1383 EnsureCorrectStats();
michael@0 1384 }
michael@0 1385
michael@0 1386 // static
michael@0 1387 PLDHashOperator
michael@0 1388 CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure)
michael@0 1389 {
michael@0 1390 CacheIndex *index = static_cast<CacheIndex *>(aClosure);
michael@0 1391
michael@0 1392 LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
michael@0 1393 LOGSHA1(aEntry->Hash())));
michael@0 1394
michael@0 1395 MOZ_ASSERT(aEntry->IsFresh());
michael@0 1396 MOZ_ASSERT(aEntry->IsDirty());
michael@0 1397
michael@0 1398 CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
michael@0 1399
michael@0 1400 CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
michael@0 1401 emng.DoNotSearchInUpdates();
michael@0 1402
michael@0 1403 if (aEntry->IsRemoved()) {
michael@0 1404 if (entry) {
michael@0 1405 if (entry->IsRemoved()) {
michael@0 1406 MOZ_ASSERT(entry->IsFresh());
michael@0 1407 MOZ_ASSERT(entry->IsDirty());
michael@0 1408 } else if (!entry->IsDirty() && entry->IsFileEmpty()) {
michael@0 1409 // Entries with empty file are not stored in index on disk. Just remove
michael@0 1410 // the entry, but only in case the entry is not dirty, i.e. the entry
michael@0 1411 // file was empty when we wrote the index.
michael@0 1412 index->mIndex.RemoveEntry(*aEntry->Hash());
michael@0 1413 entry = nullptr;
michael@0 1414 } else {
michael@0 1415 entry->MarkRemoved();
michael@0 1416 entry->MarkDirty();
michael@0 1417 entry->MarkFresh();
michael@0 1418 }
michael@0 1419 }
michael@0 1420
michael@0 1421 return PL_DHASH_REMOVE;
michael@0 1422 }
michael@0 1423
michael@0 1424 entry = index->mIndex.PutEntry(*aEntry->Hash());
michael@0 1425 *entry = *aEntry;
michael@0 1426
michael@0 1427 return PL_DHASH_REMOVE;
michael@0 1428 }
michael@0 1429
michael@0 1430 bool
michael@0 1431 CacheIndex::WriteIndexToDiskIfNeeded()
michael@0 1432 {
michael@0 1433 if (mState != READY || mShuttingDown) {
michael@0 1434 return false;
michael@0 1435 }
michael@0 1436
michael@0 1437 if (!mLastDumpTime.IsNull() &&
michael@0 1438 (TimeStamp::NowLoRes() - mLastDumpTime).ToMilliseconds() <
michael@0 1439 kMinDumpInterval) {
michael@0 1440 return false;
michael@0 1441 }
michael@0 1442
michael@0 1443 if (mIndexStats.Dirty() < kMinUnwrittenChanges) {
michael@0 1444 return false;
michael@0 1445 }
michael@0 1446
michael@0 1447 WriteIndexToDisk();
michael@0 1448 return true;
michael@0 1449 }
michael@0 1450
michael@0 1451 void
michael@0 1452 CacheIndex::WriteIndexToDisk()
michael@0 1453 {
michael@0 1454 LOG(("CacheIndex::WriteIndexToDisk()"));
michael@0 1455 mIndexStats.Log();
michael@0 1456
michael@0 1457 nsresult rv;
michael@0 1458
michael@0 1459 AssertOwnsLock();
michael@0 1460 MOZ_ASSERT(mState == READY);
michael@0 1461 MOZ_ASSERT(!mRWBuf);
michael@0 1462 MOZ_ASSERT(!mRWHash);
michael@0 1463
michael@0 1464 ChangeState(WRITING);
michael@0 1465
michael@0 1466 mProcessEntries = mIndexStats.ActiveEntriesCount();
michael@0 1467
michael@0 1468 mIndexFileOpener = new FileOpenHelper(this);
michael@0 1469 rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
michael@0 1470 CacheFileIOManager::SPECIAL_FILE |
michael@0 1471 CacheFileIOManager::CREATE,
michael@0 1472 true,
michael@0 1473 mIndexFileOpener);
michael@0 1474 if (NS_FAILED(rv)) {
michael@0 1475 LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08x]", rv));
michael@0 1476 FinishWrite(false);
michael@0 1477 return;
michael@0 1478 }
michael@0 1479
michael@0 1480 // Write index header to a buffer, it will be written to disk together with
michael@0 1481 // records in WriteRecords() once we open the file successfully.
michael@0 1482 AllocBuffer();
michael@0 1483 mRWHash = new CacheHash();
michael@0 1484
michael@0 1485 CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>(mRWBuf);
michael@0 1486 NetworkEndian::writeUint32(&hdr->mVersion, kIndexVersion);
michael@0 1487 NetworkEndian::writeUint32(&hdr->mTimeStamp,
michael@0 1488 static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC));
michael@0 1489 NetworkEndian::writeUint32(&hdr->mIsDirty, 1);
michael@0 1490
michael@0 1491 mRWBufPos = sizeof(CacheIndexHeader);
michael@0 1492 mSkipEntries = 0;
michael@0 1493 }
michael@0 1494
michael@0 1495 namespace { // anon
michael@0 1496
michael@0 1497 struct WriteRecordsHelper
michael@0 1498 {
michael@0 1499 char *mBuf;
michael@0 1500 uint32_t mSkip;
michael@0 1501 uint32_t mProcessMax;
michael@0 1502 uint32_t mProcessed;
michael@0 1503 #ifdef DEBUG
michael@0 1504 bool mHasMore;
michael@0 1505 #endif
michael@0 1506 };
michael@0 1507
michael@0 1508 } // anon
michael@0 1509
michael@0 1510 void
michael@0 1511 CacheIndex::WriteRecords()
michael@0 1512 {
michael@0 1513 LOG(("CacheIndex::WriteRecords()"));
michael@0 1514
michael@0 1515 nsresult rv;
michael@0 1516
michael@0 1517 AssertOwnsLock();
michael@0 1518 MOZ_ASSERT(mState == WRITING);
michael@0 1519
michael@0 1520 int64_t fileOffset;
michael@0 1521
michael@0 1522 if (mSkipEntries) {
michael@0 1523 MOZ_ASSERT(mRWBufPos == 0);
michael@0 1524 fileOffset = sizeof(CacheIndexHeader);
michael@0 1525 fileOffset += sizeof(CacheIndexRecord) * mSkipEntries;
michael@0 1526 } else {
michael@0 1527 MOZ_ASSERT(mRWBufPos == sizeof(CacheIndexHeader));
michael@0 1528 fileOffset = 0;
michael@0 1529 }
michael@0 1530 uint32_t hashOffset = mRWBufPos;
michael@0 1531
michael@0 1532 WriteRecordsHelper data;
michael@0 1533 data.mBuf = mRWBuf + mRWBufPos;
michael@0 1534 data.mSkip = mSkipEntries;
michael@0 1535 data.mProcessMax = (mRWBufSize - mRWBufPos) / sizeof(CacheIndexRecord);
michael@0 1536 MOZ_ASSERT(data.mProcessMax != 0 || mProcessEntries == 0); // TODO make sure we can write an empty index
michael@0 1537 data.mProcessed = 0;
michael@0 1538 #ifdef DEBUG
michael@0 1539 data.mHasMore = false;
michael@0 1540 #endif
michael@0 1541
michael@0 1542 mIndex.EnumerateEntries(&CacheIndex::CopyRecordsToRWBuf, &data);
michael@0 1543 MOZ_ASSERT(mRWBufPos != static_cast<uint32_t>(data.mBuf - mRWBuf) ||
michael@0 1544 mProcessEntries == 0);
michael@0 1545 mRWBufPos = data.mBuf - mRWBuf;
michael@0 1546 mSkipEntries += data.mProcessed;
michael@0 1547 MOZ_ASSERT(mSkipEntries <= mProcessEntries);
michael@0 1548
michael@0 1549 mRWHash->Update(mRWBuf + hashOffset, mRWBufPos - hashOffset);
michael@0 1550
michael@0 1551 if (mSkipEntries == mProcessEntries) {
michael@0 1552 MOZ_ASSERT(!data.mHasMore);
michael@0 1553
michael@0 1554 // We've processed all records
michael@0 1555 if (mRWBufPos + sizeof(CacheHash::Hash32_t) > mRWBufSize) {
michael@0 1556 // realloc buffer to spare another write cycle
michael@0 1557 mRWBufSize = mRWBufPos + sizeof(CacheHash::Hash32_t);
michael@0 1558 mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mRWBufSize));
michael@0 1559 }
michael@0 1560
michael@0 1561 NetworkEndian::writeUint32(mRWBuf + mRWBufPos, mRWHash->GetHash());
michael@0 1562 mRWBufPos += sizeof(CacheHash::Hash32_t);
michael@0 1563 } else {
michael@0 1564 MOZ_ASSERT(data.mHasMore);
michael@0 1565 }
michael@0 1566
michael@0 1567 rv = CacheFileIOManager::Write(mIndexHandle, fileOffset, mRWBuf, mRWBufPos,
michael@0 1568 mSkipEntries == mProcessEntries, this);
michael@0 1569 if (NS_FAILED(rv)) {
michael@0 1570 LOG(("CacheIndex::WriteRecords() - CacheFileIOManager::Write() failed "
michael@0 1571 "synchronously [rv=0x%08x]", rv));
michael@0 1572 FinishWrite(false);
michael@0 1573 }
michael@0 1574
michael@0 1575 mRWBufPos = 0;
michael@0 1576 }
michael@0 1577
michael@0 1578 void
michael@0 1579 CacheIndex::FinishWrite(bool aSucceeded)
michael@0 1580 {
michael@0 1581 LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded));
michael@0 1582
michael@0 1583 MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == WRITING);
michael@0 1584
michael@0 1585 AssertOwnsLock();
michael@0 1586
michael@0 1587 mIndexHandle = nullptr;
michael@0 1588 mRWHash = nullptr;
michael@0 1589 ReleaseBuffer();
michael@0 1590
michael@0 1591 if (aSucceeded) {
michael@0 1592 // Opening of the file must not be in progress if writing succeeded.
michael@0 1593 MOZ_ASSERT(!mIndexFileOpener);
michael@0 1594
michael@0 1595 mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
michael@0 1596 mIndexOnDiskIsValid = true;
michael@0 1597 } else {
michael@0 1598 if (mIndexFileOpener) {
michael@0 1599 // If opening of the file is still in progress (e.g. WRITE process was
michael@0 1600 // canceled by RemoveAll()) then we need to cancel the opener to make sure
michael@0 1601 // that OnFileOpenedInternal() won't be called.
michael@0 1602 mIndexFileOpener->Cancel();
michael@0 1603 mIndexFileOpener = nullptr;
michael@0 1604 }
michael@0 1605 }
michael@0 1606
michael@0 1607 ProcessPendingOperations();
michael@0 1608 mIndexStats.Log();
michael@0 1609
michael@0 1610 if (mState == WRITING) {
michael@0 1611 ChangeState(READY);
michael@0 1612 mLastDumpTime = TimeStamp::NowLoRes();
michael@0 1613 }
michael@0 1614 }
michael@0 1615
michael@0 1616 // static
michael@0 1617 PLDHashOperator
michael@0 1618 CacheIndex::CopyRecordsToRWBuf(CacheIndexEntry *aEntry, void* aClosure)
michael@0 1619 {
michael@0 1620 if (aEntry->IsRemoved()) {
michael@0 1621 return PL_DHASH_NEXT;
michael@0 1622 }
michael@0 1623
michael@0 1624 if (!aEntry->IsInitialized()) {
michael@0 1625 return PL_DHASH_NEXT;
michael@0 1626 }
michael@0 1627
michael@0 1628 if (aEntry->IsFileEmpty()) {
michael@0 1629 return PL_DHASH_NEXT;
michael@0 1630 }
michael@0 1631
michael@0 1632 WriteRecordsHelper *data = static_cast<WriteRecordsHelper *>(aClosure);
michael@0 1633 if (data->mSkip) {
michael@0 1634 data->mSkip--;
michael@0 1635 return PL_DHASH_NEXT;
michael@0 1636 }
michael@0 1637
michael@0 1638 if (data->mProcessed == data->mProcessMax) {
michael@0 1639 #ifdef DEBUG
michael@0 1640 data->mHasMore = true;
michael@0 1641 #endif
michael@0 1642 return PL_DHASH_STOP;
michael@0 1643 }
michael@0 1644
michael@0 1645 aEntry->WriteToBuf(data->mBuf);
michael@0 1646 data->mBuf += sizeof(CacheIndexRecord);
michael@0 1647 data->mProcessed++;
michael@0 1648
michael@0 1649 return PL_DHASH_NEXT;
michael@0 1650 }
michael@0 1651
michael@0 1652 // static
michael@0 1653 PLDHashOperator
michael@0 1654 CacheIndex::ApplyIndexChanges(CacheIndexEntry *aEntry, void* aClosure)
michael@0 1655 {
michael@0 1656 CacheIndex *index = static_cast<CacheIndex *>(aClosure);
michael@0 1657
michael@0 1658 CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
michael@0 1659
michael@0 1660 if (aEntry->IsRemoved()) {
michael@0 1661 emng.DoNotSearchInIndex();
michael@0 1662 return PL_DHASH_REMOVE;
michael@0 1663 }
michael@0 1664
michael@0 1665 if (aEntry->IsDirty()) {
michael@0 1666 aEntry->ClearDirty();
michael@0 1667 }
michael@0 1668
michael@0 1669 return PL_DHASH_NEXT;
michael@0 1670 }
michael@0 1671
michael@0 1672 nsresult
michael@0 1673 CacheIndex::GetFile(const nsACString &aName, nsIFile **_retval)
michael@0 1674 {
michael@0 1675 nsresult rv;
michael@0 1676
michael@0 1677 nsCOMPtr<nsIFile> file;
michael@0 1678 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 1679 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1680
michael@0 1681 rv = file->AppendNative(aName);
michael@0 1682 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1683
michael@0 1684 file.swap(*_retval);
michael@0 1685 return NS_OK;
michael@0 1686 }
michael@0 1687
michael@0 1688 nsresult
michael@0 1689 CacheIndex::RemoveFile(const nsACString &aName)
michael@0 1690 {
michael@0 1691 MOZ_ASSERT(mState == SHUTDOWN);
michael@0 1692
michael@0 1693 nsresult rv;
michael@0 1694
michael@0 1695 nsCOMPtr<nsIFile> file;
michael@0 1696 rv = GetFile(aName, getter_AddRefs(file));
michael@0 1697 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1698
michael@0 1699 bool exists;
michael@0 1700 rv = file->Exists(&exists);
michael@0 1701 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1702
michael@0 1703 if (exists) {
michael@0 1704 rv = file->Remove(false);
michael@0 1705 if (NS_FAILED(rv)) {
michael@0 1706 LOG(("CacheIndex::RemoveFile() - Cannot remove old entry file from disk."
michael@0 1707 "[name=%s]", PromiseFlatCString(aName).get()));
michael@0 1708 NS_WARNING("Cannot remove old entry file from the disk");
michael@0 1709 return rv;
michael@0 1710 }
michael@0 1711 }
michael@0 1712
michael@0 1713 return NS_OK;
michael@0 1714 }
michael@0 1715
michael@0 1716 void
michael@0 1717 CacheIndex::RemoveIndexFromDisk()
michael@0 1718 {
michael@0 1719 LOG(("CacheIndex::RemoveIndexFromDisk()"));
michael@0 1720
michael@0 1721 RemoveFile(NS_LITERAL_CSTRING(kIndexName));
michael@0 1722 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName));
michael@0 1723 RemoveFile(NS_LITERAL_CSTRING(kJournalName));
michael@0 1724 }
michael@0 1725
michael@0 1726 class WriteLogHelper
michael@0 1727 {
michael@0 1728 public:
michael@0 1729 WriteLogHelper(PRFileDesc *aFD)
michael@0 1730 : mStatus(NS_OK)
michael@0 1731 , mFD(aFD)
michael@0 1732 , mBufSize(kMaxBufSize)
michael@0 1733 , mBufPos(0)
michael@0 1734 {
michael@0 1735 mHash = new CacheHash();
michael@0 1736 mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
michael@0 1737 }
michael@0 1738
michael@0 1739 ~WriteLogHelper() {
michael@0 1740 free(mBuf);
michael@0 1741 }
michael@0 1742
michael@0 1743 nsresult AddEntry(CacheIndexEntry *aEntry);
michael@0 1744 nsresult Finish();
michael@0 1745
michael@0 1746 private:
michael@0 1747
michael@0 1748 nsresult FlushBuffer();
michael@0 1749
michael@0 1750 nsresult mStatus;
michael@0 1751 PRFileDesc *mFD;
michael@0 1752 char *mBuf;
michael@0 1753 uint32_t mBufSize;
michael@0 1754 int32_t mBufPos;
michael@0 1755 nsRefPtr<CacheHash> mHash;
michael@0 1756 };
michael@0 1757
michael@0 1758 nsresult
michael@0 1759 WriteLogHelper::AddEntry(CacheIndexEntry *aEntry)
michael@0 1760 {
michael@0 1761 nsresult rv;
michael@0 1762
michael@0 1763 if (NS_FAILED(mStatus)) {
michael@0 1764 return mStatus;
michael@0 1765 }
michael@0 1766
michael@0 1767 if (mBufPos + sizeof(CacheIndexRecord) > mBufSize) {
michael@0 1768 mHash->Update(mBuf, mBufPos);
michael@0 1769
michael@0 1770 rv = FlushBuffer();
michael@0 1771 if (NS_FAILED(rv)) {
michael@0 1772 mStatus = rv;
michael@0 1773 return rv;
michael@0 1774 }
michael@0 1775 MOZ_ASSERT(mBufPos + sizeof(CacheIndexRecord) <= mBufSize);
michael@0 1776 }
michael@0 1777
michael@0 1778 aEntry->WriteToBuf(mBuf + mBufPos);
michael@0 1779 mBufPos += sizeof(CacheIndexRecord);
michael@0 1780
michael@0 1781 return NS_OK;
michael@0 1782 }
michael@0 1783
michael@0 1784 nsresult
michael@0 1785 WriteLogHelper::Finish()
michael@0 1786 {
michael@0 1787 nsresult rv;
michael@0 1788
michael@0 1789 if (NS_FAILED(mStatus)) {
michael@0 1790 return mStatus;
michael@0 1791 }
michael@0 1792
michael@0 1793 mHash->Update(mBuf, mBufPos);
michael@0 1794 if (mBufPos + sizeof(CacheHash::Hash32_t) > mBufSize) {
michael@0 1795 rv = FlushBuffer();
michael@0 1796 if (NS_FAILED(rv)) {
michael@0 1797 mStatus = rv;
michael@0 1798 return rv;
michael@0 1799 }
michael@0 1800 MOZ_ASSERT(mBufPos + sizeof(CacheHash::Hash32_t) <= mBufSize);
michael@0 1801 }
michael@0 1802
michael@0 1803 NetworkEndian::writeUint32(mBuf + mBufPos, mHash->GetHash());
michael@0 1804 mBufPos += sizeof(CacheHash::Hash32_t);
michael@0 1805
michael@0 1806 rv = FlushBuffer();
michael@0 1807 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1808
michael@0 1809 mStatus = NS_ERROR_UNEXPECTED; // Don't allow any other operation
michael@0 1810 return NS_OK;
michael@0 1811 }
michael@0 1812
michael@0 1813 nsresult
michael@0 1814 WriteLogHelper::FlushBuffer()
michael@0 1815 {
michael@0 1816 MOZ_ASSERT(NS_SUCCEEDED(mStatus));
michael@0 1817
michael@0 1818 int32_t bytesWritten = PR_Write(mFD, mBuf, mBufPos);
michael@0 1819
michael@0 1820 if (bytesWritten != mBufPos) {
michael@0 1821 return NS_ERROR_FAILURE;
michael@0 1822 }
michael@0 1823
michael@0 1824 mBufPos = 0;
michael@0 1825 return NS_OK;
michael@0 1826 }
michael@0 1827
michael@0 1828 nsresult
michael@0 1829 CacheIndex::WriteLogToDisk()
michael@0 1830 {
michael@0 1831 LOG(("CacheIndex::WriteLogToDisk()"));
michael@0 1832
michael@0 1833 nsresult rv;
michael@0 1834
michael@0 1835 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 1836 MOZ_ASSERT(mState == SHUTDOWN);
michael@0 1837
michael@0 1838 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName));
michael@0 1839
michael@0 1840 nsCOMPtr<nsIFile> indexFile;
michael@0 1841 rv = GetFile(NS_LITERAL_CSTRING(kIndexName), getter_AddRefs(indexFile));
michael@0 1842 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1843
michael@0 1844 nsCOMPtr<nsIFile> logFile;
michael@0 1845 rv = GetFile(NS_LITERAL_CSTRING(kJournalName), getter_AddRefs(logFile));
michael@0 1846 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1847
michael@0 1848 mIndexStats.Log();
michael@0 1849
michael@0 1850 PRFileDesc *fd = nullptr;
michael@0 1851 rv = logFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
michael@0 1852 0600, &fd);
michael@0 1853 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1854
michael@0 1855 WriteLogHelper wlh(fd);
michael@0 1856 mIndex.EnumerateEntries(&CacheIndex::WriteEntryToLog, &wlh);
michael@0 1857
michael@0 1858 rv = wlh.Finish();
michael@0 1859 PR_Close(fd);
michael@0 1860 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1861
michael@0 1862 rv = indexFile->OpenNSPRFileDesc(PR_RDWR, 0600, &fd);
michael@0 1863 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1864
michael@0 1865 CacheIndexHeader header;
michael@0 1866 int32_t bytesRead = PR_Read(fd, &header, sizeof(CacheIndexHeader));
michael@0 1867 if (bytesRead != sizeof(CacheIndexHeader)) {
michael@0 1868 PR_Close(fd);
michael@0 1869 return NS_ERROR_FAILURE;
michael@0 1870 }
michael@0 1871
michael@0 1872 NetworkEndian::writeUint32(&header.mIsDirty, 0);
michael@0 1873
michael@0 1874 int64_t offset = PR_Seek64(fd, 0, PR_SEEK_SET);
michael@0 1875 if (offset == -1) {
michael@0 1876 PR_Close(fd);
michael@0 1877 return NS_ERROR_FAILURE;
michael@0 1878 }
michael@0 1879
michael@0 1880 int32_t bytesWritten = PR_Write(fd, &header, sizeof(CacheIndexHeader));
michael@0 1881 PR_Close(fd);
michael@0 1882 if (bytesWritten != sizeof(CacheIndexHeader)) {
michael@0 1883 return NS_ERROR_FAILURE;
michael@0 1884 }
michael@0 1885
michael@0 1886 return NS_OK;
michael@0 1887 }
michael@0 1888
michael@0 1889 // static
michael@0 1890 PLDHashOperator
michael@0 1891 CacheIndex::WriteEntryToLog(CacheIndexEntry *aEntry, void* aClosure)
michael@0 1892 {
michael@0 1893 WriteLogHelper *wlh = static_cast<WriteLogHelper *>(aClosure);
michael@0 1894
michael@0 1895 if (aEntry->IsRemoved() || aEntry->IsDirty()) {
michael@0 1896 wlh->AddEntry(aEntry);
michael@0 1897 }
michael@0 1898
michael@0 1899 return PL_DHASH_REMOVE;
michael@0 1900 }
michael@0 1901
michael@0 1902 void
michael@0 1903 CacheIndex::ReadIndexFromDisk()
michael@0 1904 {
michael@0 1905 LOG(("CacheIndex::ReadIndexFromDisk()"));
michael@0 1906
michael@0 1907 nsresult rv;
michael@0 1908
michael@0 1909 AssertOwnsLock();
michael@0 1910 MOZ_ASSERT(mState == INITIAL);
michael@0 1911
michael@0 1912 ChangeState(READING);
michael@0 1913
michael@0 1914 mIndexFileOpener = new FileOpenHelper(this);
michael@0 1915 rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kIndexName),
michael@0 1916 CacheFileIOManager::SPECIAL_FILE |
michael@0 1917 CacheFileIOManager::OPEN,
michael@0 1918 true,
michael@0 1919 mIndexFileOpener);
michael@0 1920 if (NS_FAILED(rv)) {
michael@0 1921 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
michael@0 1922 "failed [rv=0x%08x, file=%s]", rv, kIndexName));
michael@0 1923 FinishRead(false);
michael@0 1924 return;
michael@0 1925 }
michael@0 1926
michael@0 1927 mJournalFileOpener = new FileOpenHelper(this);
michael@0 1928 rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kJournalName),
michael@0 1929 CacheFileIOManager::SPECIAL_FILE |
michael@0 1930 CacheFileIOManager::OPEN,
michael@0 1931 true,
michael@0 1932 mJournalFileOpener);
michael@0 1933 if (NS_FAILED(rv)) {
michael@0 1934 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
michael@0 1935 "failed [rv=0x%08x, file=%s]", rv, kJournalName));
michael@0 1936 FinishRead(false);
michael@0 1937 }
michael@0 1938
michael@0 1939 mTmpFileOpener = new FileOpenHelper(this);
michael@0 1940 rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName),
michael@0 1941 CacheFileIOManager::SPECIAL_FILE |
michael@0 1942 CacheFileIOManager::OPEN,
michael@0 1943 true,
michael@0 1944 mTmpFileOpener);
michael@0 1945 if (NS_FAILED(rv)) {
michael@0 1946 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
michael@0 1947 "failed [rv=0x%08x, file=%s]", rv, kTempIndexName));
michael@0 1948 FinishRead(false);
michael@0 1949 }
michael@0 1950 }
michael@0 1951
michael@0 1952 void
michael@0 1953 CacheIndex::StartReadingIndex()
michael@0 1954 {
michael@0 1955 LOG(("CacheIndex::StartReadingIndex()"));
michael@0 1956
michael@0 1957 nsresult rv;
michael@0 1958
michael@0 1959 AssertOwnsLock();
michael@0 1960
michael@0 1961 MOZ_ASSERT(mIndexHandle);
michael@0 1962 MOZ_ASSERT(mState == READING);
michael@0 1963 MOZ_ASSERT(!mIndexOnDiskIsValid);
michael@0 1964 MOZ_ASSERT(!mDontMarkIndexClean);
michael@0 1965 MOZ_ASSERT(!mJournalReadSuccessfully);
michael@0 1966 MOZ_ASSERT(mIndexHandle->FileSize() >= 0);
michael@0 1967
michael@0 1968 int64_t entriesSize = mIndexHandle->FileSize() - sizeof(CacheIndexHeader) -
michael@0 1969 sizeof(CacheHash::Hash32_t);
michael@0 1970
michael@0 1971 if (entriesSize < 0 || entriesSize % sizeof(CacheIndexRecord)) {
michael@0 1972 LOG(("CacheIndex::StartReadingIndex() - Index is corrupted"));
michael@0 1973 FinishRead(false);
michael@0 1974 return;
michael@0 1975 }
michael@0 1976
michael@0 1977 AllocBuffer();
michael@0 1978 mSkipEntries = 0;
michael@0 1979 mRWHash = new CacheHash();
michael@0 1980
michael@0 1981 mRWBufPos = std::min(mRWBufSize,
michael@0 1982 static_cast<uint32_t>(mIndexHandle->FileSize()));
michael@0 1983
michael@0 1984 rv = CacheFileIOManager::Read(mIndexHandle, 0, mRWBuf, mRWBufPos, true, this);
michael@0 1985 if (NS_FAILED(rv)) {
michael@0 1986 LOG(("CacheIndex::StartReadingIndex() - CacheFileIOManager::Read() failed "
michael@0 1987 "synchronously [rv=0x%08x]", rv));
michael@0 1988 FinishRead(false);
michael@0 1989 }
michael@0 1990 }
michael@0 1991
michael@0 1992 void
michael@0 1993 CacheIndex::ParseRecords()
michael@0 1994 {
michael@0 1995 LOG(("CacheIndex::ParseRecords()"));
michael@0 1996
michael@0 1997 nsresult rv;
michael@0 1998
michael@0 1999 AssertOwnsLock();
michael@0 2000
michael@0 2001 uint32_t entryCnt = (mIndexHandle->FileSize() - sizeof(CacheIndexHeader) -
michael@0 2002 sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
michael@0 2003 uint32_t pos = 0;
michael@0 2004
michael@0 2005 if (!mSkipEntries) {
michael@0 2006 CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>(
michael@0 2007 moz_xmalloc(sizeof(CacheIndexHeader)));
michael@0 2008 memcpy(hdr, mRWBuf, sizeof(CacheIndexHeader));
michael@0 2009
michael@0 2010 if (NetworkEndian::readUint32(&hdr->mVersion) != kIndexVersion) {
michael@0 2011 free(hdr);
michael@0 2012 FinishRead(false);
michael@0 2013 return;
michael@0 2014 }
michael@0 2015
michael@0 2016 mIndexTimeStamp = NetworkEndian::readUint32(&hdr->mTimeStamp);
michael@0 2017
michael@0 2018 if (NetworkEndian::readUint32(&hdr->mIsDirty)) {
michael@0 2019 if (mJournalHandle) {
michael@0 2020 CacheFileIOManager::DoomFile(mJournalHandle, nullptr);
michael@0 2021 mJournalHandle = nullptr;
michael@0 2022 }
michael@0 2023 free(hdr);
michael@0 2024 } else {
michael@0 2025 NetworkEndian::writeUint32(&hdr->mIsDirty, 1);
michael@0 2026
michael@0 2027 // Mark index dirty. The buffer is freed by CacheFileIOManager when
michael@0 2028 // nullptr is passed as the listener and the call doesn't fail
michael@0 2029 // synchronously.
michael@0 2030 rv = CacheFileIOManager::Write(mIndexHandle, 0,
michael@0 2031 reinterpret_cast<char *>(hdr),
michael@0 2032 sizeof(CacheIndexHeader), true, nullptr);
michael@0 2033 if (NS_FAILED(rv)) {
michael@0 2034 // This is not fatal, just free the memory
michael@0 2035 free(hdr);
michael@0 2036 }
michael@0 2037 }
michael@0 2038
michael@0 2039 pos += sizeof(CacheIndexHeader);
michael@0 2040 }
michael@0 2041
michael@0 2042 uint32_t hashOffset = pos;
michael@0 2043
michael@0 2044 while (pos + sizeof(CacheIndexRecord) <= mRWBufPos &&
michael@0 2045 mSkipEntries != entryCnt) {
michael@0 2046 CacheIndexRecord *rec = reinterpret_cast<CacheIndexRecord *>(mRWBuf + pos);
michael@0 2047 CacheIndexEntry tmpEntry(&rec->mHash);
michael@0 2048 tmpEntry.ReadFromBuf(mRWBuf + pos);
michael@0 2049
michael@0 2050 if (tmpEntry.IsDirty() || !tmpEntry.IsInitialized() ||
michael@0 2051 tmpEntry.IsFileEmpty() || tmpEntry.IsFresh() || tmpEntry.IsRemoved()) {
michael@0 2052 LOG(("CacheIndex::ParseRecords() - Invalid entry found in index, removing"
michael@0 2053 " whole index [dirty=%d, initialized=%d, fileEmpty=%d, fresh=%d, "
michael@0 2054 "removed=%d]", tmpEntry.IsDirty(), tmpEntry.IsInitialized(),
michael@0 2055 tmpEntry.IsFileEmpty(), tmpEntry.IsFresh(), tmpEntry.IsRemoved()));
michael@0 2056 FinishRead(false);
michael@0 2057 return;
michael@0 2058 }
michael@0 2059
michael@0 2060 CacheIndexEntryAutoManage emng(tmpEntry.Hash(), this);
michael@0 2061
michael@0 2062 CacheIndexEntry *entry = mIndex.PutEntry(*tmpEntry.Hash());
michael@0 2063 *entry = tmpEntry;
michael@0 2064
michael@0 2065 pos += sizeof(CacheIndexRecord);
michael@0 2066 mSkipEntries++;
michael@0 2067 }
michael@0 2068
michael@0 2069 mRWHash->Update(mRWBuf + hashOffset, pos - hashOffset);
michael@0 2070
michael@0 2071 if (pos != mRWBufPos) {
michael@0 2072 memmove(mRWBuf, mRWBuf + pos, mRWBufPos - pos);
michael@0 2073 mRWBufPos -= pos;
michael@0 2074 pos = 0;
michael@0 2075 }
michael@0 2076
michael@0 2077 int64_t fileOffset = sizeof(CacheIndexHeader) +
michael@0 2078 mSkipEntries * sizeof(CacheIndexRecord) + mRWBufPos;
michael@0 2079
michael@0 2080 MOZ_ASSERT(fileOffset <= mIndexHandle->FileSize());
michael@0 2081 if (fileOffset == mIndexHandle->FileSize()) {
michael@0 2082 if (mRWHash->GetHash() != NetworkEndian::readUint32(mRWBuf)) {
michael@0 2083 LOG(("CacheIndex::ParseRecords() - Hash mismatch, [is %x, should be %x]",
michael@0 2084 mRWHash->GetHash(),
michael@0 2085 NetworkEndian::readUint32(mRWBuf)));
michael@0 2086 FinishRead(false);
michael@0 2087 return;
michael@0 2088 }
michael@0 2089
michael@0 2090 mIndexOnDiskIsValid = true;
michael@0 2091 mJournalReadSuccessfully = false;
michael@0 2092
michael@0 2093 if (mJournalHandle) {
michael@0 2094 StartReadingJournal();
michael@0 2095 } else {
michael@0 2096 FinishRead(false);
michael@0 2097 }
michael@0 2098
michael@0 2099 return;
michael@0 2100 }
michael@0 2101
michael@0 2102 pos = mRWBufPos;
michael@0 2103 uint32_t toRead = std::min(mRWBufSize - pos,
michael@0 2104 static_cast<uint32_t>(mIndexHandle->FileSize() -
michael@0 2105 fileOffset));
michael@0 2106 mRWBufPos = pos + toRead;
michael@0 2107
michael@0 2108 rv = CacheFileIOManager::Read(mIndexHandle, fileOffset, mRWBuf + pos, toRead,
michael@0 2109 true, this);
michael@0 2110 if (NS_FAILED(rv)) {
michael@0 2111 LOG(("CacheIndex::ParseRecords() - CacheFileIOManager::Read() failed "
michael@0 2112 "synchronously [rv=0x%08x]", rv));
michael@0 2113 FinishRead(false);
michael@0 2114 return;
michael@0 2115 }
michael@0 2116 }
michael@0 2117
michael@0 2118 void
michael@0 2119 CacheIndex::StartReadingJournal()
michael@0 2120 {
michael@0 2121 LOG(("CacheIndex::StartReadingJournal()"));
michael@0 2122
michael@0 2123 nsresult rv;
michael@0 2124
michael@0 2125 AssertOwnsLock();
michael@0 2126
michael@0 2127 MOZ_ASSERT(mJournalHandle);
michael@0 2128 MOZ_ASSERT(mIndexOnDiskIsValid);
michael@0 2129 MOZ_ASSERT(mTmpJournal.Count() == 0);
michael@0 2130 MOZ_ASSERT(mJournalHandle->FileSize() >= 0);
michael@0 2131
michael@0 2132 int64_t entriesSize = mJournalHandle->FileSize() -
michael@0 2133 sizeof(CacheHash::Hash32_t);
michael@0 2134
michael@0 2135 if (entriesSize < 0 || entriesSize % sizeof(CacheIndexRecord)) {
michael@0 2136 LOG(("CacheIndex::StartReadingJournal() - Journal is corrupted"));
michael@0 2137 FinishRead(false);
michael@0 2138 return;
michael@0 2139 }
michael@0 2140
michael@0 2141 mSkipEntries = 0;
michael@0 2142 mRWHash = new CacheHash();
michael@0 2143
michael@0 2144 mRWBufPos = std::min(mRWBufSize,
michael@0 2145 static_cast<uint32_t>(mJournalHandle->FileSize()));
michael@0 2146
michael@0 2147 rv = CacheFileIOManager::Read(mJournalHandle, 0, mRWBuf, mRWBufPos, true, this);
michael@0 2148 if (NS_FAILED(rv)) {
michael@0 2149 LOG(("CacheIndex::StartReadingJournal() - CacheFileIOManager::Read() failed"
michael@0 2150 " synchronously [rv=0x%08x]", rv));
michael@0 2151 FinishRead(false);
michael@0 2152 }
michael@0 2153 }
michael@0 2154
michael@0 2155 void
michael@0 2156 CacheIndex::ParseJournal()
michael@0 2157 {
michael@0 2158 LOG(("CacheIndex::ParseRecords()"));
michael@0 2159
michael@0 2160 nsresult rv;
michael@0 2161
michael@0 2162 AssertOwnsLock();
michael@0 2163
michael@0 2164 uint32_t entryCnt = (mJournalHandle->FileSize() -
michael@0 2165 sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
michael@0 2166
michael@0 2167 uint32_t pos = 0;
michael@0 2168
michael@0 2169 while (pos + sizeof(CacheIndexRecord) <= mRWBufPos &&
michael@0 2170 mSkipEntries != entryCnt) {
michael@0 2171 CacheIndexRecord *rec = reinterpret_cast<CacheIndexRecord *>(mRWBuf + pos);
michael@0 2172 CacheIndexEntry tmpEntry(&rec->mHash);
michael@0 2173 tmpEntry.ReadFromBuf(mRWBuf + pos);
michael@0 2174
michael@0 2175 CacheIndexEntry *entry = mTmpJournal.PutEntry(*tmpEntry.Hash());
michael@0 2176 *entry = tmpEntry;
michael@0 2177
michael@0 2178 if (entry->IsDirty() || entry->IsFresh()) {
michael@0 2179 LOG(("CacheIndex::ParseJournal() - Invalid entry found in journal, "
michael@0 2180 "ignoring whole journal [dirty=%d, fresh=%d]", entry->IsDirty(),
michael@0 2181 entry->IsFresh()));
michael@0 2182 FinishRead(false);
michael@0 2183 return;
michael@0 2184 }
michael@0 2185
michael@0 2186 pos += sizeof(CacheIndexRecord);
michael@0 2187 mSkipEntries++;
michael@0 2188 }
michael@0 2189
michael@0 2190 mRWHash->Update(mRWBuf, pos);
michael@0 2191
michael@0 2192 if (pos != mRWBufPos) {
michael@0 2193 memmove(mRWBuf, mRWBuf + pos, mRWBufPos - pos);
michael@0 2194 mRWBufPos -= pos;
michael@0 2195 pos = 0;
michael@0 2196 }
michael@0 2197
michael@0 2198 int64_t fileOffset = mSkipEntries * sizeof(CacheIndexRecord) + mRWBufPos;
michael@0 2199
michael@0 2200 MOZ_ASSERT(fileOffset <= mJournalHandle->FileSize());
michael@0 2201 if (fileOffset == mJournalHandle->FileSize()) {
michael@0 2202 if (mRWHash->GetHash() != NetworkEndian::readUint32(mRWBuf)) {
michael@0 2203 LOG(("CacheIndex::ParseJournal() - Hash mismatch, [is %x, should be %x]",
michael@0 2204 mRWHash->GetHash(),
michael@0 2205 NetworkEndian::readUint32(mRWBuf)));
michael@0 2206 FinishRead(false);
michael@0 2207 return;
michael@0 2208 }
michael@0 2209
michael@0 2210 mJournalReadSuccessfully = true;
michael@0 2211 FinishRead(true);
michael@0 2212 return;
michael@0 2213 }
michael@0 2214
michael@0 2215 pos = mRWBufPos;
michael@0 2216 uint32_t toRead = std::min(mRWBufSize - pos,
michael@0 2217 static_cast<uint32_t>(mJournalHandle->FileSize() -
michael@0 2218 fileOffset));
michael@0 2219 mRWBufPos = pos + toRead;
michael@0 2220
michael@0 2221 rv = CacheFileIOManager::Read(mJournalHandle, fileOffset, mRWBuf + pos,
michael@0 2222 toRead, true, this);
michael@0 2223 if (NS_FAILED(rv)) {
michael@0 2224 LOG(("CacheIndex::ParseJournal() - CacheFileIOManager::Read() failed "
michael@0 2225 "synchronously [rv=0x%08x]", rv));
michael@0 2226 FinishRead(false);
michael@0 2227 return;
michael@0 2228 }
michael@0 2229 }
michael@0 2230
michael@0 2231 void
michael@0 2232 CacheIndex::MergeJournal()
michael@0 2233 {
michael@0 2234 LOG(("CacheIndex::MergeJournal()"));
michael@0 2235
michael@0 2236 AssertOwnsLock();
michael@0 2237
michael@0 2238 mTmpJournal.EnumerateEntries(&CacheIndex::ProcessJournalEntry, this);
michael@0 2239
michael@0 2240 MOZ_ASSERT(mTmpJournal.Count() == 0);
michael@0 2241 }
michael@0 2242
michael@0 2243 // static
michael@0 2244 PLDHashOperator
michael@0 2245 CacheIndex::ProcessJournalEntry(CacheIndexEntry *aEntry, void* aClosure)
michael@0 2246 {
michael@0 2247 CacheIndex *index = static_cast<CacheIndex *>(aClosure);
michael@0 2248
michael@0 2249 LOG(("CacheFile::ProcessJournalEntry() [hash=%08x%08x%08x%08x%08x]",
michael@0 2250 LOGSHA1(aEntry->Hash())));
michael@0 2251
michael@0 2252 CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
michael@0 2253
michael@0 2254 CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
michael@0 2255
michael@0 2256 if (aEntry->IsRemoved()) {
michael@0 2257 if (entry) {
michael@0 2258 entry->MarkRemoved();
michael@0 2259 entry->MarkDirty();
michael@0 2260 }
michael@0 2261 } else {
michael@0 2262 if (!entry) {
michael@0 2263 entry = index->mIndex.PutEntry(*aEntry->Hash());
michael@0 2264 }
michael@0 2265
michael@0 2266 *entry = *aEntry;
michael@0 2267 entry->MarkDirty();
michael@0 2268 }
michael@0 2269
michael@0 2270 return PL_DHASH_REMOVE;
michael@0 2271 }
michael@0 2272
michael@0 2273 void
michael@0 2274 CacheIndex::EnsureNoFreshEntry()
michael@0 2275 {
michael@0 2276 #ifdef DEBUG_STATS
michael@0 2277 CacheIndexStats debugStats;
michael@0 2278 debugStats.DisableLogging();
michael@0 2279 mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
michael@0 2280 MOZ_ASSERT(debugStats.Fresh() == 0);
michael@0 2281 #endif
michael@0 2282 }
michael@0 2283
michael@0 2284 void
michael@0 2285 CacheIndex::EnsureCorrectStats()
michael@0 2286 {
michael@0 2287 #ifdef DEBUG_STATS
michael@0 2288 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 2289 CacheIndexStats debugStats;
michael@0 2290 debugStats.DisableLogging();
michael@0 2291 mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
michael@0 2292 MOZ_ASSERT(debugStats == mIndexStats);
michael@0 2293 #endif
michael@0 2294 }
michael@0 2295
michael@0 2296 // static
michael@0 2297 PLDHashOperator
michael@0 2298 CacheIndex::SumIndexStats(CacheIndexEntry *aEntry, void* aClosure)
michael@0 2299 {
michael@0 2300 CacheIndexStats *stats = static_cast<CacheIndexStats *>(aClosure);
michael@0 2301 stats->BeforeChange(nullptr);
michael@0 2302 stats->AfterChange(aEntry);
michael@0 2303 return PL_DHASH_NEXT;
michael@0 2304 }
michael@0 2305
michael@0 2306 void
michael@0 2307 CacheIndex::FinishRead(bool aSucceeded)
michael@0 2308 {
michael@0 2309 LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded));
michael@0 2310 AssertOwnsLock();
michael@0 2311
michael@0 2312 MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING);
michael@0 2313
michael@0 2314 MOZ_ASSERT(
michael@0 2315 // -> rebuild
michael@0 2316 (!aSucceeded && !mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
michael@0 2317 // -> update
michael@0 2318 (!aSucceeded && mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
michael@0 2319 // -> ready
michael@0 2320 (aSucceeded && mIndexOnDiskIsValid && mJournalReadSuccessfully));
michael@0 2321
michael@0 2322 if (mState == SHUTDOWN) {
michael@0 2323 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName));
michael@0 2324 RemoveFile(NS_LITERAL_CSTRING(kJournalName));
michael@0 2325 } else {
michael@0 2326 if (mIndexHandle && !mIndexOnDiskIsValid) {
michael@0 2327 CacheFileIOManager::DoomFile(mIndexHandle, nullptr);
michael@0 2328 }
michael@0 2329
michael@0 2330 if (mJournalHandle) {
michael@0 2331 CacheFileIOManager::DoomFile(mJournalHandle, nullptr);
michael@0 2332 }
michael@0 2333 }
michael@0 2334
michael@0 2335 if (mIndexFileOpener) {
michael@0 2336 mIndexFileOpener->Cancel();
michael@0 2337 mIndexFileOpener = nullptr;
michael@0 2338 }
michael@0 2339 if (mJournalFileOpener) {
michael@0 2340 mJournalFileOpener->Cancel();
michael@0 2341 mJournalFileOpener = nullptr;
michael@0 2342 }
michael@0 2343 if (mTmpFileOpener) {
michael@0 2344 mTmpFileOpener->Cancel();
michael@0 2345 mTmpFileOpener = nullptr;
michael@0 2346 }
michael@0 2347
michael@0 2348 mIndexHandle = nullptr;
michael@0 2349 mJournalHandle = nullptr;
michael@0 2350 mRWHash = nullptr;
michael@0 2351 ReleaseBuffer();
michael@0 2352
michael@0 2353 if (mState == SHUTDOWN) {
michael@0 2354 return;
michael@0 2355 }
michael@0 2356
michael@0 2357 if (!mIndexOnDiskIsValid) {
michael@0 2358 MOZ_ASSERT(mTmpJournal.Count() == 0);
michael@0 2359 EnsureNoFreshEntry();
michael@0 2360 ProcessPendingOperations();
michael@0 2361 // Remove all entries that we haven't seen during this session
michael@0 2362 mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
michael@0 2363 StartUpdatingIndex(true);
michael@0 2364 return;
michael@0 2365 }
michael@0 2366
michael@0 2367 if (!mJournalReadSuccessfully) {
michael@0 2368 mTmpJournal.Clear();
michael@0 2369 EnsureNoFreshEntry();
michael@0 2370 ProcessPendingOperations();
michael@0 2371 StartUpdatingIndex(false);
michael@0 2372 return;
michael@0 2373 }
michael@0 2374
michael@0 2375 MergeJournal();
michael@0 2376 EnsureNoFreshEntry();
michael@0 2377 ProcessPendingOperations();
michael@0 2378 mIndexStats.Log();
michael@0 2379
michael@0 2380 ChangeState(READY);
michael@0 2381 mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
michael@0 2382 }
michael@0 2383
michael@0 2384 // static
michael@0 2385 void
michael@0 2386 CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure)
michael@0 2387 {
michael@0 2388 LOG(("CacheIndex::DelayedUpdate()"));
michael@0 2389
michael@0 2390 nsresult rv;
michael@0 2391 nsRefPtr<CacheIndex> index = gInstance;
michael@0 2392
michael@0 2393 if (!index) {
michael@0 2394 return;
michael@0 2395 }
michael@0 2396
michael@0 2397 CacheIndexAutoLock lock(index);
michael@0 2398
michael@0 2399 index->mUpdateTimer = nullptr;
michael@0 2400
michael@0 2401 if (!index->IsIndexUsable()) {
michael@0 2402 return;
michael@0 2403 }
michael@0 2404
michael@0 2405 if (index->mState == READY && index->mShuttingDown) {
michael@0 2406 return;
michael@0 2407 }
michael@0 2408
michael@0 2409 // mUpdateEventPending must be false here since StartUpdatingIndex() won't
michael@0 2410 // schedule timer if it is true.
michael@0 2411 MOZ_ASSERT(!index->mUpdateEventPending);
michael@0 2412 if (index->mState != BUILDING && index->mState != UPDATING) {
michael@0 2413 LOG(("CacheIndex::DelayedUpdate() - Update was canceled"));
michael@0 2414 return;
michael@0 2415 }
michael@0 2416
michael@0 2417 // We need to redispatch to run with lower priority
michael@0 2418 nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
michael@0 2419 MOZ_ASSERT(ioThread);
michael@0 2420
michael@0 2421 index->mUpdateEventPending = true;
michael@0 2422 rv = ioThread->Dispatch(index, CacheIOThread::INDEX);
michael@0 2423 if (NS_FAILED(rv)) {
michael@0 2424 index->mUpdateEventPending = false;
michael@0 2425 NS_WARNING("CacheIndex::DelayedUpdate() - Can't dispatch event");
michael@0 2426 LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event" ));
michael@0 2427 index->FinishUpdate(false);
michael@0 2428 }
michael@0 2429 }
michael@0 2430
michael@0 2431 nsresult
michael@0 2432 CacheIndex::ScheduleUpdateTimer(uint32_t aDelay)
michael@0 2433 {
michael@0 2434 LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay));
michael@0 2435
michael@0 2436 MOZ_ASSERT(!mUpdateTimer);
michael@0 2437
michael@0 2438 nsresult rv;
michael@0 2439
michael@0 2440 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 2441 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2442
michael@0 2443 nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
michael@0 2444 MOZ_ASSERT(ioTarget);
michael@0 2445
michael@0 2446 rv = timer->SetTarget(ioTarget);
michael@0 2447 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2448
michael@0 2449 rv = timer->InitWithFuncCallback(CacheIndex::DelayedUpdate, nullptr,
michael@0 2450 aDelay, nsITimer::TYPE_ONE_SHOT);
michael@0 2451 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2452
michael@0 2453 mUpdateTimer.swap(timer);
michael@0 2454 return NS_OK;
michael@0 2455 }
michael@0 2456
michael@0 2457 nsresult
michael@0 2458 CacheIndex::SetupDirectoryEnumerator()
michael@0 2459 {
michael@0 2460 MOZ_ASSERT(!NS_IsMainThread());
michael@0 2461 MOZ_ASSERT(!mDirEnumerator);
michael@0 2462
michael@0 2463 nsresult rv;
michael@0 2464 nsCOMPtr<nsIFile> file;
michael@0 2465
michael@0 2466 rv = mCacheDirectory->Clone(getter_AddRefs(file));
michael@0 2467 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2468
michael@0 2469 rv = file->AppendNative(NS_LITERAL_CSTRING(kEntriesDir));
michael@0 2470 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2471
michael@0 2472 bool exists;
michael@0 2473 rv = file->Exists(&exists);
michael@0 2474 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2475
michael@0 2476 if (!exists) {
michael@0 2477 NS_WARNING("CacheIndex::SetupDirectoryEnumerator() - Entries directory "
michael@0 2478 "doesn't exist!");
michael@0 2479 LOG(("CacheIndex::SetupDirectoryEnumerator() - Entries directory doesn't "
michael@0 2480 "exist!" ));
michael@0 2481 return NS_ERROR_UNEXPECTED;
michael@0 2482 }
michael@0 2483
michael@0 2484 nsCOMPtr<nsISimpleEnumerator> enumerator;
michael@0 2485 rv = file->GetDirectoryEntries(getter_AddRefs(enumerator));
michael@0 2486 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2487
michael@0 2488 mDirEnumerator = do_QueryInterface(enumerator, &rv);
michael@0 2489 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2490
michael@0 2491 return NS_OK;
michael@0 2492 }
michael@0 2493
michael@0 2494 void
michael@0 2495 CacheIndex::InitEntryFromDiskData(CacheIndexEntry *aEntry,
michael@0 2496 CacheFileMetadata *aMetaData,
michael@0 2497 int64_t aFileSize)
michael@0 2498 {
michael@0 2499 aEntry->InitNew();
michael@0 2500 aEntry->MarkDirty();
michael@0 2501 aEntry->MarkFresh();
michael@0 2502 aEntry->Init(aMetaData->AppId(), aMetaData->IsAnonymous(),
michael@0 2503 aMetaData->IsInBrowser());
michael@0 2504
michael@0 2505 uint32_t expirationTime;
michael@0 2506 aMetaData->GetExpirationTime(&expirationTime);
michael@0 2507 aEntry->SetExpirationTime(expirationTime);
michael@0 2508
michael@0 2509 uint32_t frecency;
michael@0 2510 aMetaData->GetFrecency(&frecency);
michael@0 2511 aEntry->SetFrecency(frecency);
michael@0 2512
michael@0 2513 aEntry->SetFileSize(static_cast<uint32_t>(
michael@0 2514 std::min(static_cast<int64_t>(PR_UINT32_MAX),
michael@0 2515 (aFileSize + 0x3FF) >> 10)));
michael@0 2516 }
michael@0 2517
michael@0 2518 bool
michael@0 2519 CacheIndex::IsUpdatePending()
michael@0 2520 {
michael@0 2521 AssertOwnsLock();
michael@0 2522
michael@0 2523 if (mUpdateTimer || mUpdateEventPending) {
michael@0 2524 return true;
michael@0 2525 }
michael@0 2526
michael@0 2527 return false;
michael@0 2528 }
michael@0 2529
michael@0 2530 void
michael@0 2531 CacheIndex::BuildIndex()
michael@0 2532 {
michael@0 2533 LOG(("CacheIndex::BuildIndex()"));
michael@0 2534
michael@0 2535 AssertOwnsLock();
michael@0 2536
michael@0 2537 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 2538
michael@0 2539 nsresult rv;
michael@0 2540
michael@0 2541 if (!mDirEnumerator) {
michael@0 2542 {
michael@0 2543 // Do not do IO under the lock.
michael@0 2544 CacheIndexAutoUnlock unlock(this);
michael@0 2545 rv = SetupDirectoryEnumerator();
michael@0 2546 }
michael@0 2547 if (mState == SHUTDOWN) {
michael@0 2548 // The index was shut down while we released the lock. FinishUpdate() was
michael@0 2549 // already called from Shutdown(), so just simply return here.
michael@0 2550 return;
michael@0 2551 }
michael@0 2552
michael@0 2553 if (NS_FAILED(rv)) {
michael@0 2554 FinishUpdate(false);
michael@0 2555 return;
michael@0 2556 }
michael@0 2557 }
michael@0 2558
michael@0 2559 while (true) {
michael@0 2560 if (CacheIOThread::YieldAndRerun()) {
michael@0 2561 LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
michael@0 2562 mUpdateEventPending = true;
michael@0 2563 return;
michael@0 2564 }
michael@0 2565
michael@0 2566 nsCOMPtr<nsIFile> file;
michael@0 2567 {
michael@0 2568 // Do not do IO under the lock.
michael@0 2569 CacheIndexAutoUnlock unlock(this);
michael@0 2570 rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
michael@0 2571 }
michael@0 2572 if (mState == SHUTDOWN) {
michael@0 2573 return;
michael@0 2574 }
michael@0 2575 if (!file) {
michael@0 2576 FinishUpdate(NS_SUCCEEDED(rv));
michael@0 2577 return;
michael@0 2578 }
michael@0 2579
michael@0 2580 nsAutoCString leaf;
michael@0 2581 rv = file->GetNativeLeafName(leaf);
michael@0 2582 if (NS_FAILED(rv)) {
michael@0 2583 LOG(("CacheIndex::BuildIndex() - GetNativeLeafName() failed! Skipping "
michael@0 2584 "file."));
michael@0 2585 mDontMarkIndexClean = true;
michael@0 2586 continue;
michael@0 2587 }
michael@0 2588
michael@0 2589 SHA1Sum::Hash hash;
michael@0 2590 rv = CacheFileIOManager::StrToHash(leaf, &hash);
michael@0 2591 if (NS_FAILED(rv)) {
michael@0 2592 LOG(("CacheIndex::BuildIndex() - Filename is not a hash, removing file. "
michael@0 2593 "[name=%s]", leaf.get()));
michael@0 2594 file->Remove(false);
michael@0 2595 continue;
michael@0 2596 }
michael@0 2597
michael@0 2598 CacheIndexEntry *entry = mIndex.GetEntry(hash);
michael@0 2599 if (entry && entry->IsRemoved()) {
michael@0 2600 LOG(("CacheIndex::BuildIndex() - Found file that should not exist. "
michael@0 2601 "[name=%s]", leaf.get()));
michael@0 2602 entry->Log();
michael@0 2603 MOZ_ASSERT(entry->IsFresh());
michael@0 2604 entry = nullptr;
michael@0 2605 }
michael@0 2606
michael@0 2607 #ifdef DEBUG
michael@0 2608 nsRefPtr<CacheFileHandle> handle;
michael@0 2609 CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
michael@0 2610 getter_AddRefs(handle));
michael@0 2611 #endif
michael@0 2612
michael@0 2613 if (entry) {
michael@0 2614 // the entry is up to date
michael@0 2615 LOG(("CacheIndex::BuildIndex() - Skipping file because the entry is up to"
michael@0 2616 " date. [name=%s]", leaf.get()));
michael@0 2617 entry->Log();
michael@0 2618 MOZ_ASSERT(entry->IsFresh()); // The entry must be from this session
michael@0 2619 // there must be an active CacheFile if the entry is not initialized
michael@0 2620 MOZ_ASSERT(entry->IsInitialized() || handle);
michael@0 2621 continue;
michael@0 2622 }
michael@0 2623
michael@0 2624 MOZ_ASSERT(!handle);
michael@0 2625
michael@0 2626 nsRefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
michael@0 2627 int64_t size = 0;
michael@0 2628
michael@0 2629 {
michael@0 2630 // Do not do IO under the lock.
michael@0 2631 CacheIndexAutoUnlock unlock(this);
michael@0 2632 rv = meta->SyncReadMetadata(file);
michael@0 2633
michael@0 2634 if (NS_SUCCEEDED(rv)) {
michael@0 2635 rv = file->GetFileSize(&size);
michael@0 2636 if (NS_FAILED(rv)) {
michael@0 2637 LOG(("CacheIndex::BuildIndex() - Cannot get filesize of file that was"
michael@0 2638 " successfully parsed. [name=%s]", leaf.get()));
michael@0 2639 }
michael@0 2640 }
michael@0 2641 }
michael@0 2642 if (mState == SHUTDOWN) {
michael@0 2643 return;
michael@0 2644 }
michael@0 2645
michael@0 2646 // Nobody could add the entry while the lock was released since we modify
michael@0 2647 // the index only on IO thread and this loop is executed on IO thread too.
michael@0 2648 entry = mIndex.GetEntry(hash);
michael@0 2649 MOZ_ASSERT(!entry || entry->IsRemoved());
michael@0 2650
michael@0 2651 if (NS_FAILED(rv)) {
michael@0 2652 LOG(("CacheIndex::BuildIndex() - CacheFileMetadata::SyncReadMetadata() "
michael@0 2653 "failed, removing file. [name=%s]", leaf.get()));
michael@0 2654 file->Remove(false);
michael@0 2655 } else {
michael@0 2656 CacheIndexEntryAutoManage entryMng(&hash, this);
michael@0 2657 entry = mIndex.PutEntry(hash);
michael@0 2658 InitEntryFromDiskData(entry, meta, size);
michael@0 2659 LOG(("CacheIndex::BuildIndex() - Added entry to index. [hash=%s]",
michael@0 2660 leaf.get()));
michael@0 2661 entry->Log();
michael@0 2662 }
michael@0 2663 }
michael@0 2664
michael@0 2665 NS_NOTREACHED("We should never get here");
michael@0 2666 }
michael@0 2667
michael@0 2668 bool
michael@0 2669 CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState)
michael@0 2670 {
michael@0 2671 // Start updating process when we are in or we are switching to READY state
michael@0 2672 // and index needs update, but not during shutdown or when removing all
michael@0 2673 // entries.
michael@0 2674 if ((mState == READY || aSwitchingToReadyState) && mIndexNeedsUpdate &&
michael@0 2675 !mShuttingDown && !mRemovingAll) {
michael@0 2676 LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process"));
michael@0 2677 mIndexNeedsUpdate = false;
michael@0 2678 StartUpdatingIndex(false);
michael@0 2679 return true;
michael@0 2680 }
michael@0 2681
michael@0 2682 return false;
michael@0 2683 }
michael@0 2684
michael@0 2685 void
michael@0 2686 CacheIndex::StartUpdatingIndex(bool aRebuild)
michael@0 2687 {
michael@0 2688 LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild));
michael@0 2689
michael@0 2690 AssertOwnsLock();
michael@0 2691
michael@0 2692 nsresult rv;
michael@0 2693
michael@0 2694 mIndexStats.Log();
michael@0 2695
michael@0 2696 ChangeState(aRebuild ? BUILDING : UPDATING);
michael@0 2697 mDontMarkIndexClean = false;
michael@0 2698
michael@0 2699 if (mShuttingDown || mRemovingAll) {
michael@0 2700 FinishUpdate(false);
michael@0 2701 return;
michael@0 2702 }
michael@0 2703
michael@0 2704 if (IsUpdatePending()) {
michael@0 2705 LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending"));
michael@0 2706 return;
michael@0 2707 }
michael@0 2708
michael@0 2709 uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
michael@0 2710 if (elapsed < kUpdateIndexStartDelay) {
michael@0 2711 LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
michael@0 2712 "scheduling timer to fire in %u ms.", elapsed,
michael@0 2713 kUpdateIndexStartDelay - elapsed));
michael@0 2714 rv = ScheduleUpdateTimer(kUpdateIndexStartDelay - elapsed);
michael@0 2715 if (NS_SUCCEEDED(rv)) {
michael@0 2716 return;
michael@0 2717 }
michael@0 2718
michael@0 2719 LOG(("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. "
michael@0 2720 "Starting update immediately."));
michael@0 2721 } else {
michael@0 2722 LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
michael@0 2723 "starting update now.", elapsed));
michael@0 2724 }
michael@0 2725
michael@0 2726 nsRefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
michael@0 2727 MOZ_ASSERT(ioThread);
michael@0 2728
michael@0 2729 // We need to dispatch an event even if we are on IO thread since we need to
michael@0 2730 // update the index with the correct priority.
michael@0 2731 mUpdateEventPending = true;
michael@0 2732 rv = ioThread->Dispatch(this, CacheIOThread::INDEX);
michael@0 2733 if (NS_FAILED(rv)) {
michael@0 2734 mUpdateEventPending = false;
michael@0 2735 NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event");
michael@0 2736 LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event" ));
michael@0 2737 FinishUpdate(false);
michael@0 2738 }
michael@0 2739 }
michael@0 2740
michael@0 2741 void
michael@0 2742 CacheIndex::UpdateIndex()
michael@0 2743 {
michael@0 2744 LOG(("CacheIndex::UpdateIndex()"));
michael@0 2745
michael@0 2746 AssertOwnsLock();
michael@0 2747
michael@0 2748 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 2749
michael@0 2750 nsresult rv;
michael@0 2751
michael@0 2752 if (!mDirEnumerator) {
michael@0 2753 {
michael@0 2754 // Do not do IO under the lock.
michael@0 2755 CacheIndexAutoUnlock unlock(this);
michael@0 2756 rv = SetupDirectoryEnumerator();
michael@0 2757 }
michael@0 2758 if (mState == SHUTDOWN) {
michael@0 2759 // The index was shut down while we released the lock. FinishUpdate() was
michael@0 2760 // already called from Shutdown(), so just simply return here.
michael@0 2761 return;
michael@0 2762 }
michael@0 2763
michael@0 2764 if (NS_FAILED(rv)) {
michael@0 2765 FinishUpdate(false);
michael@0 2766 return;
michael@0 2767 }
michael@0 2768 }
michael@0 2769
michael@0 2770 while (true) {
michael@0 2771 if (CacheIOThread::YieldAndRerun()) {
michael@0 2772 LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level "
michael@0 2773 "events."));
michael@0 2774 mUpdateEventPending = true;
michael@0 2775 return;
michael@0 2776 }
michael@0 2777
michael@0 2778 nsCOMPtr<nsIFile> file;
michael@0 2779 {
michael@0 2780 // Do not do IO under the lock.
michael@0 2781 CacheIndexAutoUnlock unlock(this);
michael@0 2782 rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
michael@0 2783 }
michael@0 2784 if (mState == SHUTDOWN) {
michael@0 2785 return;
michael@0 2786 }
michael@0 2787 if (!file) {
michael@0 2788 FinishUpdate(NS_SUCCEEDED(rv));
michael@0 2789 return;
michael@0 2790 }
michael@0 2791
michael@0 2792 nsAutoCString leaf;
michael@0 2793 rv = file->GetNativeLeafName(leaf);
michael@0 2794 if (NS_FAILED(rv)) {
michael@0 2795 LOG(("CacheIndex::UpdateIndex() - GetNativeLeafName() failed! Skipping "
michael@0 2796 "file."));
michael@0 2797 mDontMarkIndexClean = true;
michael@0 2798 continue;
michael@0 2799 }
michael@0 2800
michael@0 2801 SHA1Sum::Hash hash;
michael@0 2802 rv = CacheFileIOManager::StrToHash(leaf, &hash);
michael@0 2803 if (NS_FAILED(rv)) {
michael@0 2804 LOG(("CacheIndex::UpdateIndex() - Filename is not a hash, removing file. "
michael@0 2805 "[name=%s]", leaf.get()));
michael@0 2806 file->Remove(false);
michael@0 2807 continue;
michael@0 2808 }
michael@0 2809
michael@0 2810 CacheIndexEntry *entry = mIndex.GetEntry(hash);
michael@0 2811 if (entry && entry->IsRemoved()) {
michael@0 2812 if (entry->IsFresh()) {
michael@0 2813 LOG(("CacheIndex::UpdateIndex() - Found file that should not exist. "
michael@0 2814 "[name=%s]", leaf.get()));
michael@0 2815 entry->Log();
michael@0 2816 }
michael@0 2817 entry = nullptr;
michael@0 2818 }
michael@0 2819
michael@0 2820 #ifdef DEBUG
michael@0 2821 nsRefPtr<CacheFileHandle> handle;
michael@0 2822 CacheFileIOManager::gInstance->mHandles.GetHandle(&hash, false,
michael@0 2823 getter_AddRefs(handle));
michael@0 2824 #endif
michael@0 2825
michael@0 2826 if (entry && entry->IsFresh()) {
michael@0 2827 // the entry is up to date
michael@0 2828 LOG(("CacheIndex::UpdateIndex() - Skipping file because the entry is up "
michael@0 2829 " to date. [name=%s]", leaf.get()));
michael@0 2830 entry->Log();
michael@0 2831 // there must be an active CacheFile if the entry is not initialized
michael@0 2832 MOZ_ASSERT(entry->IsInitialized() || handle);
michael@0 2833 continue;
michael@0 2834 }
michael@0 2835
michael@0 2836 MOZ_ASSERT(!handle);
michael@0 2837
michael@0 2838 if (entry) {
michael@0 2839 PRTime lastModifiedTime;
michael@0 2840 {
michael@0 2841 // Do not do IO under the lock.
michael@0 2842 CacheIndexAutoUnlock unlock(this);
michael@0 2843 rv = file->GetLastModifiedTime(&lastModifiedTime);
michael@0 2844 }
michael@0 2845 if (mState == SHUTDOWN) {
michael@0 2846 return;
michael@0 2847 }
michael@0 2848 if (NS_FAILED(rv)) {
michael@0 2849 LOG(("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. "
michael@0 2850 "[name=%s]", leaf.get()));
michael@0 2851 // Assume the file is newer than index
michael@0 2852 } else {
michael@0 2853 if (mIndexTimeStamp > (lastModifiedTime / PR_MSEC_PER_SEC)) {
michael@0 2854 LOG(("CacheIndex::UpdateIndex() - Skipping file because of last "
michael@0 2855 "modified time. [name=%s, indexTimeStamp=%u, "
michael@0 2856 "lastModifiedTime=%u]", leaf.get(), mIndexTimeStamp,
michael@0 2857 lastModifiedTime / PR_MSEC_PER_SEC));
michael@0 2858
michael@0 2859 CacheIndexEntryAutoManage entryMng(&hash, this);
michael@0 2860 entry->MarkFresh();
michael@0 2861 continue;
michael@0 2862 }
michael@0 2863 }
michael@0 2864 }
michael@0 2865
michael@0 2866 nsRefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
michael@0 2867 int64_t size = 0;
michael@0 2868
michael@0 2869 {
michael@0 2870 // Do not do IO under the lock.
michael@0 2871 CacheIndexAutoUnlock unlock(this);
michael@0 2872 rv = meta->SyncReadMetadata(file);
michael@0 2873
michael@0 2874 if (NS_SUCCEEDED(rv)) {
michael@0 2875 rv = file->GetFileSize(&size);
michael@0 2876 if (NS_FAILED(rv)) {
michael@0 2877 LOG(("CacheIndex::UpdateIndex() - Cannot get filesize of file that "
michael@0 2878 "was successfully parsed. [name=%s]", leaf.get()));
michael@0 2879 }
michael@0 2880 }
michael@0 2881 }
michael@0 2882 if (mState == SHUTDOWN) {
michael@0 2883 return;
michael@0 2884 }
michael@0 2885
michael@0 2886 // Nobody could add the entry while the lock was released since we modify
michael@0 2887 // the index only on IO thread and this loop is executed on IO thread too.
michael@0 2888 entry = mIndex.GetEntry(hash);
michael@0 2889 MOZ_ASSERT(!entry || !entry->IsFresh());
michael@0 2890
michael@0 2891 CacheIndexEntryAutoManage entryMng(&hash, this);
michael@0 2892
michael@0 2893 if (NS_FAILED(rv)) {
michael@0 2894 LOG(("CacheIndex::UpdateIndex() - CacheFileMetadata::SyncReadMetadata() "
michael@0 2895 "failed, removing file. [name=%s]", leaf.get()));
michael@0 2896 file->Remove(false);
michael@0 2897 if (entry) {
michael@0 2898 entry->MarkRemoved();
michael@0 2899 entry->MarkFresh();
michael@0 2900 entry->MarkDirty();
michael@0 2901 }
michael@0 2902 } else {
michael@0 2903 entry = mIndex.PutEntry(hash);
michael@0 2904 InitEntryFromDiskData(entry, meta, size);
michael@0 2905 LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
michael@0 2906 "[hash=%s]", leaf.get()));
michael@0 2907 entry->Log();
michael@0 2908 }
michael@0 2909 }
michael@0 2910
michael@0 2911 NS_NOTREACHED("We should never get here");
michael@0 2912 }
michael@0 2913
michael@0 2914 void
michael@0 2915 CacheIndex::FinishUpdate(bool aSucceeded)
michael@0 2916 {
michael@0 2917 LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
michael@0 2918
michael@0 2919 MOZ_ASSERT(mState == UPDATING || mState == BUILDING ||
michael@0 2920 (!aSucceeded && mState == SHUTDOWN));
michael@0 2921
michael@0 2922 AssertOwnsLock();
michael@0 2923
michael@0 2924 if (mDirEnumerator) {
michael@0 2925 if (NS_IsMainThread()) {
michael@0 2926 LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
michael@0 2927 " Cannot safely release mDirEnumerator, leaking it!"));
michael@0 2928 NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
michael@0 2929 // This can happen only in case dispatching event to IO thread failed in
michael@0 2930 // CacheIndex::PreShutdown().
michael@0 2931 mDirEnumerator.forget(); // Leak it since dir enumerator is not threadsafe
michael@0 2932 } else {
michael@0 2933 mDirEnumerator->Close();
michael@0 2934 mDirEnumerator = nullptr;
michael@0 2935 }
michael@0 2936 }
michael@0 2937
michael@0 2938 if (!aSucceeded) {
michael@0 2939 mDontMarkIndexClean = true;
michael@0 2940 }
michael@0 2941
michael@0 2942 if (mState == SHUTDOWN) {
michael@0 2943 return;
michael@0 2944 }
michael@0 2945
michael@0 2946 if (mState == UPDATING && aSucceeded) {
michael@0 2947 // If we've iterated over all entries successfully then all entries that
michael@0 2948 // really exist on the disk are now marked as fresh. All non-fresh entries
michael@0 2949 // don't exist anymore and must be removed from the index.
michael@0 2950 mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
michael@0 2951 }
michael@0 2952
michael@0 2953 // Make sure we won't start update. If the build or update failed, there is no
michael@0 2954 // reason to believe that it will succeed next time.
michael@0 2955 mIndexNeedsUpdate = false;
michael@0 2956
michael@0 2957 ChangeState(READY);
michael@0 2958 mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
michael@0 2959 }
michael@0 2960
michael@0 2961 // static
michael@0 2962 PLDHashOperator
michael@0 2963 CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure)
michael@0 2964 {
michael@0 2965 if (aEntry->IsFresh()) {
michael@0 2966 return PL_DHASH_NEXT;
michael@0 2967 }
michael@0 2968
michael@0 2969 LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. "
michael@0 2970 "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry->Hash())));
michael@0 2971
michael@0 2972 CacheIndex *index = static_cast<CacheIndex *>(aClosure);
michael@0 2973
michael@0 2974 CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
michael@0 2975 emng.DoNotSearchInIndex();
michael@0 2976
michael@0 2977 return PL_DHASH_REMOVE;
michael@0 2978 }
michael@0 2979
michael@0 2980 #ifdef PR_LOGGING
michael@0 2981 // static
michael@0 2982 char const *
michael@0 2983 CacheIndex::StateString(EState aState)
michael@0 2984 {
michael@0 2985 switch (aState) {
michael@0 2986 case INITIAL: return "INITIAL";
michael@0 2987 case READING: return "READING";
michael@0 2988 case WRITING: return "WRITING";
michael@0 2989 case BUILDING: return "BUILDING";
michael@0 2990 case UPDATING: return "UPDATING";
michael@0 2991 case READY: return "READY";
michael@0 2992 case SHUTDOWN: return "SHUTDOWN";
michael@0 2993 }
michael@0 2994
michael@0 2995 MOZ_ASSERT(false, "Unexpected state!");
michael@0 2996 return "?";
michael@0 2997 }
michael@0 2998 #endif
michael@0 2999
michael@0 3000 void
michael@0 3001 CacheIndex::ChangeState(EState aNewState)
michael@0 3002 {
michael@0 3003 LOG(("CacheIndex::ChangeState() changing state %s -> %s", StateString(mState),
michael@0 3004 StateString(aNewState)));
michael@0 3005
michael@0 3006 // All pending updates should be processed before changing state
michael@0 3007 MOZ_ASSERT(mPendingUpdates.Count() == 0);
michael@0 3008
michael@0 3009 // PreShutdownInternal() should change the state to READY from every state. It
michael@0 3010 // may go through different states, but once we are in READY state the only
michael@0 3011 // possible transition is to SHUTDOWN state.
michael@0 3012 MOZ_ASSERT(!mShuttingDown || mState != READY || aNewState == SHUTDOWN);
michael@0 3013
michael@0 3014 // Start updating process when switching to READY state if needed
michael@0 3015 if (aNewState == READY && StartUpdatingIndexIfNeeded(true)) {
michael@0 3016 return;
michael@0 3017 }
michael@0 3018
michael@0 3019 // Try to evict entries over limit everytime we're leaving state READING,
michael@0 3020 // BUILDING or UPDATING, but not during shutdown or when removing all
michael@0 3021 // entries.
michael@0 3022 if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN &&
michael@0 3023 (mState == READING || mState == BUILDING || mState == UPDATING)) {
michael@0 3024 CacheFileIOManager::EvictIfOverLimit();
michael@0 3025 }
michael@0 3026
michael@0 3027 mState = aNewState;
michael@0 3028
michael@0 3029 if (mState != SHUTDOWN) {
michael@0 3030 CacheFileIOManager::CacheIndexStateChanged();
michael@0 3031 }
michael@0 3032
michael@0 3033 if (mState == READY && mDiskConsumptionObservers.Length()) {
michael@0 3034 for (uint32_t i = 0; i < mDiskConsumptionObservers.Length(); ++i) {
michael@0 3035 DiskConsumptionObserver* o = mDiskConsumptionObservers[i];
michael@0 3036 // Safe to call under the lock. We always post to the main thread.
michael@0 3037 o->OnDiskConsumption(mIndexStats.Size() << 10);
michael@0 3038 }
michael@0 3039
michael@0 3040 mDiskConsumptionObservers.Clear();
michael@0 3041 }
michael@0 3042 }
michael@0 3043
michael@0 3044 void
michael@0 3045 CacheIndex::AllocBuffer()
michael@0 3046 {
michael@0 3047 switch (mState) {
michael@0 3048 case WRITING:
michael@0 3049 mRWBufSize = sizeof(CacheIndexHeader) + sizeof(CacheHash::Hash32_t) +
michael@0 3050 mProcessEntries * sizeof(CacheIndexRecord);
michael@0 3051 if (mRWBufSize > kMaxBufSize) {
michael@0 3052 mRWBufSize = kMaxBufSize;
michael@0 3053 }
michael@0 3054 break;
michael@0 3055 case READING:
michael@0 3056 mRWBufSize = kMaxBufSize;
michael@0 3057 break;
michael@0 3058 default:
michael@0 3059 MOZ_ASSERT(false, "Unexpected state!");
michael@0 3060 }
michael@0 3061
michael@0 3062 mRWBuf = static_cast<char *>(moz_xmalloc(mRWBufSize));
michael@0 3063 }
michael@0 3064
michael@0 3065 void
michael@0 3066 CacheIndex::ReleaseBuffer()
michael@0 3067 {
michael@0 3068 if (!mRWBuf) {
michael@0 3069 return;
michael@0 3070 }
michael@0 3071
michael@0 3072 free(mRWBuf);
michael@0 3073 mRWBuf = nullptr;
michael@0 3074 mRWBufSize = 0;
michael@0 3075 mRWBufPos = 0;
michael@0 3076 }
michael@0 3077
michael@0 3078 namespace { // anon
michael@0 3079
michael@0 3080 class FrecencyComparator
michael@0 3081 {
michael@0 3082 public:
michael@0 3083 bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const {
michael@0 3084 return a->mFrecency == b->mFrecency;
michael@0 3085 }
michael@0 3086 bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const {
michael@0 3087 // Place entries with frecency 0 at the end of the array.
michael@0 3088 if (a->mFrecency == 0) {
michael@0 3089 return false;
michael@0 3090 }
michael@0 3091 if (b->mFrecency == 0) {
michael@0 3092 return true;
michael@0 3093 }
michael@0 3094 return a->mFrecency < b->mFrecency;
michael@0 3095 }
michael@0 3096 };
michael@0 3097
michael@0 3098 class ExpirationComparator
michael@0 3099 {
michael@0 3100 public:
michael@0 3101 bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const {
michael@0 3102 return a->mExpirationTime == b->mExpirationTime;
michael@0 3103 }
michael@0 3104 bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const {
michael@0 3105 return a->mExpirationTime < b->mExpirationTime;
michael@0 3106 }
michael@0 3107 };
michael@0 3108
michael@0 3109 } // anon
michael@0 3110
michael@0 3111 void
michael@0 3112 CacheIndex::InsertRecordToFrecencyArray(CacheIndexRecord *aRecord)
michael@0 3113 {
michael@0 3114 LOG(("CacheIndex::InsertRecordToFrecencyArray() [record=%p, hash=%08x%08x%08x"
michael@0 3115 "%08x%08x]", aRecord, LOGSHA1(aRecord->mHash)));
michael@0 3116
michael@0 3117 MOZ_ASSERT(!mFrecencyArray.Contains(aRecord));
michael@0 3118 mFrecencyArray.InsertElementSorted(aRecord, FrecencyComparator());
michael@0 3119 }
michael@0 3120
michael@0 3121 void
michael@0 3122 CacheIndex::InsertRecordToExpirationArray(CacheIndexRecord *aRecord)
michael@0 3123 {
michael@0 3124 LOG(("CacheIndex::InsertRecordToExpirationArray() [record=%p, hash=%08x%08x"
michael@0 3125 "%08x%08x%08x]", aRecord, LOGSHA1(aRecord->mHash)));
michael@0 3126
michael@0 3127 MOZ_ASSERT(!mExpirationArray.Contains(aRecord));
michael@0 3128 mExpirationArray.InsertElementSorted(aRecord, ExpirationComparator());
michael@0 3129 }
michael@0 3130
michael@0 3131 void
michael@0 3132 CacheIndex::RemoveRecordFromFrecencyArray(CacheIndexRecord *aRecord)
michael@0 3133 {
michael@0 3134 LOG(("CacheIndex::RemoveRecordFromFrecencyArray() [record=%p]", aRecord));
michael@0 3135
michael@0 3136 DebugOnly<bool> removed;
michael@0 3137 removed = mFrecencyArray.RemoveElement(aRecord);
michael@0 3138 MOZ_ASSERT(removed);
michael@0 3139 }
michael@0 3140
michael@0 3141 void
michael@0 3142 CacheIndex::RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord)
michael@0 3143 {
michael@0 3144 LOG(("CacheIndex::RemoveRecordFromExpirationArray() [record=%p]", aRecord));
michael@0 3145
michael@0 3146 DebugOnly<bool> removed;
michael@0 3147 removed = mExpirationArray.RemoveElement(aRecord);
michael@0 3148 MOZ_ASSERT(removed);
michael@0 3149 }
michael@0 3150
michael@0 3151 void
michael@0 3152 CacheIndex::AddRecordToIterators(CacheIndexRecord *aRecord)
michael@0 3153 {
michael@0 3154 AssertOwnsLock();
michael@0 3155
michael@0 3156 for (uint32_t i = 0; i < mIterators.Length(); ++i) {
michael@0 3157 // Add a new record only when iterator is supposed to be updated.
michael@0 3158 if (mIterators[i]->ShouldBeNewAdded()) {
michael@0 3159 mIterators[i]->AddRecord(aRecord);
michael@0 3160 }
michael@0 3161 }
michael@0 3162 }
michael@0 3163
michael@0 3164 void
michael@0 3165 CacheIndex::RemoveRecordFromIterators(CacheIndexRecord *aRecord)
michael@0 3166 {
michael@0 3167 AssertOwnsLock();
michael@0 3168
michael@0 3169 for (uint32_t i = 0; i < mIterators.Length(); ++i) {
michael@0 3170 // Remove the record from iterator always, it makes no sence to return
michael@0 3171 // non-existing entries. Also the pointer to the record is no longer valid
michael@0 3172 // once the entry is removed from index.
michael@0 3173 mIterators[i]->RemoveRecord(aRecord);
michael@0 3174 }
michael@0 3175 }
michael@0 3176
michael@0 3177 void
michael@0 3178 CacheIndex::ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
michael@0 3179 CacheIndexRecord *aNewRecord)
michael@0 3180 {
michael@0 3181 AssertOwnsLock();
michael@0 3182
michael@0 3183 for (uint32_t i = 0; i < mIterators.Length(); ++i) {
michael@0 3184 // We have to replace the record always since the pointer is no longer
michael@0 3185 // valid after this point. NOTE: Replacing the record doesn't mean that
michael@0 3186 // a new entry was added, it just means that the data in the entry was
michael@0 3187 // changed (e.g. a file size) and we had to track this change in
michael@0 3188 // mPendingUpdates since mIndex was read-only.
michael@0 3189 mIterators[i]->ReplaceRecord(aOldRecord, aNewRecord);
michael@0 3190 }
michael@0 3191 }
michael@0 3192
michael@0 3193 nsresult
michael@0 3194 CacheIndex::Run()
michael@0 3195 {
michael@0 3196 LOG(("CacheIndex::Run()"));
michael@0 3197
michael@0 3198 CacheIndexAutoLock lock(this);
michael@0 3199
michael@0 3200 if (!IsIndexUsable()) {
michael@0 3201 return NS_ERROR_NOT_AVAILABLE;
michael@0 3202 }
michael@0 3203
michael@0 3204 if (mState == READY && mShuttingDown) {
michael@0 3205 return NS_OK;
michael@0 3206 }
michael@0 3207
michael@0 3208 mUpdateEventPending = false;
michael@0 3209
michael@0 3210 switch (mState) {
michael@0 3211 case BUILDING:
michael@0 3212 BuildIndex();
michael@0 3213 break;
michael@0 3214 case UPDATING:
michael@0 3215 UpdateIndex();
michael@0 3216 break;
michael@0 3217 default:
michael@0 3218 LOG(("CacheIndex::Run() - Update/Build was canceled"));
michael@0 3219 }
michael@0 3220
michael@0 3221 return NS_OK;
michael@0 3222 }
michael@0 3223
michael@0 3224 nsresult
michael@0 3225 CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener,
michael@0 3226 CacheFileHandle *aHandle, nsresult aResult)
michael@0 3227 {
michael@0 3228 LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
michael@0 3229 "result=0x%08x]", aOpener, aHandle, aResult));
michael@0 3230
michael@0 3231 nsresult rv;
michael@0 3232
michael@0 3233 AssertOwnsLock();
michael@0 3234
michael@0 3235 if (!IsIndexUsable()) {
michael@0 3236 return NS_ERROR_NOT_AVAILABLE;
michael@0 3237 }
michael@0 3238
michael@0 3239 if (mState == READY && mShuttingDown) {
michael@0 3240 return NS_OK;
michael@0 3241 }
michael@0 3242
michael@0 3243 switch (mState) {
michael@0 3244 case WRITING:
michael@0 3245 MOZ_ASSERT(aOpener == mIndexFileOpener);
michael@0 3246 mIndexFileOpener = nullptr;
michael@0 3247
michael@0 3248 if (NS_FAILED(aResult)) {
michael@0 3249 LOG(("CacheIndex::OnFileOpenedInternal() - Can't open index file for "
michael@0 3250 "writing [rv=0x%08x]", aResult));
michael@0 3251 FinishWrite(false);
michael@0 3252 } else {
michael@0 3253 mIndexHandle = aHandle;
michael@0 3254 WriteRecords();
michael@0 3255 }
michael@0 3256 break;
michael@0 3257 case READING:
michael@0 3258 if (aOpener == mIndexFileOpener) {
michael@0 3259 mIndexFileOpener = nullptr;
michael@0 3260
michael@0 3261 if (NS_SUCCEEDED(aResult)) {
michael@0 3262 if (aHandle->FileSize() == 0) {
michael@0 3263 FinishRead(false);
michael@0 3264 CacheFileIOManager::DoomFile(aHandle, nullptr);
michael@0 3265 break;
michael@0 3266 } else {
michael@0 3267 mIndexHandle = aHandle;
michael@0 3268 }
michael@0 3269 } else {
michael@0 3270 FinishRead(false);
michael@0 3271 break;
michael@0 3272 }
michael@0 3273 } else if (aOpener == mJournalFileOpener) {
michael@0 3274 mJournalFileOpener = nullptr;
michael@0 3275 mJournalHandle = aHandle;
michael@0 3276 } else if (aOpener == mTmpFileOpener) {
michael@0 3277 mTmpFileOpener = nullptr;
michael@0 3278 mTmpHandle = aHandle;
michael@0 3279 } else {
michael@0 3280 MOZ_ASSERT(false, "Unexpected state!");
michael@0 3281 }
michael@0 3282
michael@0 3283 if (mIndexFileOpener || mJournalFileOpener || mTmpFileOpener) {
michael@0 3284 // Some opener still didn't finish
michael@0 3285 break;
michael@0 3286 }
michael@0 3287
michael@0 3288 // We fail and cancel all other openers when we opening index file fails.
michael@0 3289 MOZ_ASSERT(mIndexHandle);
michael@0 3290
michael@0 3291 if (mTmpHandle) {
michael@0 3292 CacheFileIOManager::DoomFile(mTmpHandle, nullptr);
michael@0 3293 mTmpHandle = nullptr;
michael@0 3294
michael@0 3295 if (mJournalHandle) { // this shouldn't normally happen
michael@0 3296 LOG(("CacheIndex::OnFileOpenedInternal() - Unexpected state, all "
michael@0 3297 "files [%s, %s, %s] should never exist. Removing whole index.",
michael@0 3298 kIndexName, kJournalName, kTempIndexName));
michael@0 3299 FinishRead(false);
michael@0 3300 break;
michael@0 3301 }
michael@0 3302 }
michael@0 3303
michael@0 3304 if (mJournalHandle) {
michael@0 3305 // Rename journal to make sure we update index on next start in case
michael@0 3306 // firefox crashes
michael@0 3307 rv = CacheFileIOManager::RenameFile(
michael@0 3308 mJournalHandle, NS_LITERAL_CSTRING(kTempIndexName), this);
michael@0 3309 if (NS_FAILED(rv)) {
michael@0 3310 LOG(("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::"
michael@0 3311 "RenameFile() failed synchronously [rv=0x%08x]", rv));
michael@0 3312 FinishRead(false);
michael@0 3313 break;
michael@0 3314 }
michael@0 3315 } else {
michael@0 3316 StartReadingIndex();
michael@0 3317 }
michael@0 3318
michael@0 3319 break;
michael@0 3320 default:
michael@0 3321 MOZ_ASSERT(false, "Unexpected state!");
michael@0 3322 }
michael@0 3323
michael@0 3324 return NS_OK;
michael@0 3325 }
michael@0 3326
michael@0 3327 nsresult
michael@0 3328 CacheIndex::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
michael@0 3329 {
michael@0 3330 MOZ_CRASH("CacheIndex::OnFileOpened should not be called!");
michael@0 3331 return NS_ERROR_UNEXPECTED;
michael@0 3332 }
michael@0 3333
michael@0 3334 nsresult
michael@0 3335 CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
michael@0 3336 nsresult aResult)
michael@0 3337 {
michael@0 3338 LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle,
michael@0 3339 aResult));
michael@0 3340
michael@0 3341 nsresult rv;
michael@0 3342
michael@0 3343 CacheIndexAutoLock lock(this);
michael@0 3344
michael@0 3345 if (!IsIndexUsable()) {
michael@0 3346 return NS_ERROR_NOT_AVAILABLE;
michael@0 3347 }
michael@0 3348
michael@0 3349 if (mState == READY && mShuttingDown) {
michael@0 3350 return NS_OK;
michael@0 3351 }
michael@0 3352
michael@0 3353 switch (mState) {
michael@0 3354 case WRITING:
michael@0 3355 if (mIndexHandle != aHandle) {
michael@0 3356 LOG(("CacheIndex::OnDataWritten() - ignoring notification since it "
michael@0 3357 "belongs to previously canceled operation [state=%d]", mState));
michael@0 3358 break;
michael@0 3359 }
michael@0 3360
michael@0 3361 if (NS_FAILED(aResult)) {
michael@0 3362 FinishWrite(false);
michael@0 3363 } else {
michael@0 3364 if (mSkipEntries == mProcessEntries) {
michael@0 3365 rv = CacheFileIOManager::RenameFile(mIndexHandle,
michael@0 3366 NS_LITERAL_CSTRING(kIndexName),
michael@0 3367 this);
michael@0 3368 if (NS_FAILED(rv)) {
michael@0 3369 LOG(("CacheIndex::OnDataWritten() - CacheFileIOManager::"
michael@0 3370 "RenameFile() failed synchronously [rv=0x%08x]", rv));
michael@0 3371 FinishWrite(false);
michael@0 3372 }
michael@0 3373 } else {
michael@0 3374 WriteRecords();
michael@0 3375 }
michael@0 3376 }
michael@0 3377 break;
michael@0 3378 default:
michael@0 3379 // Writing was canceled.
michael@0 3380 LOG(("CacheIndex::OnDataWritten() - ignoring notification since the "
michael@0 3381 "operation was previously canceled [state=%d]", mState));
michael@0 3382 }
michael@0 3383
michael@0 3384 return NS_OK;
michael@0 3385 }
michael@0 3386
michael@0 3387 nsresult
michael@0 3388 CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
michael@0 3389 {
michael@0 3390 LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08x]", aHandle,
michael@0 3391 aResult));
michael@0 3392
michael@0 3393 CacheIndexAutoLock lock(this);
michael@0 3394
michael@0 3395 if (!IsIndexUsable()) {
michael@0 3396 return NS_ERROR_NOT_AVAILABLE;
michael@0 3397 }
michael@0 3398
michael@0 3399 switch (mState) {
michael@0 3400 case READING:
michael@0 3401 MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle);
michael@0 3402
michael@0 3403 if (NS_FAILED(aResult)) {
michael@0 3404 FinishRead(false);
michael@0 3405 } else {
michael@0 3406 if (!mIndexOnDiskIsValid) {
michael@0 3407 ParseRecords();
michael@0 3408 } else {
michael@0 3409 ParseJournal();
michael@0 3410 }
michael@0 3411 }
michael@0 3412 break;
michael@0 3413 default:
michael@0 3414 // Reading was canceled.
michael@0 3415 LOG(("CacheIndex::OnDataRead() - ignoring notification since the "
michael@0 3416 "operation was previously canceled [state=%d]", mState));
michael@0 3417 }
michael@0 3418
michael@0 3419 return NS_OK;
michael@0 3420 }
michael@0 3421
michael@0 3422 nsresult
michael@0 3423 CacheIndex::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 3424 {
michael@0 3425 MOZ_CRASH("CacheIndex::OnFileDoomed should not be called!");
michael@0 3426 return NS_ERROR_UNEXPECTED;
michael@0 3427 }
michael@0 3428
michael@0 3429 nsresult
michael@0 3430 CacheIndex::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
michael@0 3431 {
michael@0 3432 MOZ_CRASH("CacheIndex::OnEOFSet should not be called!");
michael@0 3433 return NS_ERROR_UNEXPECTED;
michael@0 3434 }
michael@0 3435
michael@0 3436 nsresult
michael@0 3437 CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 3438 {
michael@0 3439 LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08x]", aHandle,
michael@0 3440 aResult));
michael@0 3441
michael@0 3442 CacheIndexAutoLock lock(this);
michael@0 3443
michael@0 3444 if (!IsIndexUsable()) {
michael@0 3445 return NS_ERROR_NOT_AVAILABLE;
michael@0 3446 }
michael@0 3447
michael@0 3448 if (mState == READY && mShuttingDown) {
michael@0 3449 return NS_OK;
michael@0 3450 }
michael@0 3451
michael@0 3452 switch (mState) {
michael@0 3453 case WRITING:
michael@0 3454 // This is a result of renaming the new index written to tmpfile to index
michael@0 3455 // file. This is the last step when writing the index and the whole
michael@0 3456 // writing process is successful iff renaming was successful.
michael@0 3457
michael@0 3458 if (mIndexHandle != aHandle) {
michael@0 3459 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
michael@0 3460 "belongs to previously canceled operation [state=%d]", mState));
michael@0 3461 break;
michael@0 3462 }
michael@0 3463
michael@0 3464 FinishWrite(NS_SUCCEEDED(aResult));
michael@0 3465 break;
michael@0 3466 case READING:
michael@0 3467 // This is a result of renaming journal file to tmpfile. It is renamed
michael@0 3468 // before we start reading index and journal file and it should normally
michael@0 3469 // succeed. If it fails give up reading of index.
michael@0 3470
michael@0 3471 if (mJournalHandle != aHandle) {
michael@0 3472 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
michael@0 3473 "belongs to previously canceled operation [state=%d]", mState));
michael@0 3474 break;
michael@0 3475 }
michael@0 3476
michael@0 3477 if (NS_FAILED(aResult)) {
michael@0 3478 FinishRead(false);
michael@0 3479 } else {
michael@0 3480 StartReadingIndex();
michael@0 3481 }
michael@0 3482 break;
michael@0 3483 default:
michael@0 3484 // Reading/writing was canceled.
michael@0 3485 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since the "
michael@0 3486 "operation was previously canceled [state=%d]", mState));
michael@0 3487 }
michael@0 3488
michael@0 3489 return NS_OK;
michael@0 3490 }
michael@0 3491
michael@0 3492 // Memory reporting
michael@0 3493
michael@0 3494 namespace { // anon
michael@0 3495
michael@0 3496 size_t
michael@0 3497 CollectIndexEntryMemory(CacheIndexEntry* aEntry,
michael@0 3498 mozilla::MallocSizeOf mallocSizeOf,
michael@0 3499 void *arg)
michael@0 3500 {
michael@0 3501 return aEntry->SizeOfExcludingThis(mallocSizeOf);
michael@0 3502 }
michael@0 3503
michael@0 3504 } // anon
michael@0 3505
michael@0 3506 size_t
michael@0 3507 CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 3508 {
michael@0 3509 CacheIndexAutoLock lock(const_cast<CacheIndex*>(this));
michael@0 3510
michael@0 3511 size_t n = 0;
michael@0 3512 nsCOMPtr<nsISizeOf> sizeOf;
michael@0 3513
michael@0 3514 // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
michael@0 3515 // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
michael@0 3516 // handles array.
michael@0 3517
michael@0 3518 sizeOf = do_QueryInterface(mCacheDirectory);
michael@0 3519 if (sizeOf) {
michael@0 3520 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3521 }
michael@0 3522
michael@0 3523 sizeOf = do_QueryInterface(mUpdateTimer);
michael@0 3524 if (sizeOf) {
michael@0 3525 n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
michael@0 3526 }
michael@0 3527
michael@0 3528 n += mallocSizeOf(mRWBuf);
michael@0 3529 n += mallocSizeOf(mRWHash);
michael@0 3530
michael@0 3531 n += mIndex.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
michael@0 3532 n += mPendingUpdates.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
michael@0 3533 n += mTmpJournal.SizeOfExcludingThis(&CollectIndexEntryMemory, mallocSizeOf);
michael@0 3534
michael@0 3535 // mFrecencyArray and mExpirationArray items are reported by
michael@0 3536 // mIndex/mPendingUpdates
michael@0 3537 n += mFrecencyArray.SizeOfExcludingThis(mallocSizeOf);
michael@0 3538 n += mExpirationArray.SizeOfExcludingThis(mallocSizeOf);
michael@0 3539 n += mDiskConsumptionObservers.SizeOfExcludingThis(mallocSizeOf);
michael@0 3540
michael@0 3541 return n;
michael@0 3542 }
michael@0 3543
michael@0 3544 // static
michael@0 3545 size_t
michael@0 3546 CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 3547 {
michael@0 3548 if (!gInstance)
michael@0 3549 return 0;
michael@0 3550
michael@0 3551 return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
michael@0 3552 }
michael@0 3553
michael@0 3554 // static
michael@0 3555 size_t
michael@0 3556 CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 3557 {
michael@0 3558 return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
michael@0 3559 }
michael@0 3560
michael@0 3561 } // net
michael@0 3562 } // mozilla

mercurial