netwerk/cache2/CacheIndex.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial