netwerk/cache2/CacheIndex.h

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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 #ifndef CacheIndex__h__
michael@0 6 #define CacheIndex__h__
michael@0 7
michael@0 8 #include "CacheLog.h"
michael@0 9 #include "CacheFileIOManager.h"
michael@0 10 #include "nsIRunnable.h"
michael@0 11 #include "CacheHashUtils.h"
michael@0 12 #include "nsICacheStorageService.h"
michael@0 13 #include "nsICacheEntry.h"
michael@0 14 #include "nsILoadContextInfo.h"
michael@0 15 #include "nsTHashtable.h"
michael@0 16 #include "nsThreadUtils.h"
michael@0 17 #include "nsWeakReference.h"
michael@0 18 #include "mozilla/SHA1.h"
michael@0 19 #include "mozilla/Mutex.h"
michael@0 20 #include "mozilla/Endian.h"
michael@0 21 #include "mozilla/TimeStamp.h"
michael@0 22
michael@0 23 class nsIFile;
michael@0 24 class nsIDirectoryEnumerator;
michael@0 25 class nsITimer;
michael@0 26
michael@0 27
michael@0 28 #ifdef DEBUG
michael@0 29 #define DEBUG_STATS 1
michael@0 30 #endif
michael@0 31
michael@0 32 namespace mozilla {
michael@0 33 namespace net {
michael@0 34
michael@0 35 class CacheFileMetadata;
michael@0 36 class FileOpenHelper;
michael@0 37 class CacheIndexIterator;
michael@0 38
michael@0 39 typedef struct {
michael@0 40 // Version of the index. The index must be ignored and deleted when the file
michael@0 41 // on disk was written with a newer version.
michael@0 42 uint32_t mVersion;
michael@0 43
michael@0 44 // Timestamp of time when the last successful write of the index started.
michael@0 45 // During update process we use this timestamp for a quick validation of entry
michael@0 46 // files. If last modified time of the file is lower than this timestamp, we
michael@0 47 // skip parsing of such file since the information in index should be up to
michael@0 48 // date.
michael@0 49 uint32_t mTimeStamp;
michael@0 50
michael@0 51 // We set this flag as soon as possible after parsing index during startup
michael@0 52 // and clean it after we write journal to disk during shutdown. We ignore the
michael@0 53 // journal and start update process whenever this flag is set during index
michael@0 54 // parsing.
michael@0 55 uint32_t mIsDirty;
michael@0 56 } CacheIndexHeader;
michael@0 57
michael@0 58 struct CacheIndexRecord {
michael@0 59 SHA1Sum::Hash mHash;
michael@0 60 uint32_t mFrecency;
michael@0 61 uint32_t mExpirationTime;
michael@0 62 uint32_t mAppId;
michael@0 63
michael@0 64 /*
michael@0 65 * 1000 0000 0000 0000 0000 0000 0000 0000 : initialized
michael@0 66 * 0100 0000 0000 0000 0000 0000 0000 0000 : anonymous
michael@0 67 * 0010 0000 0000 0000 0000 0000 0000 0000 : inBrowser
michael@0 68 * 0001 0000 0000 0000 0000 0000 0000 0000 : removed
michael@0 69 * 0000 1000 0000 0000 0000 0000 0000 0000 : dirty
michael@0 70 * 0000 0100 0000 0000 0000 0000 0000 0000 : fresh
michael@0 71 * 0000 0011 0000 0000 0000 0000 0000 0000 : reserved
michael@0 72 * 0000 0000 1111 1111 1111 1111 1111 1111 : file size (in kB)
michael@0 73 */
michael@0 74 uint32_t mFlags;
michael@0 75
michael@0 76 CacheIndexRecord()
michael@0 77 : mFrecency(0)
michael@0 78 , mExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
michael@0 79 , mAppId(nsILoadContextInfo::NO_APP_ID)
michael@0 80 , mFlags(0)
michael@0 81 {}
michael@0 82 };
michael@0 83
michael@0 84 class CacheIndexEntry : public PLDHashEntryHdr
michael@0 85 {
michael@0 86 public:
michael@0 87 typedef const SHA1Sum::Hash& KeyType;
michael@0 88 typedef const SHA1Sum::Hash* KeyTypePointer;
michael@0 89
michael@0 90 CacheIndexEntry(KeyTypePointer aKey)
michael@0 91 {
michael@0 92 MOZ_COUNT_CTOR(CacheIndexEntry);
michael@0 93 mRec = new CacheIndexRecord();
michael@0 94 LOG(("CacheIndexEntry::CacheIndexEntry() - Created record [rec=%p]", mRec.get()));
michael@0 95 memcpy(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash));
michael@0 96 }
michael@0 97 CacheIndexEntry(const CacheIndexEntry& aOther)
michael@0 98 {
michael@0 99 NS_NOTREACHED("CacheIndexEntry copy constructor is forbidden!");
michael@0 100 }
michael@0 101 ~CacheIndexEntry()
michael@0 102 {
michael@0 103 MOZ_COUNT_DTOR(CacheIndexEntry);
michael@0 104 LOG(("CacheIndexEntry::~CacheIndexEntry() - Deleting record [rec=%p]",
michael@0 105 mRec.get()));
michael@0 106 }
michael@0 107
michael@0 108 // KeyEquals(): does this entry match this key?
michael@0 109 bool KeyEquals(KeyTypePointer aKey) const
michael@0 110 {
michael@0 111 return memcmp(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash)) == 0;
michael@0 112 }
michael@0 113
michael@0 114 // KeyToPointer(): Convert KeyType to KeyTypePointer
michael@0 115 static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
michael@0 116
michael@0 117 // HashKey(): calculate the hash number
michael@0 118 static PLDHashNumber HashKey(KeyTypePointer aKey)
michael@0 119 {
michael@0 120 return (reinterpret_cast<const uint32_t *>(aKey))[0];
michael@0 121 }
michael@0 122
michael@0 123 // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
michael@0 124 // to use the copy constructor?
michael@0 125 enum { ALLOW_MEMMOVE = true };
michael@0 126
michael@0 127 bool operator==(const CacheIndexEntry& aOther) const
michael@0 128 {
michael@0 129 return KeyEquals(&aOther.mRec->mHash);
michael@0 130 }
michael@0 131
michael@0 132 CacheIndexEntry& operator=(const CacheIndexEntry& aOther)
michael@0 133 {
michael@0 134 MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
michael@0 135 sizeof(SHA1Sum::Hash)) == 0);
michael@0 136 mRec->mFrecency = aOther.mRec->mFrecency;
michael@0 137 mRec->mExpirationTime = aOther.mRec->mExpirationTime;
michael@0 138 mRec->mAppId = aOther.mRec->mAppId;
michael@0 139 mRec->mFlags = aOther.mRec->mFlags;
michael@0 140 return *this;
michael@0 141 }
michael@0 142
michael@0 143 void InitNew()
michael@0 144 {
michael@0 145 mRec->mFrecency = 0;
michael@0 146 mRec->mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
michael@0 147 mRec->mAppId = nsILoadContextInfo::NO_APP_ID;
michael@0 148 mRec->mFlags = 0;
michael@0 149 }
michael@0 150
michael@0 151 void Init(uint32_t aAppId, bool aAnonymous, bool aInBrowser)
michael@0 152 {
michael@0 153 MOZ_ASSERT(mRec->mFrecency == 0);
michael@0 154 MOZ_ASSERT(mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME);
michael@0 155 MOZ_ASSERT(mRec->mAppId == nsILoadContextInfo::NO_APP_ID);
michael@0 156 // When we init the entry it must be fresh and may be dirty
michael@0 157 MOZ_ASSERT((mRec->mFlags & ~kDirtyMask) == kFreshMask);
michael@0 158
michael@0 159 mRec->mAppId = aAppId;
michael@0 160 mRec->mFlags |= kInitializedMask;
michael@0 161 if (aAnonymous) {
michael@0 162 mRec->mFlags |= kAnonymousMask;
michael@0 163 }
michael@0 164 if (aInBrowser) {
michael@0 165 mRec->mFlags |= kInBrowserMask;
michael@0 166 }
michael@0 167 }
michael@0 168
michael@0 169 const SHA1Sum::Hash * Hash() { return &mRec->mHash; }
michael@0 170
michael@0 171 bool IsInitialized() { return !!(mRec->mFlags & kInitializedMask); }
michael@0 172
michael@0 173 uint32_t AppId() { return mRec->mAppId; }
michael@0 174 bool Anonymous() { return !!(mRec->mFlags & kAnonymousMask); }
michael@0 175 bool InBrowser() { return !!(mRec->mFlags & kInBrowserMask); }
michael@0 176
michael@0 177 bool IsRemoved() { return !!(mRec->mFlags & kRemovedMask); }
michael@0 178 void MarkRemoved() { mRec->mFlags |= kRemovedMask; }
michael@0 179
michael@0 180 bool IsDirty() { return !!(mRec->mFlags & kDirtyMask); }
michael@0 181 void MarkDirty() { mRec->mFlags |= kDirtyMask; }
michael@0 182 void ClearDirty() { mRec->mFlags &= ~kDirtyMask; }
michael@0 183
michael@0 184 bool IsFresh() { return !!(mRec->mFlags & kFreshMask); }
michael@0 185 void MarkFresh() { mRec->mFlags |= kFreshMask; }
michael@0 186
michael@0 187 void SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
michael@0 188 uint32_t GetFrecency() { return mRec->mFrecency; }
michael@0 189
michael@0 190 void SetExpirationTime(uint32_t aExpirationTime)
michael@0 191 {
michael@0 192 mRec->mExpirationTime = aExpirationTime;
michael@0 193 }
michael@0 194 uint32_t GetExpirationTime() { return mRec->mExpirationTime; }
michael@0 195
michael@0 196 // Sets filesize in kilobytes.
michael@0 197 void SetFileSize(uint32_t aFileSize)
michael@0 198 {
michael@0 199 if (aFileSize > kFileSizeMask) {
michael@0 200 LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
michael@0 201 "truncating to %u", kFileSizeMask));
michael@0 202 aFileSize = kFileSizeMask;
michael@0 203 }
michael@0 204 mRec->mFlags &= ~kFileSizeMask;
michael@0 205 mRec->mFlags |= aFileSize;
michael@0 206 }
michael@0 207 // Returns filesize in kilobytes.
michael@0 208 uint32_t GetFileSize() { return mRec->mFlags & kFileSizeMask; }
michael@0 209 bool IsFileEmpty() { return GetFileSize() == 0; }
michael@0 210
michael@0 211 void WriteToBuf(void *aBuf)
michael@0 212 {
michael@0 213 CacheIndexRecord *dst = reinterpret_cast<CacheIndexRecord *>(aBuf);
michael@0 214
michael@0 215 // Copy the whole record to the buffer.
michael@0 216 memcpy(aBuf, mRec, sizeof(CacheIndexRecord));
michael@0 217
michael@0 218 // Dirty and fresh flags should never go to disk, since they make sense only
michael@0 219 // during current session.
michael@0 220 dst->mFlags &= ~kDirtyMask;
michael@0 221 dst->mFlags &= ~kFreshMask;
michael@0 222
michael@0 223 #if defined(IS_LITTLE_ENDIAN)
michael@0 224 // Data in the buffer are in machine byte order and we want them in network
michael@0 225 // byte order.
michael@0 226 NetworkEndian::writeUint32(&dst->mFrecency, dst->mFrecency);
michael@0 227 NetworkEndian::writeUint32(&dst->mExpirationTime, dst->mExpirationTime);
michael@0 228 NetworkEndian::writeUint32(&dst->mAppId, dst->mAppId);
michael@0 229 NetworkEndian::writeUint32(&dst->mFlags, dst->mFlags);
michael@0 230 #endif
michael@0 231 }
michael@0 232
michael@0 233 void ReadFromBuf(void *aBuf)
michael@0 234 {
michael@0 235 CacheIndexRecord *src= reinterpret_cast<CacheIndexRecord *>(aBuf);
michael@0 236 MOZ_ASSERT(memcmp(&mRec->mHash, &src->mHash,
michael@0 237 sizeof(SHA1Sum::Hash)) == 0);
michael@0 238
michael@0 239 mRec->mFrecency = NetworkEndian::readUint32(&src->mFrecency);
michael@0 240 mRec->mExpirationTime = NetworkEndian::readUint32(&src->mExpirationTime);
michael@0 241 mRec->mAppId = NetworkEndian::readUint32(&src->mAppId);
michael@0 242 mRec->mFlags = NetworkEndian::readUint32(&src->mFlags);
michael@0 243 }
michael@0 244
michael@0 245 void Log() {
michael@0 246 LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
michael@0 247 " initialized=%u, removed=%u, dirty=%u, anonymous=%u, inBrowser=%u, "
michael@0 248 "appId=%u, frecency=%u, expirationTime=%u, size=%u]",
michael@0 249 this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
michael@0 250 IsDirty(), Anonymous(), InBrowser(), AppId(), GetFrecency(),
michael@0 251 GetExpirationTime(), GetFileSize()));
michael@0 252 }
michael@0 253
michael@0 254 static bool RecordMatchesLoadContextInfo(CacheIndexRecord *aRec,
michael@0 255 nsILoadContextInfo *aInfo)
michael@0 256 {
michael@0 257 if (!aInfo->IsPrivate() &&
michael@0 258 aInfo->AppId() == aRec->mAppId &&
michael@0 259 aInfo->IsAnonymous() == !!(aRec->mFlags & kAnonymousMask) &&
michael@0 260 aInfo->IsInBrowserElement() == !!(aRec->mFlags & kInBrowserMask)) {
michael@0 261 return true;
michael@0 262 }
michael@0 263
michael@0 264 return false;
michael@0 265 }
michael@0 266
michael@0 267 // Memory reporting
michael@0 268 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 269 {
michael@0 270 return mallocSizeOf(mRec.get());
michael@0 271 }
michael@0 272
michael@0 273 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 274 {
michael@0 275 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 276 }
michael@0 277
michael@0 278 private:
michael@0 279 friend class CacheIndex;
michael@0 280 friend class CacheIndexEntryAutoManage;
michael@0 281
michael@0 282 static const uint32_t kInitializedMask = 0x80000000;
michael@0 283 static const uint32_t kAnonymousMask = 0x40000000;
michael@0 284 static const uint32_t kInBrowserMask = 0x20000000;
michael@0 285
michael@0 286 // This flag is set when the entry was removed. We need to keep this
michael@0 287 // information in memory until we write the index file.
michael@0 288 static const uint32_t kRemovedMask = 0x10000000;
michael@0 289
michael@0 290 // This flag is set when the information in memory is not in sync with the
michael@0 291 // information in index file on disk.
michael@0 292 static const uint32_t kDirtyMask = 0x08000000;
michael@0 293
michael@0 294 // This flag is set when the information about the entry is fresh, i.e.
michael@0 295 // we've created or opened this entry during this session, or we've seen
michael@0 296 // this entry during update or build process.
michael@0 297 static const uint32_t kFreshMask = 0x04000000;
michael@0 298
michael@0 299 static const uint32_t kReservedMask = 0x03000000;
michael@0 300
michael@0 301 // FileSize in kilobytes
michael@0 302 static const uint32_t kFileSizeMask = 0x00FFFFFF;
michael@0 303
michael@0 304 nsAutoPtr<CacheIndexRecord> mRec;
michael@0 305 };
michael@0 306
michael@0 307 class CacheIndexStats
michael@0 308 {
michael@0 309 public:
michael@0 310 CacheIndexStats()
michael@0 311 : mCount(0)
michael@0 312 , mNotInitialized(0)
michael@0 313 , mRemoved(0)
michael@0 314 , mDirty(0)
michael@0 315 , mFresh(0)
michael@0 316 , mEmpty(0)
michael@0 317 , mSize(0)
michael@0 318 #ifdef DEBUG
michael@0 319 , mStateLogged(false)
michael@0 320 , mDisableLogging(false)
michael@0 321 #endif
michael@0 322 {
michael@0 323 }
michael@0 324
michael@0 325 bool operator==(const CacheIndexStats& aOther) const
michael@0 326 {
michael@0 327 return
michael@0 328 #ifdef DEBUG
michael@0 329 aOther.mStateLogged == mStateLogged &&
michael@0 330 #endif
michael@0 331 aOther.mCount == mCount &&
michael@0 332 aOther.mNotInitialized == mNotInitialized &&
michael@0 333 aOther.mRemoved == mRemoved &&
michael@0 334 aOther.mDirty == mDirty &&
michael@0 335 aOther.mFresh == mFresh &&
michael@0 336 aOther.mEmpty == mEmpty &&
michael@0 337 aOther.mSize == mSize;
michael@0 338 }
michael@0 339
michael@0 340 #ifdef DEBUG
michael@0 341 void DisableLogging() {
michael@0 342 mDisableLogging = true;
michael@0 343 }
michael@0 344 #endif
michael@0 345
michael@0 346 void Log() {
michael@0 347 LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
michael@0 348 "dirty=%u, fresh=%u, empty=%u, size=%u]", mCount, mNotInitialized,
michael@0 349 mRemoved, mDirty, mFresh, mEmpty, mSize));
michael@0 350 }
michael@0 351
michael@0 352 void Clear() {
michael@0 353 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Clear() - state logged!");
michael@0 354
michael@0 355 mCount = 0;
michael@0 356 mNotInitialized = 0;
michael@0 357 mRemoved = 0;
michael@0 358 mDirty = 0;
michael@0 359 mFresh = 0;
michael@0 360 mEmpty = 0;
michael@0 361 mSize = 0;
michael@0 362 }
michael@0 363
michael@0 364 #ifdef DEBUG
michael@0 365 bool StateLogged() {
michael@0 366 return mStateLogged;
michael@0 367 }
michael@0 368 #endif
michael@0 369
michael@0 370 uint32_t Count() {
michael@0 371 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Count() - state logged!");
michael@0 372 return mCount;
michael@0 373 }
michael@0 374
michael@0 375 uint32_t Dirty() {
michael@0 376 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Dirty() - state logged!");
michael@0 377 return mDirty;
michael@0 378 }
michael@0 379
michael@0 380 uint32_t Fresh() {
michael@0 381 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Fresh() - state logged!");
michael@0 382 return mFresh;
michael@0 383 }
michael@0 384
michael@0 385 uint32_t ActiveEntriesCount() {
michael@0 386 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::ActiveEntriesCount() - state "
michael@0 387 "logged!");
michael@0 388 return mCount - mRemoved - mNotInitialized - mEmpty;
michael@0 389 }
michael@0 390
michael@0 391 uint32_t Size() {
michael@0 392 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Size() - state logged!");
michael@0 393 return mSize;
michael@0 394 }
michael@0 395
michael@0 396 void BeforeChange(CacheIndexEntry *aEntry) {
michael@0 397 #ifdef DEBUG_STATS
michael@0 398 if (!mDisableLogging) {
michael@0 399 LOG(("CacheIndexStats::BeforeChange()"));
michael@0 400 Log();
michael@0 401 }
michael@0 402 #endif
michael@0 403
michael@0 404 MOZ_ASSERT(!mStateLogged, "CacheIndexStats::BeforeChange() - state "
michael@0 405 "logged!");
michael@0 406 #ifdef DEBUG
michael@0 407 mStateLogged = true;
michael@0 408 #endif
michael@0 409 if (aEntry) {
michael@0 410 MOZ_ASSERT(mCount);
michael@0 411 mCount--;
michael@0 412 if (aEntry->IsDirty()) {
michael@0 413 MOZ_ASSERT(mDirty);
michael@0 414 mDirty--;
michael@0 415 }
michael@0 416 if (aEntry->IsFresh()) {
michael@0 417 MOZ_ASSERT(mFresh);
michael@0 418 mFresh--;
michael@0 419 }
michael@0 420 if (aEntry->IsRemoved()) {
michael@0 421 MOZ_ASSERT(mRemoved);
michael@0 422 mRemoved--;
michael@0 423 } else {
michael@0 424 if (!aEntry->IsInitialized()) {
michael@0 425 MOZ_ASSERT(mNotInitialized);
michael@0 426 mNotInitialized--;
michael@0 427 } else {
michael@0 428 if (aEntry->IsFileEmpty()) {
michael@0 429 MOZ_ASSERT(mEmpty);
michael@0 430 mEmpty--;
michael@0 431 } else {
michael@0 432 MOZ_ASSERT(mSize >= aEntry->GetFileSize());
michael@0 433 mSize -= aEntry->GetFileSize();
michael@0 434 }
michael@0 435 }
michael@0 436 }
michael@0 437 }
michael@0 438 }
michael@0 439
michael@0 440 void AfterChange(CacheIndexEntry *aEntry) {
michael@0 441 MOZ_ASSERT(mStateLogged, "CacheIndexStats::AfterChange() - state not "
michael@0 442 "logged!");
michael@0 443 #ifdef DEBUG
michael@0 444 mStateLogged = false;
michael@0 445 #endif
michael@0 446 if (aEntry) {
michael@0 447 ++mCount;
michael@0 448 if (aEntry->IsDirty()) {
michael@0 449 mDirty++;
michael@0 450 }
michael@0 451 if (aEntry->IsFresh()) {
michael@0 452 mFresh++;
michael@0 453 }
michael@0 454 if (aEntry->IsRemoved()) {
michael@0 455 mRemoved++;
michael@0 456 } else {
michael@0 457 if (!aEntry->IsInitialized()) {
michael@0 458 mNotInitialized++;
michael@0 459 } else {
michael@0 460 if (aEntry->IsFileEmpty()) {
michael@0 461 mEmpty++;
michael@0 462 } else {
michael@0 463 mSize += aEntry->GetFileSize();
michael@0 464 }
michael@0 465 }
michael@0 466 }
michael@0 467 }
michael@0 468
michael@0 469 #ifdef DEBUG_STATS
michael@0 470 if (!mDisableLogging) {
michael@0 471 LOG(("CacheIndexStats::AfterChange()"));
michael@0 472 Log();
michael@0 473 }
michael@0 474 #endif
michael@0 475 }
michael@0 476
michael@0 477 private:
michael@0 478 uint32_t mCount;
michael@0 479 uint32_t mNotInitialized;
michael@0 480 uint32_t mRemoved;
michael@0 481 uint32_t mDirty;
michael@0 482 uint32_t mFresh;
michael@0 483 uint32_t mEmpty;
michael@0 484 uint32_t mSize;
michael@0 485 #ifdef DEBUG
michael@0 486 // We completely remove the data about an entry from the stats in
michael@0 487 // BeforeChange() and set this flag to true. The entry is then modified,
michael@0 488 // deleted or created and the data is again put into the stats and this flag
michael@0 489 // set to false. Statistics must not be read during this time since the
michael@0 490 // information is not correct.
michael@0 491 bool mStateLogged;
michael@0 492
michael@0 493 // Disables logging in this instance of CacheIndexStats
michael@0 494 bool mDisableLogging;
michael@0 495 #endif
michael@0 496 };
michael@0 497
michael@0 498 class CacheIndex : public CacheFileIOListener
michael@0 499 , public nsIRunnable
michael@0 500 {
michael@0 501 public:
michael@0 502 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 503 NS_DECL_NSIRUNNABLE
michael@0 504
michael@0 505 CacheIndex();
michael@0 506
michael@0 507 static nsresult Init(nsIFile *aCacheDirectory);
michael@0 508 static nsresult PreShutdown();
michael@0 509 static nsresult Shutdown();
michael@0 510
michael@0 511 // Following methods can be called only on IO thread.
michael@0 512
michael@0 513 // Add entry to the index. The entry shouldn't be present in index. This
michael@0 514 // method is called whenever a new handle for a new entry file is created. The
michael@0 515 // newly created entry is not initialized and it must be either initialized
michael@0 516 // with InitEntry() or removed with RemoveEntry().
michael@0 517 static nsresult AddEntry(const SHA1Sum::Hash *aHash);
michael@0 518
michael@0 519 // Inform index about an existing entry that should be present in index. This
michael@0 520 // method is called whenever a new handle for an existing entry file is
michael@0 521 // created. Like in case of AddEntry(), either InitEntry() or RemoveEntry()
michael@0 522 // must be called on the entry, since the entry is not initizlized if the
michael@0 523 // index is outdated.
michael@0 524 static nsresult EnsureEntryExists(const SHA1Sum::Hash *aHash);
michael@0 525
michael@0 526 // Initialize the entry. It MUST be present in index. Call to AddEntry() or
michael@0 527 // EnsureEntryExists() must precede the call to this method.
michael@0 528 static nsresult InitEntry(const SHA1Sum::Hash *aHash,
michael@0 529 uint32_t aAppId,
michael@0 530 bool aAnonymous,
michael@0 531 bool aInBrowser);
michael@0 532
michael@0 533 // Remove entry from index. The entry should be present in index.
michael@0 534 static nsresult RemoveEntry(const SHA1Sum::Hash *aHash);
michael@0 535
michael@0 536 // Update some information in entry. The entry MUST be present in index and
michael@0 537 // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
michael@0 538 // InitEntry() must precede the call to this method.
michael@0 539 // Pass nullptr if the value didn't change.
michael@0 540 static nsresult UpdateEntry(const SHA1Sum::Hash *aHash,
michael@0 541 const uint32_t *aFrecency,
michael@0 542 const uint32_t *aExpirationTime,
michael@0 543 const uint32_t *aSize);
michael@0 544
michael@0 545 // Remove all entries from the index. Called when clearing the whole cache.
michael@0 546 static nsresult RemoveAll();
michael@0 547
michael@0 548 enum EntryStatus {
michael@0 549 EXISTS = 0,
michael@0 550 DOES_NOT_EXIST = 1,
michael@0 551 DO_NOT_KNOW = 2
michael@0 552 };
michael@0 553
michael@0 554 // Returns status of the entry in index for the given key. It can be called
michael@0 555 // on any thread.
michael@0 556 static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval);
michael@0 557
michael@0 558 // Returns a hash of the least important entry that should be evicted if the
michael@0 559 // cache size is over limit and also returns a total number of all entries in
michael@0 560 // the index.
michael@0 561 static nsresult GetEntryForEviction(SHA1Sum::Hash *aHash, uint32_t *aCnt);
michael@0 562
michael@0 563 // Returns cache size in kB.
michael@0 564 static nsresult GetCacheSize(uint32_t *_retval);
michael@0 565
michael@0 566 // Asynchronously gets the disk cache size, used for display in the UI.
michael@0 567 static nsresult AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver);
michael@0 568
michael@0 569 // Returns an iterator that returns entries matching a given context that were
michael@0 570 // present in the index at the time this method was called. If aAddNew is true
michael@0 571 // then the iterator will also return entries created after this call.
michael@0 572 // NOTE: When some entry is removed from index it is removed also from the
michael@0 573 // iterator regardless what aAddNew was passed.
michael@0 574 static nsresult GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
michael@0 575 CacheIndexIterator **_retval);
michael@0 576
michael@0 577 // Returns true if we _think_ that the index is up to date. I.e. the state is
michael@0 578 // READY or WRITING and mIndexNeedsUpdate as well as mShuttingDown is false.
michael@0 579 static nsresult IsUpToDate(bool *_retval);
michael@0 580
michael@0 581 // Memory reporting
michael@0 582 static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
michael@0 583 static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
michael@0 584
michael@0 585 private:
michael@0 586 friend class CacheIndexEntryAutoManage;
michael@0 587 friend class CacheIndexAutoLock;
michael@0 588 friend class CacheIndexAutoUnlock;
michael@0 589 friend class FileOpenHelper;
michael@0 590 friend class CacheIndexIterator;
michael@0 591
michael@0 592 virtual ~CacheIndex();
michael@0 593
michael@0 594 NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
michael@0 595 nsresult OnFileOpenedInternal(FileOpenHelper *aOpener,
michael@0 596 CacheFileHandle *aHandle, nsresult aResult);
michael@0 597 NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
michael@0 598 nsresult aResult);
michael@0 599 NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
michael@0 600 NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
michael@0 601 NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
michael@0 602 NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult);
michael@0 603
michael@0 604 void Lock();
michael@0 605 void Unlock();
michael@0 606 void AssertOwnsLock();
michael@0 607
michael@0 608 nsresult InitInternal(nsIFile *aCacheDirectory);
michael@0 609 void PreShutdownInternal();
michael@0 610
michael@0 611 // This method returns false when index is not initialized or is shut down.
michael@0 612 bool IsIndexUsable();
michael@0 613
michael@0 614 // This method checks whether the entry has the same values of appId,
michael@0 615 // isAnonymous and isInBrowser. We don't expect to find a collision since
michael@0 616 // these values are part of the key that we hash and we use a strong hash
michael@0 617 // function.
michael@0 618 static bool IsCollision(CacheIndexEntry *aEntry,
michael@0 619 uint32_t aAppId,
michael@0 620 bool aAnonymous,
michael@0 621 bool aInBrowser);
michael@0 622
michael@0 623 // Checks whether any of the information about the entry has changed.
michael@0 624 static bool HasEntryChanged(CacheIndexEntry *aEntry,
michael@0 625 const uint32_t *aFrecency,
michael@0 626 const uint32_t *aExpirationTime,
michael@0 627 const uint32_t *aSize);
michael@0 628
michael@0 629 // Merge all pending operations from mPendingUpdates into mIndex.
michael@0 630 void ProcessPendingOperations();
michael@0 631 static PLDHashOperator UpdateEntryInIndex(CacheIndexEntry *aEntry,
michael@0 632 void* aClosure);
michael@0 633
michael@0 634 // Following methods perform writing of the index file.
michael@0 635 //
michael@0 636 // The index is written periodically, but not earlier than once in
michael@0 637 // kMinDumpInterval and there must be at least kMinUnwrittenChanges
michael@0 638 // differences between index on disk and in memory. Index is always first
michael@0 639 // written to a temporary file and the old index file is replaced when the
michael@0 640 // writing process succeeds.
michael@0 641 //
michael@0 642 // Starts writing of index when both limits (minimal delay between writes and
michael@0 643 // minimum number of changes in index) were exceeded.
michael@0 644 bool WriteIndexToDiskIfNeeded();
michael@0 645 // Starts writing of index file.
michael@0 646 void WriteIndexToDisk();
michael@0 647 // Serializes part of mIndex hashtable to the write buffer a writes the buffer
michael@0 648 // to the file.
michael@0 649 void WriteRecords();
michael@0 650 // Finalizes writing process.
michael@0 651 void FinishWrite(bool aSucceeded);
michael@0 652
michael@0 653 static PLDHashOperator CopyRecordsToRWBuf(CacheIndexEntry *aEntry,
michael@0 654 void* aClosure);
michael@0 655 static PLDHashOperator ApplyIndexChanges(CacheIndexEntry *aEntry,
michael@0 656 void* aClosure);
michael@0 657
michael@0 658 // Following methods perform writing of the journal during shutdown. All these
michael@0 659 // methods must be called only during shutdown since they write/delete files
michael@0 660 // directly on the main thread instead of using CacheFileIOManager that does
michael@0 661 // it asynchronously on IO thread. Journal contains only entries that are
michael@0 662 // dirty, i.e. changes that are not present in the index file on the disk.
michael@0 663 // When the log is written successfully, the dirty flag in index file is
michael@0 664 // cleared.
michael@0 665 nsresult GetFile(const nsACString &aName, nsIFile **_retval);
michael@0 666 nsresult RemoveFile(const nsACString &aName);
michael@0 667 void RemoveIndexFromDisk();
michael@0 668 // Writes journal to the disk and clears dirty flag in index header.
michael@0 669 nsresult WriteLogToDisk();
michael@0 670
michael@0 671 static PLDHashOperator WriteEntryToLog(CacheIndexEntry *aEntry,
michael@0 672 void* aClosure);
michael@0 673
michael@0 674 // Following methods perform reading of the index from the disk.
michael@0 675 //
michael@0 676 // Index is read at startup just after initializing the CacheIndex. There are
michael@0 677 // 3 files used when manipulating with index: index file, journal file and
michael@0 678 // a temporary file. All files contain the hash of the data, so we can check
michael@0 679 // whether the content is valid and complete. Index file contains also a dirty
michael@0 680 // flag in the index header which is unset on a clean shutdown. During opening
michael@0 681 // and reading of the files we determine the status of the whole index from
michael@0 682 // the states of the separate files. Following table shows all possible
michael@0 683 // combinations:
michael@0 684 //
michael@0 685 // index, journal, tmpfile
michael@0 686 // M * * - index is missing -> BUILD
michael@0 687 // I * * - index is invalid -> BUILD
michael@0 688 // D * * - index is dirty -> UPDATE
michael@0 689 // C M * - index is dirty -> UPDATE
michael@0 690 // C I * - unexpected state -> UPDATE
michael@0 691 // C V E - unexpected state -> UPDATE
michael@0 692 // C V M - index is up to date -> READY
michael@0 693 //
michael@0 694 // where the letters mean:
michael@0 695 // * - any state
michael@0 696 // E - file exists
michael@0 697 // M - file is missing
michael@0 698 // I - data is invalid (parsing failed or hash didn't match)
michael@0 699 // D - dirty (data in index file is correct, but dirty flag is set)
michael@0 700 // C - clean (index file is clean)
michael@0 701 // V - valid (data in journal file is correct)
michael@0 702 //
michael@0 703 // Note: We accept the data from journal only when the index is up to date as
michael@0 704 // a whole (i.e. C,V,M state).
michael@0 705 //
michael@0 706 // We rename the journal file to the temporary file as soon as possible after
michael@0 707 // initial test to ensure that we start update process on the next startup if
michael@0 708 // FF crashes during parsing of the index.
michael@0 709 //
michael@0 710 // Initiates reading index from disk.
michael@0 711 void ReadIndexFromDisk();
michael@0 712 // Starts reading data from index file.
michael@0 713 void StartReadingIndex();
michael@0 714 // Parses data read from index file.
michael@0 715 void ParseRecords();
michael@0 716 // Starts reading data from journal file.
michael@0 717 void StartReadingJournal();
michael@0 718 // Parses data read from journal file.
michael@0 719 void ParseJournal();
michael@0 720 // Merges entries from journal into mIndex.
michael@0 721 void MergeJournal();
michael@0 722 // In debug build this method checks that we have no fresh entry in mIndex
michael@0 723 // after we finish reading index and before we process pending operations.
michael@0 724 void EnsureNoFreshEntry();
michael@0 725 // In debug build this method is called after processing pending operations
michael@0 726 // to make sure mIndexStats contains correct information.
michael@0 727 void EnsureCorrectStats();
michael@0 728 static PLDHashOperator SumIndexStats(CacheIndexEntry *aEntry, void* aClosure);
michael@0 729 // Finalizes reading process.
michael@0 730 void FinishRead(bool aSucceeded);
michael@0 731
michael@0 732 static PLDHashOperator ProcessJournalEntry(CacheIndexEntry *aEntry,
michael@0 733 void* aClosure);
michael@0 734
michael@0 735 // Following methods perform updating and building of the index.
michael@0 736 // Timer callback that starts update or build process.
michael@0 737 static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
michael@0 738 // Posts timer event that start update or build process.
michael@0 739 nsresult ScheduleUpdateTimer(uint32_t aDelay);
michael@0 740 nsresult SetupDirectoryEnumerator();
michael@0 741 void InitEntryFromDiskData(CacheIndexEntry *aEntry,
michael@0 742 CacheFileMetadata *aMetaData,
michael@0 743 int64_t aFileSize);
michael@0 744 // Returns true when either a timer is scheduled or event is posted.
michael@0 745 bool IsUpdatePending();
michael@0 746 // Iterates through all files in entries directory that we didn't create/open
michael@0 747 // during this session, parses them and adds the entries to the index.
michael@0 748 void BuildIndex();
michael@0 749
michael@0 750 bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
michael@0 751 // Starts update or build process or fires a timer when it is too early after
michael@0 752 // startup.
michael@0 753 void StartUpdatingIndex(bool aRebuild);
michael@0 754 // Iterates through all files in entries directory that we didn't create/open
michael@0 755 // during this session and theirs last modified time is newer than timestamp
michael@0 756 // in the index header. Parses the files and adds the entries to the index.
michael@0 757 void UpdateIndex();
michael@0 758 // Finalizes update or build process.
michael@0 759 void FinishUpdate(bool aSucceeded);
michael@0 760
michael@0 761 static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
michael@0 762 void* aClosure);
michael@0 763
michael@0 764 enum EState {
michael@0 765 // Initial state in which the index is not usable
michael@0 766 // Possible transitions:
michael@0 767 // -> READING
michael@0 768 INITIAL = 0,
michael@0 769
michael@0 770 // Index is being read from the disk.
michael@0 771 // Possible transitions:
michael@0 772 // -> INITIAL - We failed to dispatch a read event.
michael@0 773 // -> BUILDING - No or corrupted index file was found.
michael@0 774 // -> UPDATING - No or corrupted journal file was found.
michael@0 775 // - Dirty flag was set in index header.
michael@0 776 // -> READY - Index was read successfully or was interrupted by
michael@0 777 // pre-shutdown.
michael@0 778 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
michael@0 779 READING = 1,
michael@0 780
michael@0 781 // Index is being written to the disk.
michael@0 782 // Possible transitions:
michael@0 783 // -> READY - Writing of index finished or was interrupted by
michael@0 784 // pre-shutdown..
michael@0 785 // -> UPDATING - Writing of index finished, but index was found outdated
michael@0 786 // during writing.
michael@0 787 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
michael@0 788 WRITING = 2,
michael@0 789
michael@0 790 // Index is being build.
michael@0 791 // Possible transitions:
michael@0 792 // -> READY - Building of index finished or was interrupted by
michael@0 793 // pre-shutdown.
michael@0 794 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
michael@0 795 BUILDING = 3,
michael@0 796
michael@0 797 // Index is being updated.
michael@0 798 // Possible transitions:
michael@0 799 // -> READY - Updating of index finished or was interrupted by
michael@0 800 // pre-shutdown.
michael@0 801 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
michael@0 802 UPDATING = 4,
michael@0 803
michael@0 804 // Index is ready.
michael@0 805 // Possible transitions:
michael@0 806 // -> UPDATING - Index was found outdated.
michael@0 807 // -> SHUTDOWN - Index is shutting down.
michael@0 808 READY = 5,
michael@0 809
michael@0 810 // Index is shutting down.
michael@0 811 SHUTDOWN = 6
michael@0 812 };
michael@0 813
michael@0 814 #ifdef PR_LOGGING
michael@0 815 static char const * StateString(EState aState);
michael@0 816 #endif
michael@0 817 void ChangeState(EState aNewState);
michael@0 818
michael@0 819 // Allocates and releases buffer used for reading and writing index.
michael@0 820 void AllocBuffer();
michael@0 821 void ReleaseBuffer();
michael@0 822
michael@0 823 // Methods used by CacheIndexEntryAutoManage to keep the arrays up to date.
michael@0 824 void InsertRecordToFrecencyArray(CacheIndexRecord *aRecord);
michael@0 825 void InsertRecordToExpirationArray(CacheIndexRecord *aRecord);
michael@0 826 void RemoveRecordFromFrecencyArray(CacheIndexRecord *aRecord);
michael@0 827 void RemoveRecordFromExpirationArray(CacheIndexRecord *aRecord);
michael@0 828
michael@0 829 // Methods used by CacheIndexEntryAutoManage to keep the iterators up to date.
michael@0 830 void AddRecordToIterators(CacheIndexRecord *aRecord);
michael@0 831 void RemoveRecordFromIterators(CacheIndexRecord *aRecord);
michael@0 832 void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
michael@0 833 CacheIndexRecord *aNewRecord);
michael@0 834
michael@0 835 // Memory reporting (private part)
michael@0 836 size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
michael@0 837
michael@0 838 static CacheIndex *gInstance;
michael@0 839
michael@0 840 nsCOMPtr<nsIFile> mCacheDirectory;
michael@0 841
michael@0 842 mozilla::Mutex mLock;
michael@0 843 EState mState;
michael@0 844 // Timestamp of time when the index was initialized. We use it to delay
michael@0 845 // initial update or build of index.
michael@0 846 TimeStamp mStartTime;
michael@0 847 // Set to true in PreShutdown(), it is checked on variaous places to prevent
michael@0 848 // starting any process (write, update, etc.) during shutdown.
michael@0 849 bool mShuttingDown;
michael@0 850 // When set to true, update process should start as soon as possible. This
michael@0 851 // flag is set whenever we find some inconsistency which would be fixed by
michael@0 852 // update process. The flag is checked always when switching to READY state.
michael@0 853 // To make sure we start the update process as soon as possible, methods that
michael@0 854 // set this flag should also call StartUpdatingIndexIfNeeded() to cover the
michael@0 855 // case when we are currently in READY state.
michael@0 856 bool mIndexNeedsUpdate;
michael@0 857 // Set at the beginning of RemoveAll() which clears the whole index. When
michael@0 858 // removing all entries we must stop any pending reading, writing, updating or
michael@0 859 // building operation. This flag is checked at various places and it prevents
michael@0 860 // we won't start another operation (e.g. canceling reading of the index would
michael@0 861 // normally start update or build process)
michael@0 862 bool mRemovingAll;
michael@0 863 // Whether the index file on disk exists and is valid.
michael@0 864 bool mIndexOnDiskIsValid;
michael@0 865 // When something goes wrong during updating or building process, we don't
michael@0 866 // mark index clean (and also don't write journal) to ensure that update or
michael@0 867 // build will be initiated on the next start.
michael@0 868 bool mDontMarkIndexClean;
michael@0 869 // Timestamp value from index file. It is used during update process to skip
michael@0 870 // entries that were last modified before this timestamp.
michael@0 871 uint32_t mIndexTimeStamp;
michael@0 872 // Timestamp of last time the index was dumped to disk.
michael@0 873 // NOTE: The index might not be necessarily dumped at this time. The value
michael@0 874 // is used to schedule next dump of the index.
michael@0 875 TimeStamp mLastDumpTime;
michael@0 876
michael@0 877 // Timer of delayed update/build.
michael@0 878 nsCOMPtr<nsITimer> mUpdateTimer;
michael@0 879 // True when build or update event is posted
michael@0 880 bool mUpdateEventPending;
michael@0 881
michael@0 882 // Helper members used when reading/writing index from/to disk.
michael@0 883 // Contains number of entries that should be skipped:
michael@0 884 // - in hashtable when writing index because they were already written
michael@0 885 // - in index file when reading index because they were already read
michael@0 886 uint32_t mSkipEntries;
michael@0 887 // Number of entries that should be written to disk. This is number of entries
michael@0 888 // in hashtable that are initialized and are not marked as removed when writing
michael@0 889 // begins.
michael@0 890 uint32_t mProcessEntries;
michael@0 891 char *mRWBuf;
michael@0 892 uint32_t mRWBufSize;
michael@0 893 uint32_t mRWBufPos;
michael@0 894 nsRefPtr<CacheHash> mRWHash;
michael@0 895
michael@0 896 // Reading of journal succeeded if true.
michael@0 897 bool mJournalReadSuccessfully;
michael@0 898
michael@0 899 // Handle used for writing and reading index file.
michael@0 900 nsRefPtr<CacheFileHandle> mIndexHandle;
michael@0 901 // Handle used for reading journal file.
michael@0 902 nsRefPtr<CacheFileHandle> mJournalHandle;
michael@0 903 // Used to check the existence of the file during reading process.
michael@0 904 nsRefPtr<CacheFileHandle> mTmpHandle;
michael@0 905
michael@0 906 nsRefPtr<FileOpenHelper> mIndexFileOpener;
michael@0 907 nsRefPtr<FileOpenHelper> mJournalFileOpener;
michael@0 908 nsRefPtr<FileOpenHelper> mTmpFileOpener;
michael@0 909
michael@0 910 // Directory enumerator used when building and updating index.
michael@0 911 nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
michael@0 912
michael@0 913 // Main index hashtable.
michael@0 914 nsTHashtable<CacheIndexEntry> mIndex;
michael@0 915
michael@0 916 // We cannot add, remove or change any entry in mIndex in states READING and
michael@0 917 // WRITING. We track all changes in mPendingUpdates during these states.
michael@0 918 nsTHashtable<CacheIndexEntry> mPendingUpdates;
michael@0 919
michael@0 920 // Contains information statistics for mIndex + mPendingUpdates.
michael@0 921 CacheIndexStats mIndexStats;
michael@0 922
michael@0 923 // When reading journal, we must first parse the whole file and apply the
michael@0 924 // changes iff the journal was read successfully. mTmpJournal is used to store
michael@0 925 // entries from the journal file. We throw away all these entries if parsing
michael@0 926 // of the journal fails or the hash does not match.
michael@0 927 nsTHashtable<CacheIndexEntry> mTmpJournal;
michael@0 928
michael@0 929 // Arrays that keep entry records ordered by eviction preference. When looking
michael@0 930 // for an entry to evict, we first try to find an expired entry. If there is
michael@0 931 // no expired entry, we take the entry with lowest valid frecency. Zero
michael@0 932 // frecency is an initial value and such entries are stored at the end of the
michael@0 933 // array. Uninitialized entries and entries marked as deleted are not present
michael@0 934 // in these arrays.
michael@0 935 nsTArray<CacheIndexRecord *> mFrecencyArray;
michael@0 936 nsTArray<CacheIndexRecord *> mExpirationArray;
michael@0 937
michael@0 938 nsTArray<CacheIndexIterator *> mIterators;
michael@0 939
michael@0 940 class DiskConsumptionObserver : public nsRunnable
michael@0 941 {
michael@0 942 public:
michael@0 943 static DiskConsumptionObserver* Init(nsICacheStorageConsumptionObserver* aObserver)
michael@0 944 {
michael@0 945 nsWeakPtr observer = do_GetWeakReference(aObserver);
michael@0 946 if (!observer)
michael@0 947 return nullptr;
michael@0 948
michael@0 949 return new DiskConsumptionObserver(observer);
michael@0 950 }
michael@0 951
michael@0 952 void OnDiskConsumption(int64_t aSize)
michael@0 953 {
michael@0 954 mSize = aSize;
michael@0 955 NS_DispatchToMainThread(this);
michael@0 956 }
michael@0 957
michael@0 958 private:
michael@0 959 DiskConsumptionObserver(nsWeakPtr const &aWeakObserver)
michael@0 960 : mObserver(aWeakObserver) { }
michael@0 961 virtual ~DiskConsumptionObserver() { }
michael@0 962
michael@0 963 NS_IMETHODIMP Run()
michael@0 964 {
michael@0 965 MOZ_ASSERT(NS_IsMainThread());
michael@0 966
michael@0 967 nsCOMPtr<nsICacheStorageConsumptionObserver> observer =
michael@0 968 do_QueryReferent(mObserver);
michael@0 969
michael@0 970 if (observer) {
michael@0 971 observer->OnNetworkCacheDiskConsumption(mSize);
michael@0 972 }
michael@0 973
michael@0 974 return NS_OK;
michael@0 975 }
michael@0 976
michael@0 977 nsWeakPtr mObserver;
michael@0 978 int64_t mSize;
michael@0 979 };
michael@0 980
michael@0 981 // List of async observers that want to get disk consumption information
michael@0 982 nsTArray<nsRefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
michael@0 983 };
michael@0 984
michael@0 985 class CacheIndexAutoLock {
michael@0 986 public:
michael@0 987 CacheIndexAutoLock(CacheIndex *aIndex)
michael@0 988 : mIndex(aIndex)
michael@0 989 , mLocked(true)
michael@0 990 {
michael@0 991 mIndex->Lock();
michael@0 992 }
michael@0 993 ~CacheIndexAutoLock()
michael@0 994 {
michael@0 995 if (mLocked) {
michael@0 996 mIndex->Unlock();
michael@0 997 }
michael@0 998 }
michael@0 999 void Lock()
michael@0 1000 {
michael@0 1001 MOZ_ASSERT(!mLocked);
michael@0 1002 mIndex->Lock();
michael@0 1003 mLocked = true;
michael@0 1004 }
michael@0 1005 void Unlock()
michael@0 1006 {
michael@0 1007 MOZ_ASSERT(mLocked);
michael@0 1008 mIndex->Unlock();
michael@0 1009 mLocked = false;
michael@0 1010 }
michael@0 1011
michael@0 1012 private:
michael@0 1013 nsRefPtr<CacheIndex> mIndex;
michael@0 1014 bool mLocked;
michael@0 1015 };
michael@0 1016
michael@0 1017 class CacheIndexAutoUnlock {
michael@0 1018 public:
michael@0 1019 CacheIndexAutoUnlock(CacheIndex *aIndex)
michael@0 1020 : mIndex(aIndex)
michael@0 1021 , mLocked(false)
michael@0 1022 {
michael@0 1023 mIndex->Unlock();
michael@0 1024 }
michael@0 1025 ~CacheIndexAutoUnlock()
michael@0 1026 {
michael@0 1027 if (!mLocked) {
michael@0 1028 mIndex->Lock();
michael@0 1029 }
michael@0 1030 }
michael@0 1031 void Lock()
michael@0 1032 {
michael@0 1033 MOZ_ASSERT(!mLocked);
michael@0 1034 mIndex->Lock();
michael@0 1035 mLocked = true;
michael@0 1036 }
michael@0 1037 void Unlock()
michael@0 1038 {
michael@0 1039 MOZ_ASSERT(mLocked);
michael@0 1040 mIndex->Unlock();
michael@0 1041 mLocked = false;
michael@0 1042 }
michael@0 1043
michael@0 1044 private:
michael@0 1045 nsRefPtr<CacheIndex> mIndex;
michael@0 1046 bool mLocked;
michael@0 1047 };
michael@0 1048
michael@0 1049 } // net
michael@0 1050 } // mozilla
michael@0 1051
michael@0 1052 #endif

mercurial