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__
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"
24 class nsIDirectoryEnumerator
;
35 class CacheFileMetadata
;
37 class CacheIndexIterator
;
40 // Version of the index. The index must be ignored and deleted when the file
41 // on disk was written with a newer version.
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
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
58 struct CacheIndexRecord
{
61 uint32_t mExpirationTime
;
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)
78 , mExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME
)
79 , mAppId(nsILoadContextInfo::NO_APP_ID
)
84 class CacheIndexEntry
: public PLDHashEntryHdr
87 typedef const SHA1Sum::Hash
& KeyType
;
88 typedef const SHA1Sum::Hash
* KeyTypePointer
;
90 explicit CacheIndexEntry(KeyTypePointer aKey
)
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
));
97 CacheIndexEntry(const CacheIndexEntry
& aOther
)
99 NS_NOTREACHED("CacheIndexEntry copy constructor is forbidden!");
103 MOZ_COUNT_DTOR(CacheIndexEntry
);
104 LOG(("CacheIndexEntry::~CacheIndexEntry() - Deleting record [rec=%p]",
108 // KeyEquals(): does this entry match this key?
109 bool KeyEquals(KeyTypePointer aKey
) const
111 return memcmp(&mRec
->mHash
, aKey
, sizeof(SHA1Sum::Hash
)) == 0;
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
)
120 return (reinterpret_cast<const uint32_t *>(aKey
))[0];
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
129 return KeyEquals(&aOther
.mRec
->mHash
);
132 CacheIndexEntry
& operator=(const CacheIndexEntry
& aOther
)
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
;
146 mRec
->mExpirationTime
= nsICacheEntry::NO_EXPIRATION_TIME
;
147 mRec
->mAppId
= nsILoadContextInfo::NO_APP_ID
;
151 void Init(uint32_t aAppId
, bool aAnonymous
, bool aInBrowser
)
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
;
162 mRec
->mFlags
|= kAnonymousMask
;
165 mRec
->mFlags
|= kInBrowserMask
;
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
)
192 mRec
->mExpirationTime
= aExpirationTime
;
194 uint32_t GetExpirationTime() { return mRec
->mExpirationTime
; }
196 // Sets filesize in kilobytes.
197 void SetFileSize(uint32_t aFileSize
)
199 if (aFileSize
> kFileSizeMask
) {
200 LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
201 "truncating to %u", kFileSizeMask
));
202 aFileSize
= kFileSizeMask
;
204 mRec
->mFlags
&= ~kFileSizeMask
;
205 mRec
->mFlags
|= aFileSize
;
207 // Returns filesize in kilobytes.
208 uint32_t GetFileSize() { return GetFileSize(mRec
); }
209 static uint32_t GetFileSize(CacheIndexRecord
*aRec
)
211 return aRec
->mFlags
& kFileSizeMask
;
213 bool IsFileEmpty() { return GetFileSize() == 0; }
215 void WriteToBuf(void *aBuf
)
217 CacheIndexRecord
*dst
= reinterpret_cast<CacheIndexRecord
*>(aBuf
);
219 // Copy the whole record to the buffer.
220 memcpy(aBuf
, mRec
, sizeof(CacheIndexRecord
));
222 // Dirty and fresh flags should never go to disk, since they make sense only
223 // during current session.
224 dst
->mFlags
&= ~kDirtyMask
;
225 dst
->mFlags
&= ~kFreshMask
;
227 #if defined(IS_LITTLE_ENDIAN)
228 // Data in the buffer are in machine byte order and we want them in network
230 NetworkEndian::writeUint32(&dst
->mFrecency
, dst
->mFrecency
);
231 NetworkEndian::writeUint32(&dst
->mExpirationTime
, dst
->mExpirationTime
);
232 NetworkEndian::writeUint32(&dst
->mAppId
, dst
->mAppId
);
233 NetworkEndian::writeUint32(&dst
->mFlags
, dst
->mFlags
);
237 void ReadFromBuf(void *aBuf
)
239 CacheIndexRecord
*src
= reinterpret_cast<CacheIndexRecord
*>(aBuf
);
240 MOZ_ASSERT(memcmp(&mRec
->mHash
, &src
->mHash
,
241 sizeof(SHA1Sum::Hash
)) == 0);
243 mRec
->mFrecency
= NetworkEndian::readUint32(&src
->mFrecency
);
244 mRec
->mExpirationTime
= NetworkEndian::readUint32(&src
->mExpirationTime
);
245 mRec
->mAppId
= NetworkEndian::readUint32(&src
->mAppId
);
246 mRec
->mFlags
= NetworkEndian::readUint32(&src
->mFlags
);
250 LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
251 " initialized=%u, removed=%u, dirty=%u, anonymous=%u, inBrowser=%u, "
252 "appId=%u, frecency=%u, expirationTime=%u, size=%u]",
253 this, LOGSHA1(mRec
->mHash
), IsFresh(), IsInitialized(), IsRemoved(),
254 IsDirty(), Anonymous(), InBrowser(), AppId(), GetFrecency(),
255 GetExpirationTime(), GetFileSize()));
258 static bool RecordMatchesLoadContextInfo(CacheIndexRecord
*aRec
,
259 nsILoadContextInfo
*aInfo
)
261 if (!aInfo
->IsPrivate() &&
262 aInfo
->AppId() == aRec
->mAppId
&&
263 aInfo
->IsAnonymous() == !!(aRec
->mFlags
& kAnonymousMask
) &&
264 aInfo
->IsInBrowserElement() == !!(aRec
->mFlags
& kInBrowserMask
)) {
272 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
274 return mallocSizeOf(mRec
.get());
277 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
279 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);
283 friend class CacheIndex
;
284 friend class CacheIndexEntryAutoManage
;
286 static const uint32_t kInitializedMask
= 0x80000000;
287 static const uint32_t kAnonymousMask
= 0x40000000;
288 static const uint32_t kInBrowserMask
= 0x20000000;
290 // This flag is set when the entry was removed. We need to keep this
291 // information in memory until we write the index file.
292 static const uint32_t kRemovedMask
= 0x10000000;
294 // This flag is set when the information in memory is not in sync with the
295 // information in index file on disk.
296 static const uint32_t kDirtyMask
= 0x08000000;
298 // This flag is set when the information about the entry is fresh, i.e.
299 // we've created or opened this entry during this session, or we've seen
300 // this entry during update or build process.
301 static const uint32_t kFreshMask
= 0x04000000;
303 static const uint32_t kReservedMask
= 0x03000000;
305 // FileSize in kilobytes
306 static const uint32_t kFileSizeMask
= 0x00FFFFFF;
308 nsAutoPtr
<CacheIndexRecord
> mRec
;
311 class CacheIndexStats
323 , mStateLogged(false)
324 , mDisableLogging(false)
329 bool operator==(const CacheIndexStats
& aOther
) const
333 aOther
.mStateLogged
== mStateLogged
&&
335 aOther
.mCount
== mCount
&&
336 aOther
.mNotInitialized
== mNotInitialized
&&
337 aOther
.mRemoved
== mRemoved
&&
338 aOther
.mDirty
== mDirty
&&
339 aOther
.mFresh
== mFresh
&&
340 aOther
.mEmpty
== mEmpty
&&
341 aOther
.mSize
== mSize
;
345 void DisableLogging() {
346 mDisableLogging
= true;
351 LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
352 "dirty=%u, fresh=%u, empty=%u, size=%u]", mCount
, mNotInitialized
,
353 mRemoved
, mDirty
, mFresh
, mEmpty
, mSize
));
357 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::Clear() - state logged!");
375 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::Count() - state logged!");
380 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::Dirty() - state logged!");
385 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::Fresh() - state logged!");
389 uint32_t ActiveEntriesCount() {
390 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::ActiveEntriesCount() - state "
392 return mCount
- mRemoved
- mNotInitialized
- mEmpty
;
396 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::Size() - state logged!");
400 void BeforeChange(CacheIndexEntry
*aEntry
) {
402 if (!mDisableLogging
) {
403 LOG(("CacheIndexStats::BeforeChange()"));
408 MOZ_ASSERT(!mStateLogged
, "CacheIndexStats::BeforeChange() - state "
416 if (aEntry
->IsDirty()) {
420 if (aEntry
->IsFresh()) {
424 if (aEntry
->IsRemoved()) {
425 MOZ_ASSERT(mRemoved
);
428 if (!aEntry
->IsInitialized()) {
429 MOZ_ASSERT(mNotInitialized
);
432 if (aEntry
->IsFileEmpty()) {
436 MOZ_ASSERT(mSize
>= aEntry
->GetFileSize());
437 mSize
-= aEntry
->GetFileSize();
444 void AfterChange(CacheIndexEntry
*aEntry
) {
445 MOZ_ASSERT(mStateLogged
, "CacheIndexStats::AfterChange() - state not "
448 mStateLogged
= false;
452 if (aEntry
->IsDirty()) {
455 if (aEntry
->IsFresh()) {
458 if (aEntry
->IsRemoved()) {
461 if (!aEntry
->IsInitialized()) {
464 if (aEntry
->IsFileEmpty()) {
467 mSize
+= aEntry
->GetFileSize();
474 if (!mDisableLogging
) {
475 LOG(("CacheIndexStats::AfterChange()"));
483 uint32_t mNotInitialized
;
490 // We completely remove the data about an entry from the stats in
491 // BeforeChange() and set this flag to true. The entry is then modified,
492 // deleted or created and the data is again put into the stats and this flag
493 // set to false. Statistics must not be read during this time since the
494 // information is not correct.
497 // Disables logging in this instance of CacheIndexStats
498 bool mDisableLogging
;
502 class CacheIndex
: public CacheFileIOListener
506 NS_DECL_THREADSAFE_ISUPPORTS
511 static nsresult
Init(nsIFile
*aCacheDirectory
);
512 static nsresult
PreShutdown();
513 static nsresult
Shutdown();
515 // Following methods can be called only on IO thread.
517 // Add entry to the index. The entry shouldn't be present in index. This
518 // method is called whenever a new handle for a new entry file is created. The
519 // newly created entry is not initialized and it must be either initialized
520 // with InitEntry() or removed with RemoveEntry().
521 static nsresult
AddEntry(const SHA1Sum::Hash
*aHash
);
523 // Inform index about an existing entry that should be present in index. This
524 // method is called whenever a new handle for an existing entry file is
525 // created. Like in case of AddEntry(), either InitEntry() or RemoveEntry()
526 // must be called on the entry, since the entry is not initizlized if the
527 // index is outdated.
528 static nsresult
EnsureEntryExists(const SHA1Sum::Hash
*aHash
);
530 // Initialize the entry. It MUST be present in index. Call to AddEntry() or
531 // EnsureEntryExists() must precede the call to this method.
532 static nsresult
InitEntry(const SHA1Sum::Hash
*aHash
,
537 // Remove entry from index. The entry should be present in index.
538 static nsresult
RemoveEntry(const SHA1Sum::Hash
*aHash
);
540 // Update some information in entry. The entry MUST be present in index and
541 // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
542 // InitEntry() must precede the call to this method.
543 // Pass nullptr if the value didn't change.
544 static nsresult
UpdateEntry(const SHA1Sum::Hash
*aHash
,
545 const uint32_t *aFrecency
,
546 const uint32_t *aExpirationTime
,
547 const uint32_t *aSize
);
549 // Remove all entries from the index. Called when clearing the whole cache.
550 static nsresult
RemoveAll();
558 // Returns status of the entry in index for the given key. It can be called
560 static nsresult
HasEntry(const nsACString
&aKey
, EntryStatus
*_retval
);
562 // Returns a hash of the least important entry that should be evicted if the
563 // cache size is over limit and also returns a total number of all entries in
564 // the index minus the number of forced valid entries that we encounter
565 // when searching (see below)
566 static nsresult
GetEntryForEviction(SHA1Sum::Hash
*aHash
, uint32_t *aCnt
);
568 // Checks if a cache entry is currently forced valid. Used to prevent an entry
569 // (that has been forced valid) from being evicted when the cache size reaches
571 static bool IsForcedValidEntry(const SHA1Sum::Hash
*aHash
);
573 // Returns cache size in kB.
574 static nsresult
GetCacheSize(uint32_t *_retval
);
576 // Synchronously returns the disk occupation and number of entries per-context.
577 // Callable on any thread.
578 static nsresult
GetCacheStats(nsILoadContextInfo
*aInfo
, uint32_t *aSize
, uint32_t *aCount
);
580 // Asynchronously gets the disk cache size, used for display in the UI.
581 static nsresult
AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver
* aObserver
);
583 // Returns an iterator that returns entries matching a given context that were
584 // present in the index at the time this method was called. If aAddNew is true
585 // then the iterator will also return entries created after this call.
586 // NOTE: When some entry is removed from index it is removed also from the
587 // iterator regardless what aAddNew was passed.
588 static nsresult
GetIterator(nsILoadContextInfo
*aInfo
, bool aAddNew
,
589 CacheIndexIterator
**_retval
);
591 // Returns true if we _think_ that the index is up to date. I.e. the state is
592 // READY or WRITING and mIndexNeedsUpdate as well as mShuttingDown is false.
593 static nsresult
IsUpToDate(bool *_retval
);
596 static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
);
597 static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
);
600 friend class CacheIndexEntryAutoManage
;
601 friend class CacheIndexAutoLock
;
602 friend class CacheIndexAutoUnlock
;
603 friend class FileOpenHelper
;
604 friend class CacheIndexIterator
;
606 virtual ~CacheIndex();
608 NS_IMETHOD
OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
);
609 nsresult
OnFileOpenedInternal(FileOpenHelper
*aOpener
,
610 CacheFileHandle
*aHandle
, nsresult aResult
);
611 NS_IMETHOD
OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
613 NS_IMETHOD
OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
, nsresult aResult
);
614 NS_IMETHOD
OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
);
615 NS_IMETHOD
OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
);
616 NS_IMETHOD
OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
);
620 void AssertOwnsLock();
622 nsresult
InitInternal(nsIFile
*aCacheDirectory
);
623 void PreShutdownInternal();
625 // This method returns false when index is not initialized or is shut down.
626 bool IsIndexUsable();
628 // This method checks whether the entry has the same values of appId,
629 // isAnonymous and isInBrowser. We don't expect to find a collision since
630 // these values are part of the key that we hash and we use a strong hash
632 static bool IsCollision(CacheIndexEntry
*aEntry
,
637 // Checks whether any of the information about the entry has changed.
638 static bool HasEntryChanged(CacheIndexEntry
*aEntry
,
639 const uint32_t *aFrecency
,
640 const uint32_t *aExpirationTime
,
641 const uint32_t *aSize
);
643 // Merge all pending operations from mPendingUpdates into mIndex.
644 void ProcessPendingOperations();
645 static PLDHashOperator
UpdateEntryInIndex(CacheIndexEntry
*aEntry
,
648 // Following methods perform writing of the index file.
650 // The index is written periodically, but not earlier than once in
651 // kMinDumpInterval and there must be at least kMinUnwrittenChanges
652 // differences between index on disk and in memory. Index is always first
653 // written to a temporary file and the old index file is replaced when the
654 // writing process succeeds.
656 // Starts writing of index when both limits (minimal delay between writes and
657 // minimum number of changes in index) were exceeded.
658 bool WriteIndexToDiskIfNeeded();
659 // Starts writing of index file.
660 void WriteIndexToDisk();
661 // Serializes part of mIndex hashtable to the write buffer a writes the buffer
664 // Finalizes writing process.
665 void FinishWrite(bool aSucceeded
);
667 static PLDHashOperator
CopyRecordsToRWBuf(CacheIndexEntry
*aEntry
,
669 static PLDHashOperator
ApplyIndexChanges(CacheIndexEntry
*aEntry
,
672 // Following methods perform writing of the journal during shutdown. All these
673 // methods must be called only during shutdown since they write/delete files
674 // directly on the main thread instead of using CacheFileIOManager that does
675 // it asynchronously on IO thread. Journal contains only entries that are
676 // dirty, i.e. changes that are not present in the index file on the disk.
677 // When the log is written successfully, the dirty flag in index file is
679 nsresult
GetFile(const nsACString
&aName
, nsIFile
**_retval
);
680 nsresult
RemoveFile(const nsACString
&aName
);
681 void RemoveIndexFromDisk();
682 // Writes journal to the disk and clears dirty flag in index header.
683 nsresult
WriteLogToDisk();
685 static PLDHashOperator
WriteEntryToLog(CacheIndexEntry
*aEntry
,
688 // Following methods perform reading of the index from the disk.
690 // Index is read at startup just after initializing the CacheIndex. There are
691 // 3 files used when manipulating with index: index file, journal file and
692 // a temporary file. All files contain the hash of the data, so we can check
693 // whether the content is valid and complete. Index file contains also a dirty
694 // flag in the index header which is unset on a clean shutdown. During opening
695 // and reading of the files we determine the status of the whole index from
696 // the states of the separate files. Following table shows all possible
699 // index, journal, tmpfile
700 // M * * - index is missing -> BUILD
701 // I * * - index is invalid -> BUILD
702 // D * * - index is dirty -> UPDATE
703 // C M * - index is dirty -> UPDATE
704 // C I * - unexpected state -> UPDATE
705 // C V E - unexpected state -> UPDATE
706 // C V M - index is up to date -> READY
708 // where the letters mean:
711 // M - file is missing
712 // I - data is invalid (parsing failed or hash didn't match)
713 // D - dirty (data in index file is correct, but dirty flag is set)
714 // C - clean (index file is clean)
715 // V - valid (data in journal file is correct)
717 // Note: We accept the data from journal only when the index is up to date as
718 // a whole (i.e. C,V,M state).
720 // We rename the journal file to the temporary file as soon as possible after
721 // initial test to ensure that we start update process on the next startup if
722 // FF crashes during parsing of the index.
724 // Initiates reading index from disk.
725 void ReadIndexFromDisk();
726 // Starts reading data from index file.
727 void StartReadingIndex();
728 // Parses data read from index file.
730 // Starts reading data from journal file.
731 void StartReadingJournal();
732 // Parses data read from journal file.
734 // Merges entries from journal into mIndex.
736 // In debug build this method checks that we have no fresh entry in mIndex
737 // after we finish reading index and before we process pending operations.
738 void EnsureNoFreshEntry();
739 // In debug build this method is called after processing pending operations
740 // to make sure mIndexStats contains correct information.
741 void EnsureCorrectStats();
742 static PLDHashOperator
SumIndexStats(CacheIndexEntry
*aEntry
, void* aClosure
);
743 // Finalizes reading process.
744 void FinishRead(bool aSucceeded
);
746 static PLDHashOperator
ProcessJournalEntry(CacheIndexEntry
*aEntry
,
749 // Following methods perform updating and building of the index.
750 // Timer callback that starts update or build process.
751 static void DelayedUpdate(nsITimer
*aTimer
, void *aClosure
);
752 // Posts timer event that start update or build process.
753 nsresult
ScheduleUpdateTimer(uint32_t aDelay
);
754 nsresult
SetupDirectoryEnumerator();
755 void InitEntryFromDiskData(CacheIndexEntry
*aEntry
,
756 CacheFileMetadata
*aMetaData
,
758 // Returns true when either a timer is scheduled or event is posted.
759 bool IsUpdatePending();
760 // Iterates through all files in entries directory that we didn't create/open
761 // during this session, parses them and adds the entries to the index.
764 bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState
= false);
765 // Starts update or build process or fires a timer when it is too early after
767 void StartUpdatingIndex(bool aRebuild
);
768 // Iterates through all files in entries directory that we didn't create/open
769 // during this session and theirs last modified time is newer than timestamp
770 // in the index header. Parses the files and adds the entries to the index.
772 // Finalizes update or build process.
773 void FinishUpdate(bool aSucceeded
);
775 static PLDHashOperator
RemoveNonFreshEntries(CacheIndexEntry
*aEntry
,
779 // Initial state in which the index is not usable
780 // Possible transitions:
784 // Index is being read from the disk.
785 // Possible transitions:
786 // -> INITIAL - We failed to dispatch a read event.
787 // -> BUILDING - No or corrupted index file was found.
788 // -> UPDATING - No or corrupted journal file was found.
789 // - Dirty flag was set in index header.
790 // -> READY - Index was read successfully or was interrupted by
792 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
795 // Index is being written to the disk.
796 // Possible transitions:
797 // -> READY - Writing of index finished or was interrupted by
799 // -> UPDATING - Writing of index finished, but index was found outdated
801 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
804 // Index is being build.
805 // Possible transitions:
806 // -> READY - Building of index finished or was interrupted by
808 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
811 // Index is being updated.
812 // Possible transitions:
813 // -> READY - Updating of index finished or was interrupted by
815 // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
819 // Possible transitions:
820 // -> UPDATING - Index was found outdated.
821 // -> SHUTDOWN - Index is shutting down.
824 // Index is shutting down.
829 static char const * StateString(EState aState
);
831 void ChangeState(EState aNewState
);
833 // Allocates and releases buffer used for reading and writing index.
835 void ReleaseBuffer();
837 // Methods used by CacheIndexEntryAutoManage to keep the arrays up to date.
838 void InsertRecordToFrecencyArray(CacheIndexRecord
*aRecord
);
839 void InsertRecordToExpirationArray(CacheIndexRecord
*aRecord
);
840 void RemoveRecordFromFrecencyArray(CacheIndexRecord
*aRecord
);
841 void RemoveRecordFromExpirationArray(CacheIndexRecord
*aRecord
);
843 // Methods used by CacheIndexEntryAutoManage to keep the iterators up to date.
844 void AddRecordToIterators(CacheIndexRecord
*aRecord
);
845 void RemoveRecordFromIterators(CacheIndexRecord
*aRecord
);
846 void ReplaceRecordInIterators(CacheIndexRecord
*aOldRecord
,
847 CacheIndexRecord
*aNewRecord
);
849 // Memory reporting (private part)
850 size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf
) const;
852 static CacheIndex
*gInstance
;
854 nsCOMPtr
<nsIFile
> mCacheDirectory
;
856 mozilla::Mutex mLock
;
858 // Timestamp of time when the index was initialized. We use it to delay
859 // initial update or build of index.
860 TimeStamp mStartTime
;
861 // Set to true in PreShutdown(), it is checked on variaous places to prevent
862 // starting any process (write, update, etc.) during shutdown.
864 // When set to true, update process should start as soon as possible. This
865 // flag is set whenever we find some inconsistency which would be fixed by
866 // update process. The flag is checked always when switching to READY state.
867 // To make sure we start the update process as soon as possible, methods that
868 // set this flag should also call StartUpdatingIndexIfNeeded() to cover the
869 // case when we are currently in READY state.
870 bool mIndexNeedsUpdate
;
871 // Set at the beginning of RemoveAll() which clears the whole index. When
872 // removing all entries we must stop any pending reading, writing, updating or
873 // building operation. This flag is checked at various places and it prevents
874 // we won't start another operation (e.g. canceling reading of the index would
875 // normally start update or build process)
877 // Whether the index file on disk exists and is valid.
878 bool mIndexOnDiskIsValid
;
879 // When something goes wrong during updating or building process, we don't
880 // mark index clean (and also don't write journal) to ensure that update or
881 // build will be initiated on the next start.
882 bool mDontMarkIndexClean
;
883 // Timestamp value from index file. It is used during update process to skip
884 // entries that were last modified before this timestamp.
885 uint32_t mIndexTimeStamp
;
886 // Timestamp of last time the index was dumped to disk.
887 // NOTE: The index might not be necessarily dumped at this time. The value
888 // is used to schedule next dump of the index.
889 TimeStamp mLastDumpTime
;
891 // Timer of delayed update/build.
892 nsCOMPtr
<nsITimer
> mUpdateTimer
;
893 // True when build or update event is posted
894 bool mUpdateEventPending
;
896 // Helper members used when reading/writing index from/to disk.
897 // Contains number of entries that should be skipped:
898 // - in hashtable when writing index because they were already written
899 // - in index file when reading index because they were already read
900 uint32_t mSkipEntries
;
901 // Number of entries that should be written to disk. This is number of entries
902 // in hashtable that are initialized and are not marked as removed when writing
904 uint32_t mProcessEntries
;
908 nsRefPtr
<CacheHash
> mRWHash
;
910 // Reading of journal succeeded if true.
911 bool mJournalReadSuccessfully
;
913 // Handle used for writing and reading index file.
914 nsRefPtr
<CacheFileHandle
> mIndexHandle
;
915 // Handle used for reading journal file.
916 nsRefPtr
<CacheFileHandle
> mJournalHandle
;
917 // Used to check the existence of the file during reading process.
918 nsRefPtr
<CacheFileHandle
> mTmpHandle
;
920 nsRefPtr
<FileOpenHelper
> mIndexFileOpener
;
921 nsRefPtr
<FileOpenHelper
> mJournalFileOpener
;
922 nsRefPtr
<FileOpenHelper
> mTmpFileOpener
;
924 // Directory enumerator used when building and updating index.
925 nsCOMPtr
<nsIDirectoryEnumerator
> mDirEnumerator
;
927 // Main index hashtable.
928 nsTHashtable
<CacheIndexEntry
> mIndex
;
930 // We cannot add, remove or change any entry in mIndex in states READING and
931 // WRITING. We track all changes in mPendingUpdates during these states.
932 nsTHashtable
<CacheIndexEntry
> mPendingUpdates
;
934 // Contains information statistics for mIndex + mPendingUpdates.
935 CacheIndexStats mIndexStats
;
937 // When reading journal, we must first parse the whole file and apply the
938 // changes iff the journal was read successfully. mTmpJournal is used to store
939 // entries from the journal file. We throw away all these entries if parsing
940 // of the journal fails or the hash does not match.
941 nsTHashtable
<CacheIndexEntry
> mTmpJournal
;
943 // Arrays that keep entry records ordered by eviction preference. When looking
944 // for an entry to evict, we first try to find an expired entry. If there is
945 // no expired entry, we take the entry with lowest valid frecency. Zero
946 // frecency is an initial value and such entries are stored at the end of the
947 // array. Uninitialized entries and entries marked as deleted are not present
949 nsTArray
<CacheIndexRecord
*> mFrecencyArray
;
950 nsTArray
<CacheIndexRecord
*> mExpirationArray
;
952 nsTArray
<CacheIndexIterator
*> mIterators
;
954 class DiskConsumptionObserver
: public nsRunnable
957 static DiskConsumptionObserver
* Init(nsICacheStorageConsumptionObserver
* aObserver
)
959 nsWeakPtr observer
= do_GetWeakReference(aObserver
);
963 return new DiskConsumptionObserver(observer
);
966 void OnDiskConsumption(int64_t aSize
)
969 NS_DispatchToMainThread(this);
973 explicit DiskConsumptionObserver(nsWeakPtr
const &aWeakObserver
)
974 : mObserver(aWeakObserver
) { }
975 virtual ~DiskConsumptionObserver() { }
979 MOZ_ASSERT(NS_IsMainThread());
981 nsCOMPtr
<nsICacheStorageConsumptionObserver
> observer
=
982 do_QueryReferent(mObserver
);
985 observer
->OnNetworkCacheDiskConsumption(mSize
);
995 // List of async observers that want to get disk consumption information
996 nsTArray
<nsRefPtr
<DiskConsumptionObserver
> > mDiskConsumptionObservers
;
999 class CacheIndexAutoLock
{
1001 explicit CacheIndexAutoLock(CacheIndex
*aIndex
)
1007 ~CacheIndexAutoLock()
1015 MOZ_ASSERT(!mLocked
);
1021 MOZ_ASSERT(mLocked
);
1027 nsRefPtr
<CacheIndex
> mIndex
;
1031 class CacheIndexAutoUnlock
{
1033 explicit CacheIndexAutoUnlock(CacheIndex
*aIndex
)
1039 ~CacheIndexAutoUnlock()
1047 MOZ_ASSERT(!mLocked
);
1053 MOZ_ASSERT(mLocked
);
1059 nsRefPtr
<CacheIndex
> mIndex
;