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 #include "CacheIndex.h"
8 #include "CacheFileIOManager.h"
9 #include "CacheFileMetadata.h"
10 #include "CacheIndexIterator.h"
11 #include "CacheIndexContextIterator.h"
12 #include "nsThreadUtils.h"
13 #include "nsISimpleEnumerator.h"
14 #include "nsIDirectoryEnumerator.h"
15 #include "nsISizeOf.h"
16 #include "nsPrintfCString.h"
17 #include "mozilla/DebugOnly.h"
21 #include "mozilla/AutoRestore.h"
25 #define kMinUnwrittenChanges 300
26 #define kMinDumpInterval 20000 // in milliseconds
27 #define kMaxBufSize 16384
28 #define kIndexVersion 0x00000001
29 #define kUpdateIndexStartDelay 50000 // in milliseconds
31 const char kIndexName
[] = "index";
32 const char kTempIndexName
[] = "index.tmp";
33 const char kJournalName
[] = "index.log";
39 * This helper class is responsible for keeping CacheIndex::mIndexStats,
40 * CacheIndex::mFrecencyArray and CacheIndex::mExpirationArray up to date.
42 class CacheIndexEntryAutoManage
45 CacheIndexEntryAutoManage(const SHA1Sum::Hash
*aHash
, CacheIndex
*aIndex
)
49 , mOldExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME
)
50 , mDoNotSearchInIndex(false)
51 , mDoNotSearchInUpdates(false)
53 mIndex
->AssertOwnsLock();
56 CacheIndexEntry
*entry
= FindEntry();
57 mIndex
->mIndexStats
.BeforeChange(entry
);
58 if (entry
&& entry
->IsInitialized() && !entry
->IsRemoved()) {
59 mOldRecord
= entry
->mRec
;
60 mOldFrecency
= entry
->mRec
->mFrecency
;
61 mOldExpirationTime
= entry
->mRec
->mExpirationTime
;
65 ~CacheIndexEntryAutoManage()
67 mIndex
->AssertOwnsLock();
69 CacheIndexEntry
*entry
= FindEntry();
70 mIndex
->mIndexStats
.AfterChange(entry
);
71 if (!entry
|| !entry
->IsInitialized() || entry
->IsRemoved()) {
75 if (entry
&& !mOldRecord
) {
76 mIndex
->InsertRecordToFrecencyArray(entry
->mRec
);
77 mIndex
->InsertRecordToExpirationArray(entry
->mRec
);
78 mIndex
->AddRecordToIterators(entry
->mRec
);
79 } else if (!entry
&& mOldRecord
) {
80 mIndex
->RemoveRecordFromFrecencyArray(mOldRecord
);
81 mIndex
->RemoveRecordFromExpirationArray(mOldRecord
);
82 mIndex
->RemoveRecordFromIterators(mOldRecord
);
83 } else if (entry
&& mOldRecord
) {
84 bool replaceFrecency
= false;
85 bool replaceExpiration
= false;
87 if (entry
->mRec
!= mOldRecord
) {
88 // record has a different address, we have to replace it
89 replaceFrecency
= replaceExpiration
= true;
90 mIndex
->ReplaceRecordInIterators(mOldRecord
, entry
->mRec
);
92 if (entry
->mRec
->mFrecency
== 0 &&
93 entry
->mRec
->mExpirationTime
== nsICacheEntry::NO_EXPIRATION_TIME
) {
94 // This is a special case when we want to make sure that the entry is
95 // placed at the end of the lists even when the values didn't change.
96 replaceFrecency
= replaceExpiration
= true;
98 if (entry
->mRec
->mFrecency
!= mOldFrecency
) {
99 replaceFrecency
= true;
101 if (entry
->mRec
->mExpirationTime
!= mOldExpirationTime
) {
102 replaceExpiration
= true;
107 if (replaceFrecency
) {
108 mIndex
->RemoveRecordFromFrecencyArray(mOldRecord
);
109 mIndex
->InsertRecordToFrecencyArray(entry
->mRec
);
111 if (replaceExpiration
) {
112 mIndex
->RemoveRecordFromExpirationArray(mOldRecord
);
113 mIndex
->InsertRecordToExpirationArray(entry
->mRec
);
116 // both entries were removed or not initialized, do nothing
120 // We cannot rely on nsTHashtable::GetEntry() in case we are enumerating the
121 // entries and returning PL_DHASH_REMOVE. Destructor is called before the
122 // entry is removed. Caller must call one of following methods to skip
123 // lookup in the hashtable.
124 void DoNotSearchInIndex() { mDoNotSearchInIndex
= true; }
125 void DoNotSearchInUpdates() { mDoNotSearchInUpdates
= true; }
128 CacheIndexEntry
* FindEntry()
130 CacheIndexEntry
*entry
= nullptr;
132 switch (mIndex
->mState
) {
133 case CacheIndex::READING
:
134 case CacheIndex::WRITING
:
135 if (!mDoNotSearchInUpdates
) {
136 entry
= mIndex
->mPendingUpdates
.GetEntry(*mHash
);
139 case CacheIndex::BUILDING
:
140 case CacheIndex::UPDATING
:
141 case CacheIndex::READY
:
142 if (!entry
&& !mDoNotSearchInIndex
) {
143 entry
= mIndex
->mIndex
.GetEntry(*mHash
);
146 case CacheIndex::INITIAL
:
147 case CacheIndex::SHUTDOWN
:
149 MOZ_ASSERT(false, "Unexpected state!");
155 const SHA1Sum::Hash
*mHash
;
156 nsRefPtr
<CacheIndex
> mIndex
;
157 CacheIndexRecord
*mOldRecord
;
158 uint32_t mOldFrecency
;
159 uint32_t mOldExpirationTime
;
160 bool mDoNotSearchInIndex
;
161 bool mDoNotSearchInUpdates
;
164 class FileOpenHelper
: public CacheFileIOListener
167 NS_DECL_THREADSAFE_ISUPPORTS
169 explicit FileOpenHelper(CacheIndex
* aIndex
)
175 mIndex
->AssertOwnsLock();
180 virtual ~FileOpenHelper() {}
182 NS_IMETHOD
OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
);
183 NS_IMETHOD
OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
185 MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!");
186 return NS_ERROR_UNEXPECTED
;
188 NS_IMETHOD
OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
,
190 MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!");
191 return NS_ERROR_UNEXPECTED
;
193 NS_IMETHOD
OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
) {
194 MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!");
195 return NS_ERROR_UNEXPECTED
;
197 NS_IMETHOD
OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
) {
198 MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!");
199 return NS_ERROR_UNEXPECTED
;
201 NS_IMETHOD
OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
) {
202 MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!");
203 return NS_ERROR_UNEXPECTED
;
206 nsRefPtr
<CacheIndex
> mIndex
;
210 NS_IMETHODIMP
FileOpenHelper::OnFileOpened(CacheFileHandle
*aHandle
,
213 CacheIndexAutoLock
lock(mIndex
);
217 CacheFileIOManager::DoomFile(aHandle
, nullptr);
223 mIndex
->OnFileOpenedInternal(this, aHandle
, aResult
);
228 NS_IMPL_ISUPPORTS(FileOpenHelper
, CacheFileIOListener
);
231 CacheIndex
* CacheIndex::gInstance
= nullptr;
234 NS_IMPL_ADDREF(CacheIndex
)
235 NS_IMPL_RELEASE(CacheIndex
)
237 NS_INTERFACE_MAP_BEGIN(CacheIndex
)
238 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener
)
239 NS_INTERFACE_MAP_ENTRY(nsIRunnable
)
240 NS_INTERFACE_MAP_END_THREADSAFE
243 CacheIndex::CacheIndex()
244 : mLock("CacheIndex.mLock")
246 , mShuttingDown(false)
247 , mIndexNeedsUpdate(false)
248 , mRemovingAll(false)
249 , mIndexOnDiskIsValid(false)
250 , mDontMarkIndexClean(false)
252 , mUpdateEventPending(false)
258 , mJournalReadSuccessfully(false)
260 LOG(("CacheIndex::CacheIndex [this=%p]", this));
261 MOZ_COUNT_CTOR(CacheIndex
);
262 MOZ_ASSERT(!gInstance
, "multiple CacheIndex instances!");
265 CacheIndex::~CacheIndex()
267 LOG(("CacheIndex::~CacheIndex [this=%p]", this));
268 MOZ_COUNT_DTOR(CacheIndex
);
278 MOZ_ASSERT(!mIndexStats
.StateLogged());
284 MOZ_ASSERT(!mIndexStats
.StateLogged());
290 CacheIndex::AssertOwnsLock()
292 mLock
.AssertCurrentThreadOwns();
297 CacheIndex::Init(nsIFile
*aCacheDirectory
)
299 LOG(("CacheIndex::Init()"));
301 MOZ_ASSERT(NS_IsMainThread());
304 return NS_ERROR_ALREADY_INITIALIZED
;
307 nsRefPtr
<CacheIndex
> idx
= new CacheIndex();
309 CacheIndexAutoLock
lock(idx
);
311 nsresult rv
= idx
->InitInternal(aCacheDirectory
);
312 NS_ENSURE_SUCCESS(rv
, rv
);
319 CacheIndex::InitInternal(nsIFile
*aCacheDirectory
)
323 rv
= aCacheDirectory
->Clone(getter_AddRefs(mCacheDirectory
));
324 NS_ENSURE_SUCCESS(rv
, rv
);
326 mStartTime
= TimeStamp::NowLoRes();
335 CacheIndex::PreShutdown()
337 LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance
));
339 MOZ_ASSERT(NS_IsMainThread());
342 nsRefPtr
<CacheIndex
> index
= gInstance
;
345 return NS_ERROR_NOT_INITIALIZED
;
348 CacheIndexAutoLock
lock(index
);
350 LOG(("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, "
351 "dontMarkIndexClean=%d]", index
->mState
, index
->mIndexOnDiskIsValid
,
352 index
->mDontMarkIndexClean
));
354 LOG(("CacheIndex::PreShutdown() - Closing iterators."));
355 for (uint32_t i
= 0; i
< index
->mIterators
.Length(); ) {
356 rv
= index
->mIterators
[i
]->CloseInternal(NS_ERROR_FAILURE
);
358 // CacheIndexIterator::CloseInternal() removes itself from mIteratos iff
359 // it returns success.
360 LOG(("CacheIndex::PreShutdown() - Failed to remove iterator %p. "
366 index
->mShuttingDown
= true;
368 if (index
->mState
== READY
) {
369 return NS_OK
; // nothing to do
372 nsCOMPtr
<nsIRunnable
> event
;
373 event
= NS_NewRunnableMethod(index
, &CacheIndex::PreShutdownInternal
);
375 nsCOMPtr
<nsIEventTarget
> ioTarget
= CacheFileIOManager::IOTarget();
376 MOZ_ASSERT(ioTarget
);
378 // PreShutdownInternal() will be executed before any queued event on INDEX
379 // level. That's OK since we don't want to wait for any operation in progess.
380 // We need to interrupt it and save journal as quickly as possible.
381 rv
= ioTarget
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
383 NS_WARNING("CacheIndex::PreShutdown() - Can't dispatch event");
384 LOG(("CacheIndex::PreShutdown() - Can't dispatch event" ));
392 CacheIndex::PreShutdownInternal()
394 CacheIndexAutoLock
lock(this);
396 LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
397 "dontMarkIndexClean=%d]", mState
, mIndexOnDiskIsValid
,
398 mDontMarkIndexClean
));
400 MOZ_ASSERT(mShuttingDown
);
403 mUpdateTimer
= nullptr;
411 // nothing to do, write the journal in Shutdown()
421 MOZ_ASSERT(false, "Implement me!");
424 // We should end up in READY state
425 MOZ_ASSERT(mState
== READY
);
430 CacheIndex::Shutdown()
432 LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance
));
434 MOZ_ASSERT(NS_IsMainThread());
436 nsRefPtr
<CacheIndex
> index
;
437 index
.swap(gInstance
);
440 return NS_ERROR_NOT_INITIALIZED
;
443 CacheIndexAutoLock
lock(index
);
445 bool sanitize
= CacheObserver::ClearCacheOnShutdown();
447 LOG(("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, "
448 "dontMarkIndexClean=%d, sanitize=%d]", index
->mState
,
449 index
->mIndexOnDiskIsValid
, index
->mDontMarkIndexClean
, sanitize
));
451 MOZ_ASSERT(index
->mShuttingDown
);
453 EState oldState
= index
->mState
;
454 index
->ChangeState(SHUTDOWN
);
456 if (oldState
!= READY
) {
457 LOG(("CacheIndex::Shutdown() - Unexpected state. Did posting of "
458 "PreShutdownInternal() fail?"));
463 index
->FinishWrite(false);
466 if (index
->mIndexOnDiskIsValid
&& !index
->mDontMarkIndexClean
) {
467 if (!sanitize
&& NS_FAILED(index
->WriteLogToDisk())) {
468 index
->RemoveIndexFromDisk();
471 index
->RemoveIndexFromDisk();
475 index
->FinishRead(false);
479 index
->FinishUpdate(false);
482 MOZ_ASSERT(false, "Unexpected state!");
486 index
->RemoveIndexFromDisk();
494 CacheIndex::AddEntry(const SHA1Sum::Hash
*aHash
)
496 LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash
)));
498 nsRefPtr
<CacheIndex
> index
= gInstance
;
501 return NS_ERROR_NOT_INITIALIZED
;
504 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
506 CacheIndexAutoLock
lock(index
);
508 if (!index
->IsIndexUsable()) {
509 return NS_ERROR_NOT_AVAILABLE
;
512 // Getters in CacheIndexStats assert when mStateLogged is true since the
513 // information is incomplete between calls to BeforeChange() and AfterChange()
514 // (i.e. while CacheIndexEntryAutoManage exists). We need to check whether
515 // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
516 bool updateIfNonFreshEntriesExist
= false;
519 CacheIndexEntryAutoManage
entryMng(aHash
, index
);
521 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aHash
);
522 bool entryRemoved
= entry
&& entry
->IsRemoved();
524 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
525 index
->mState
== BUILDING
) {
526 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
528 if (entry
&& !entryRemoved
) {
529 // Found entry in index that shouldn't exist.
531 if (entry
->IsFresh()) {
532 // Someone removed the file on disk while FF is running. Update
533 // process can fix only non-fresh entries (i.e. entries that were not
534 // added within this session). Start update only if we have such
537 // TODO: This should be very rare problem. If it turns out not to be
538 // true, change the update process so that it also iterates all
539 // initialized non-empty entries and checks whether the file exists.
541 LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF "
544 updateIfNonFreshEntriesExist
= true;
545 } else if (index
->mState
== READY
) {
546 // Index is outdated, update it.
547 LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
548 "update is needed"));
549 index
->mIndexNeedsUpdate
= true;
551 // We cannot be here when building index since all entries are fresh
553 MOZ_ASSERT(index
->mState
== UPDATING
);
558 entry
= index
->mIndex
.PutEntry(*aHash
);
560 } else { // WRITING, READING
561 CacheIndexEntry
*updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
562 bool updatedRemoved
= updated
&& updated
->IsRemoved();
564 if ((updated
&& !updatedRemoved
) ||
565 (!updated
&& entry
&& !entryRemoved
&& entry
->IsFresh())) {
566 // Fresh entry found, so the file was removed outside FF
567 LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF "
570 updateIfNonFreshEntriesExist
= true;
571 } else if (!updated
&& entry
&& !entryRemoved
) {
572 if (index
->mState
== WRITING
) {
573 LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
574 "update is needed"));
575 index
->mIndexNeedsUpdate
= true;
577 // Ignore if state is READING since the index information is partial
580 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
589 if (updateIfNonFreshEntriesExist
&&
590 index
->mIndexStats
.Count() != index
->mIndexStats
.Fresh()) {
591 index
->mIndexNeedsUpdate
= true;
594 index
->StartUpdatingIndexIfNeeded();
595 index
->WriteIndexToDiskIfNeeded();
602 CacheIndex::EnsureEntryExists(const SHA1Sum::Hash
*aHash
)
604 LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]",
607 nsRefPtr
<CacheIndex
> index
= gInstance
;
610 return NS_ERROR_NOT_INITIALIZED
;
613 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
615 CacheIndexAutoLock
lock(index
);
617 if (!index
->IsIndexUsable()) {
618 return NS_ERROR_NOT_AVAILABLE
;
622 CacheIndexEntryAutoManage
entryMng(aHash
, index
);
624 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aHash
);
625 bool entryRemoved
= entry
&& entry
->IsRemoved();
627 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
628 index
->mState
== BUILDING
) {
629 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
631 if (!entry
|| entryRemoved
) {
632 if (entryRemoved
&& entry
->IsFresh()) {
633 // This could happen only if somebody copies files to the entries
634 // directory while FF is running.
635 LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside "
636 "FF process! Update is needed."));
637 index
->mIndexNeedsUpdate
= true;
638 } else if (index
->mState
== READY
||
639 (entryRemoved
&& !entry
->IsFresh())) {
640 // Removed non-fresh entries can be present as a result of
641 // ProcessJournalEntry()
642 LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
643 " exist, update is needed"));
644 index
->mIndexNeedsUpdate
= true;
648 entry
= index
->mIndex
.PutEntry(*aHash
);
654 } else { // WRITING, READING
655 CacheIndexEntry
*updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
656 bool updatedRemoved
= updated
&& updated
->IsRemoved();
658 if (updatedRemoved
||
659 (!updated
&& entryRemoved
&& entry
->IsFresh())) {
660 // Fresh information about missing entry found. This could happen only
661 // if somebody copies files to the entries directory while FF is running.
662 LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside "
663 "FF process! Update is needed."));
664 index
->mIndexNeedsUpdate
= true;
665 } else if (!updated
&& (!entry
|| entryRemoved
)) {
666 if (index
->mState
== WRITING
) {
667 LOG(("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
668 " exist, update is needed"));
669 index
->mIndexNeedsUpdate
= true;
671 // Ignore if state is READING since the index information is partial
674 // We don't need entryRemoved and updatedRemoved info anymore
675 if (entryRemoved
) entry
= nullptr;
676 if (updatedRemoved
) updated
= nullptr;
679 updated
->MarkFresh();
682 // Create a new entry
683 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
685 updated
->MarkFresh();
686 updated
->MarkDirty();
688 if (!entry
->IsFresh()) {
689 // To mark the entry fresh we must make a copy of index entry
690 // since the index is read-only.
691 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
693 updated
->MarkFresh();
700 index
->StartUpdatingIndexIfNeeded();
701 index
->WriteIndexToDiskIfNeeded();
708 CacheIndex::InitEntry(const SHA1Sum::Hash
*aHash
,
713 LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, "
714 "anonymous=%d, inBrowser=%d]", LOGSHA1(aHash
), aAppId
, aAnonymous
,
717 nsRefPtr
<CacheIndex
> index
= gInstance
;
720 return NS_ERROR_NOT_INITIALIZED
;
723 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
725 CacheIndexAutoLock
lock(index
);
727 if (!index
->IsIndexUsable()) {
728 return NS_ERROR_NOT_AVAILABLE
;
732 CacheIndexEntryAutoManage
entryMng(aHash
, index
);
734 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aHash
);
735 bool reinitEntry
= false;
737 if (entry
&& entry
->IsRemoved()) {
741 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
742 index
->mState
== BUILDING
) {
743 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
745 MOZ_ASSERT(entry
->IsFresh());
747 if (IsCollision(entry
, aAppId
, aAnonymous
, aInBrowser
)) {
748 index
->mIndexNeedsUpdate
= true; // TODO Does this really help in case of collision?
751 if (entry
->IsInitialized()) {
756 CacheIndexEntry
*updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
757 DebugOnly
<bool> removed
= updated
&& updated
->IsRemoved();
759 MOZ_ASSERT(updated
|| !removed
);
760 MOZ_ASSERT(updated
|| entry
);
763 MOZ_ASSERT(updated
->IsFresh());
765 if (IsCollision(updated
, aAppId
, aAnonymous
, aInBrowser
)) {
766 index
->mIndexNeedsUpdate
= true;
769 if (updated
->IsInitialized()) {
775 MOZ_ASSERT(entry
->IsFresh());
777 if (IsCollision(entry
, aAppId
, aAnonymous
, aInBrowser
)) {
778 index
->mIndexNeedsUpdate
= true;
781 if (entry
->IsInitialized()) {
786 // make a copy of a read-only entry
787 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
794 // There is a collision and we are going to rewrite this entry. Initialize
795 // it as a new entry.
799 entry
->Init(aAppId
, aAnonymous
, aInBrowser
);
803 index
->StartUpdatingIndexIfNeeded();
804 index
->WriteIndexToDiskIfNeeded();
811 CacheIndex::RemoveEntry(const SHA1Sum::Hash
*aHash
)
813 LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]",
816 nsRefPtr
<CacheIndex
> index
= gInstance
;
819 return NS_ERROR_NOT_INITIALIZED
;
822 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
824 CacheIndexAutoLock
lock(index
);
826 if (!index
->IsIndexUsable()) {
827 return NS_ERROR_NOT_AVAILABLE
;
831 CacheIndexEntryAutoManage
entryMng(aHash
, index
);
833 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aHash
);
834 bool entryRemoved
= entry
&& entry
->IsRemoved();
836 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
837 index
->mState
== BUILDING
) {
838 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
840 if (!entry
|| entryRemoved
) {
841 if (entryRemoved
&& entry
->IsFresh()) {
842 // This could happen only if somebody copies files to the entries
843 // directory while FF is running.
844 LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF "
845 "process! Update is needed."));
846 index
->mIndexNeedsUpdate
= true;
847 } else if (index
->mState
== READY
||
848 (entryRemoved
&& !entry
->IsFresh())) {
849 // Removed non-fresh entries can be present as a result of
850 // ProcessJournalEntry()
851 LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
852 ", update is needed"));
853 index
->mIndexNeedsUpdate
= true;
857 if (!entry
->IsDirty() && entry
->IsFileEmpty()) {
858 index
->mIndex
.RemoveEntry(*aHash
);
861 entry
->MarkRemoved();
867 } else { // WRITING, READING
868 CacheIndexEntry
*updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
869 bool updatedRemoved
= updated
&& updated
->IsRemoved();
871 if (updatedRemoved
||
872 (!updated
&& entryRemoved
&& entry
->IsFresh())) {
873 // Fresh information about missing entry found. This could happen only
874 // if somebody copies files to the entries directory while FF is running.
875 LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF "
876 "process! Update is needed."));
877 index
->mIndexNeedsUpdate
= true;
878 } else if (!updated
&& (!entry
|| entryRemoved
)) {
879 if (index
->mState
== WRITING
) {
880 LOG(("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
881 ", update is needed"));
882 index
->mIndexNeedsUpdate
= true;
884 // Ignore if state is READING since the index information is partial
888 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
892 updated
->MarkRemoved();
893 updated
->MarkDirty();
894 updated
->MarkFresh();
898 index
->StartUpdatingIndexIfNeeded();
899 index
->WriteIndexToDiskIfNeeded();
906 CacheIndex::UpdateEntry(const SHA1Sum::Hash
*aHash
,
907 const uint32_t *aFrecency
,
908 const uint32_t *aExpirationTime
,
909 const uint32_t *aSize
)
911 LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
912 "frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash
),
913 aFrecency
? nsPrintfCString("%u", *aFrecency
).get() : "",
914 aExpirationTime
? nsPrintfCString("%u", *aExpirationTime
).get() : "",
915 aSize
? nsPrintfCString("%u", *aSize
).get() : ""));
917 nsRefPtr
<CacheIndex
> index
= gInstance
;
920 return NS_ERROR_NOT_INITIALIZED
;
923 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
925 CacheIndexAutoLock
lock(index
);
927 if (!index
->IsIndexUsable()) {
928 return NS_ERROR_NOT_AVAILABLE
;
932 CacheIndexEntryAutoManage
entryMng(aHash
, index
);
934 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aHash
);
936 if (entry
&& entry
->IsRemoved()) {
940 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
941 index
->mState
== BUILDING
) {
942 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
945 if (!HasEntryChanged(entry
, aFrecency
, aExpirationTime
, aSize
)) {
949 CacheIndexEntry
*updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
950 DebugOnly
<bool> removed
= updated
&& updated
->IsRemoved();
952 MOZ_ASSERT(updated
|| !removed
);
953 MOZ_ASSERT(updated
|| entry
);
957 HasEntryChanged(entry
, aFrecency
, aExpirationTime
, aSize
)) {
958 // make a copy of a read-only entry
959 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
963 return NS_ERROR_NOT_AVAILABLE
;
970 MOZ_ASSERT(entry
->IsFresh());
971 MOZ_ASSERT(entry
->IsInitialized());
975 entry
->SetFrecency(*aFrecency
);
978 if (aExpirationTime
) {
979 entry
->SetExpirationTime(*aExpirationTime
);
983 entry
->SetFileSize(*aSize
);
987 index
->WriteIndexToDiskIfNeeded();
994 CacheIndex::RemoveAll()
996 LOG(("CacheIndex::RemoveAll()"));
998 nsRefPtr
<CacheIndex
> index
= gInstance
;
1001 return NS_ERROR_NOT_INITIALIZED
;
1004 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1006 nsCOMPtr
<nsIFile
> file
;
1009 CacheIndexAutoLock
lock(index
);
1011 MOZ_ASSERT(!index
->mRemovingAll
);
1013 if (!index
->IsIndexUsable()) {
1014 return NS_ERROR_NOT_AVAILABLE
;
1017 AutoRestore
<bool> saveRemovingAll(index
->mRemovingAll
);
1018 index
->mRemovingAll
= true;
1020 // Doom index and journal handles but don't null them out since this will be
1021 // done in FinishWrite/FinishRead methods.
1022 if (index
->mIndexHandle
) {
1023 CacheFileIOManager::DoomFile(index
->mIndexHandle
, nullptr);
1025 // We don't have a handle to index file, so get the file here, but delete
1026 // it outside the lock. Ignore the result since this is not fatal.
1027 index
->GetFile(NS_LITERAL_CSTRING(kIndexName
), getter_AddRefs(file
));
1030 if (index
->mJournalHandle
) {
1031 CacheFileIOManager::DoomFile(index
->mJournalHandle
, nullptr);
1034 switch (index
->mState
) {
1036 index
->FinishWrite(false);
1042 index
->FinishRead(false);
1046 index
->FinishUpdate(false);
1049 MOZ_ASSERT(false, "Unexpected state!");
1052 // We should end up in READY state
1053 MOZ_ASSERT(index
->mState
== READY
);
1055 // There should not be any handle
1056 MOZ_ASSERT(!index
->mIndexHandle
);
1057 MOZ_ASSERT(!index
->mJournalHandle
);
1059 index
->mIndexOnDiskIsValid
= false;
1060 index
->mIndexNeedsUpdate
= false;
1062 index
->mIndexStats
.Clear();
1063 index
->mFrecencyArray
.Clear();
1064 index
->mExpirationArray
.Clear();
1065 index
->mIndex
.Clear();
1069 // Ignore the result. The file might not exist and the failure is not fatal.
1070 file
->Remove(false);
1078 CacheIndex::HasEntry(const nsACString
&aKey
, EntryStatus
*_retval
)
1080 LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey
).get()));
1082 nsRefPtr
<CacheIndex
> index
= gInstance
;
1085 return NS_ERROR_NOT_INITIALIZED
;
1088 CacheIndexAutoLock
lock(index
);
1090 if (!index
->IsIndexUsable()) {
1091 return NS_ERROR_NOT_AVAILABLE
;
1096 sum
.update(aKey
.BeginReading(), aKey
.Length());
1099 CacheIndexEntry
*entry
= nullptr;
1101 switch (index
->mState
) {
1104 entry
= index
->mPendingUpdates
.GetEntry(hash
);
1110 entry
= index
->mIndex
.GetEntry(hash
);
1115 MOZ_ASSERT(false, "Unexpected state!");
1119 if (index
->mState
== READY
|| index
->mState
== WRITING
) {
1120 *_retval
= DOES_NOT_EXIST
;
1122 *_retval
= DO_NOT_KNOW
;
1125 if (entry
->IsRemoved()) {
1126 if (entry
->IsFresh()) {
1127 *_retval
= DOES_NOT_EXIST
;
1129 *_retval
= DO_NOT_KNOW
;
1136 LOG(("CacheIndex::HasEntry() - result is %u", *_retval
));
1142 CacheIndex::GetEntryForEviction(SHA1Sum::Hash
*aHash
, uint32_t *aCnt
)
1144 LOG(("CacheIndex::GetEntryForEviction()"));
1146 nsRefPtr
<CacheIndex
> index
= gInstance
;
1149 return NS_ERROR_NOT_INITIALIZED
;
1151 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1153 CacheIndexAutoLock
lock(index
);
1155 if (!index
->IsIndexUsable()) {
1156 return NS_ERROR_NOT_AVAILABLE
;
1159 MOZ_ASSERT(index
->mFrecencyArray
.Length() ==
1160 index
->mExpirationArray
.Length());
1162 if (index
->mExpirationArray
.Length() == 0)
1163 return NS_ERROR_NOT_AVAILABLE
;
1166 bool foundEntry
= false;
1167 uint32_t i
= 0, j
= 0;
1168 uint32_t now
= PR_Now() / PR_USEC_PER_SEC
;
1170 // find the first expired, non-forced valid entry
1171 for (i
= 0; i
< index
->mExpirationArray
.Length(); i
++) {
1172 if (index
->mExpirationArray
[i
]->mExpirationTime
< now
) {
1173 memcpy(&hash
, &index
->mExpirationArray
[i
]->mHash
, sizeof(SHA1Sum::Hash
));
1175 if (!IsForcedValidEntry(&hash
)) {
1181 // all further entries have not expired yet
1187 *aCnt
= index
->mExpirationArray
.Length() - i
;
1189 LOG(("CacheIndex::GetEntryForEviction() - returning entry from expiration "
1190 "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, "
1191 "frecency=%u]", LOGSHA1(&hash
), *aCnt
,
1192 index
->mExpirationArray
[i
]->mExpirationTime
, now
,
1193 index
->mExpirationArray
[i
]->mFrecency
));
1196 // check if we've already tried all the entries
1197 if (i
== index
->mExpirationArray
.Length())
1198 return NS_ERROR_NOT_AVAILABLE
;
1200 // find first non-forced valid entry with the lowest frecency
1201 for (j
= 0; j
< index
->mFrecencyArray
.Length(); j
++) {
1202 memcpy(&hash
, &index
->mFrecencyArray
[j
]->mHash
, sizeof(SHA1Sum::Hash
));
1204 if (!IsForcedValidEntry(&hash
)) {
1211 return NS_ERROR_NOT_AVAILABLE
;
1213 // forced valid entries skipped in both arrays could overlap, just use max
1214 *aCnt
= index
->mFrecencyArray
.Length() - std::max(i
, j
);
1216 LOG(("CacheIndex::GetEntryForEviction() - returning entry from frecency "
1217 "array [hash=%08x%08x%08x%08x%08x, cnt=%u, expTime=%u, now=%u, "
1218 "frecency=%u]", LOGSHA1(&hash
), *aCnt
,
1219 index
->mFrecencyArray
[j
]->mExpirationTime
, now
,
1220 index
->mFrecencyArray
[j
]->mFrecency
));
1223 memcpy(aHash
, &hash
, sizeof(SHA1Sum::Hash
));
1230 bool CacheIndex::IsForcedValidEntry(const SHA1Sum::Hash
*aHash
)
1232 nsRefPtr
<CacheFileHandle
> handle
;
1234 CacheFileIOManager::gInstance
->mHandles
.GetHandle(
1235 aHash
, false, getter_AddRefs(handle
));
1240 nsCString hashKey
= handle
->Key();
1241 return CacheStorageService::Self()->IsForcedValidEntry(hashKey
);
1247 CacheIndex::GetCacheSize(uint32_t *_retval
)
1249 LOG(("CacheIndex::GetCacheSize()"));
1251 nsRefPtr
<CacheIndex
> index
= gInstance
;
1254 return NS_ERROR_NOT_INITIALIZED
;
1256 CacheIndexAutoLock
lock(index
);
1258 if (!index
->IsIndexUsable()) {
1259 return NS_ERROR_NOT_AVAILABLE
;
1262 *_retval
= index
->mIndexStats
.Size();
1263 LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval
));
1269 CacheIndex::GetCacheStats(nsILoadContextInfo
*aInfo
, uint32_t *aSize
, uint32_t *aCount
)
1271 LOG(("CacheIndex::GetCacheStats() [info=%p]", aInfo
));
1273 nsRefPtr
<CacheIndex
> index
= gInstance
;
1276 return NS_ERROR_NOT_INITIALIZED
;
1279 CacheIndexAutoLock
lock(index
);
1281 if (!index
->IsIndexUsable()) {
1282 return NS_ERROR_NOT_AVAILABLE
;
1286 return NS_ERROR_INVALID_ARG
;
1292 for (uint32_t i
= 0; i
< index
->mFrecencyArray
.Length(); ++i
) {
1293 CacheIndexRecord
* record
= index
->mFrecencyArray
[i
];
1294 if (!CacheIndexEntry::RecordMatchesLoadContextInfo(record
, aInfo
))
1297 *aSize
+= CacheIndexEntry::GetFileSize(record
);
1306 CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver
* aObserver
)
1308 LOG(("CacheIndex::AsyncGetDiskConsumption()"));
1310 nsRefPtr
<CacheIndex
> index
= gInstance
;
1313 return NS_ERROR_NOT_INITIALIZED
;
1316 CacheIndexAutoLock
lock(index
);
1318 if (!index
->IsIndexUsable()) {
1319 return NS_ERROR_NOT_AVAILABLE
;
1322 nsRefPtr
<DiskConsumptionObserver
> observer
=
1323 DiskConsumptionObserver::Init(aObserver
);
1325 NS_ENSURE_ARG(observer
);
1327 if (index
->mState
== READY
|| index
->mState
== WRITING
) {
1328 LOG(("CacheIndex::AsyncGetDiskConsumption - calling immediately"));
1329 // Safe to call the callback under the lock,
1330 // we always post to the main thread.
1331 observer
->OnDiskConsumption(index
->mIndexStats
.Size() << 10);
1335 LOG(("CacheIndex::AsyncGetDiskConsumption - remembering callback"));
1336 // Will be called when the index get to the READY state.
1337 index
->mDiskConsumptionObservers
.AppendElement(observer
);
1344 CacheIndex::GetIterator(nsILoadContextInfo
*aInfo
, bool aAddNew
,
1345 CacheIndexIterator
**_retval
)
1347 LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo
, aAddNew
));
1349 nsRefPtr
<CacheIndex
> index
= gInstance
;
1352 return NS_ERROR_NOT_INITIALIZED
;
1355 CacheIndexAutoLock
lock(index
);
1357 if (!index
->IsIndexUsable()) {
1358 return NS_ERROR_NOT_AVAILABLE
;
1361 nsRefPtr
<CacheIndexIterator
> iter
;
1363 iter
= new CacheIndexContextIterator(index
, aAddNew
, aInfo
);
1365 iter
= new CacheIndexIterator(index
, aAddNew
);
1368 iter
->AddRecords(index
->mFrecencyArray
);
1370 index
->mIterators
.AppendElement(iter
);
1371 iter
.swap(*_retval
);
1377 CacheIndex::IsUpToDate(bool *_retval
)
1379 LOG(("CacheIndex::IsUpToDate()"));
1381 nsRefPtr
<CacheIndex
> index
= gInstance
;
1384 return NS_ERROR_NOT_INITIALIZED
;
1387 CacheIndexAutoLock
lock(index
);
1389 if (!index
->IsIndexUsable()) {
1390 return NS_ERROR_NOT_AVAILABLE
;
1393 *_retval
= (index
->mState
== READY
|| index
->mState
== WRITING
) &&
1394 !index
->mIndexNeedsUpdate
&& !index
->mShuttingDown
;
1396 LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval
));
1401 CacheIndex::IsIndexUsable()
1403 MOZ_ASSERT(mState
!= INITIAL
);
1423 CacheIndex::IsCollision(CacheIndexEntry
*aEntry
,
1428 if (!aEntry
->IsInitialized()) {
1432 if (aEntry
->AppId() != aAppId
|| aEntry
->Anonymous() != aAnonymous
||
1433 aEntry
->InBrowser() != aInBrowser
) {
1434 LOG(("CacheIndex::IsCollision() - Collision detected for entry hash=%08x"
1435 "%08x%08x%08x%08x, expected values: appId=%u, anonymous=%d, "
1436 "inBrowser=%d; actual values: appId=%u, anonymous=%d, inBrowser=%d]",
1437 LOGSHA1(aEntry
->Hash()), aAppId
, aAnonymous
, aInBrowser
,
1438 aEntry
->AppId(), aEntry
->Anonymous(), aEntry
->InBrowser()));
1447 CacheIndex::HasEntryChanged(CacheIndexEntry
*aEntry
,
1448 const uint32_t *aFrecency
,
1449 const uint32_t *aExpirationTime
,
1450 const uint32_t *aSize
)
1452 if (aFrecency
&& *aFrecency
!= aEntry
->GetFrecency()) {
1456 if (aExpirationTime
&& *aExpirationTime
!= aEntry
->GetExpirationTime()) {
1461 (*aSize
& CacheIndexEntry::kFileSizeMask
) != aEntry
->GetFileSize()) {
1469 CacheIndex::ProcessPendingOperations()
1471 LOG(("CacheIndex::ProcessPendingOperations()"));
1475 mPendingUpdates
.EnumerateEntries(&CacheIndex::UpdateEntryInIndex
, this);
1477 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
1479 EnsureCorrectStats();
1484 CacheIndex::UpdateEntryInIndex(CacheIndexEntry
*aEntry
, void* aClosure
)
1486 CacheIndex
*index
= static_cast<CacheIndex
*>(aClosure
);
1488 LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
1489 LOGSHA1(aEntry
->Hash())));
1491 MOZ_ASSERT(aEntry
->IsFresh());
1492 MOZ_ASSERT(aEntry
->IsDirty());
1494 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aEntry
->Hash());
1496 CacheIndexEntryAutoManage
emng(aEntry
->Hash(), index
);
1497 emng
.DoNotSearchInUpdates();
1499 if (aEntry
->IsRemoved()) {
1501 if (entry
->IsRemoved()) {
1502 MOZ_ASSERT(entry
->IsFresh());
1503 MOZ_ASSERT(entry
->IsDirty());
1504 } else if (!entry
->IsDirty() && entry
->IsFileEmpty()) {
1505 // Entries with empty file are not stored in index on disk. Just remove
1506 // the entry, but only in case the entry is not dirty, i.e. the entry
1507 // file was empty when we wrote the index.
1508 index
->mIndex
.RemoveEntry(*aEntry
->Hash());
1511 entry
->MarkRemoved();
1517 return PL_DHASH_REMOVE
;
1520 entry
= index
->mIndex
.PutEntry(*aEntry
->Hash());
1523 return PL_DHASH_REMOVE
;
1527 CacheIndex::WriteIndexToDiskIfNeeded()
1529 if (mState
!= READY
|| mShuttingDown
) {
1533 if (!mLastDumpTime
.IsNull() &&
1534 (TimeStamp::NowLoRes() - mLastDumpTime
).ToMilliseconds() <
1539 if (mIndexStats
.Dirty() < kMinUnwrittenChanges
) {
1548 CacheIndex::WriteIndexToDisk()
1550 LOG(("CacheIndex::WriteIndexToDisk()"));
1556 MOZ_ASSERT(mState
== READY
);
1557 MOZ_ASSERT(!mRWBuf
);
1558 MOZ_ASSERT(!mRWHash
);
1560 ChangeState(WRITING
);
1562 mProcessEntries
= mIndexStats
.ActiveEntriesCount();
1564 mIndexFileOpener
= new FileOpenHelper(this);
1565 rv
= CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName
),
1566 CacheFileIOManager::SPECIAL_FILE
|
1567 CacheFileIOManager::CREATE
,
1569 if (NS_FAILED(rv
)) {
1570 LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08x]", rv
));
1575 // Write index header to a buffer, it will be written to disk together with
1576 // records in WriteRecords() once we open the file successfully.
1578 mRWHash
= new CacheHash();
1580 CacheIndexHeader
*hdr
= reinterpret_cast<CacheIndexHeader
*>(mRWBuf
);
1581 NetworkEndian::writeUint32(&hdr
->mVersion
, kIndexVersion
);
1582 NetworkEndian::writeUint32(&hdr
->mTimeStamp
,
1583 static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC
));
1584 NetworkEndian::writeUint32(&hdr
->mIsDirty
, 1);
1586 mRWBufPos
= sizeof(CacheIndexHeader
);
1592 struct WriteRecordsHelper
1596 uint32_t mProcessMax
;
1597 uint32_t mProcessed
;
1606 CacheIndex::WriteRecords()
1608 LOG(("CacheIndex::WriteRecords()"));
1613 MOZ_ASSERT(mState
== WRITING
);
1618 MOZ_ASSERT(mRWBufPos
== 0);
1619 fileOffset
= sizeof(CacheIndexHeader
);
1620 fileOffset
+= sizeof(CacheIndexRecord
) * mSkipEntries
;
1622 MOZ_ASSERT(mRWBufPos
== sizeof(CacheIndexHeader
));
1625 uint32_t hashOffset
= mRWBufPos
;
1627 WriteRecordsHelper data
;
1628 data
.mBuf
= mRWBuf
+ mRWBufPos
;
1629 data
.mSkip
= mSkipEntries
;
1630 data
.mProcessMax
= (mRWBufSize
- mRWBufPos
) / sizeof(CacheIndexRecord
);
1631 MOZ_ASSERT(data
.mProcessMax
!= 0 || mProcessEntries
== 0); // TODO make sure we can write an empty index
1632 data
.mProcessed
= 0;
1634 data
.mHasMore
= false;
1637 mIndex
.EnumerateEntries(&CacheIndex::CopyRecordsToRWBuf
, &data
);
1638 MOZ_ASSERT(mRWBufPos
!= static_cast<uint32_t>(data
.mBuf
- mRWBuf
) ||
1639 mProcessEntries
== 0);
1640 mRWBufPos
= data
.mBuf
- mRWBuf
;
1641 mSkipEntries
+= data
.mProcessed
;
1642 MOZ_ASSERT(mSkipEntries
<= mProcessEntries
);
1644 mRWHash
->Update(mRWBuf
+ hashOffset
, mRWBufPos
- hashOffset
);
1646 if (mSkipEntries
== mProcessEntries
) {
1647 MOZ_ASSERT(!data
.mHasMore
);
1649 // We've processed all records
1650 if (mRWBufPos
+ sizeof(CacheHash::Hash32_t
) > mRWBufSize
) {
1651 // realloc buffer to spare another write cycle
1652 mRWBufSize
= mRWBufPos
+ sizeof(CacheHash::Hash32_t
);
1653 mRWBuf
= static_cast<char *>(moz_xrealloc(mRWBuf
, mRWBufSize
));
1656 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
, mRWHash
->GetHash());
1657 mRWBufPos
+= sizeof(CacheHash::Hash32_t
);
1659 MOZ_ASSERT(data
.mHasMore
);
1662 rv
= CacheFileIOManager::Write(mIndexHandle
, fileOffset
, mRWBuf
, mRWBufPos
,
1663 mSkipEntries
== mProcessEntries
, this);
1664 if (NS_FAILED(rv
)) {
1665 LOG(("CacheIndex::WriteRecords() - CacheFileIOManager::Write() failed "
1666 "synchronously [rv=0x%08x]", rv
));
1674 CacheIndex::FinishWrite(bool aSucceeded
)
1676 LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded
));
1678 MOZ_ASSERT((!aSucceeded
&& mState
== SHUTDOWN
) || mState
== WRITING
);
1682 mIndexHandle
= nullptr;
1687 // Opening of the file must not be in progress if writing succeeded.
1688 MOZ_ASSERT(!mIndexFileOpener
);
1690 mIndex
.EnumerateEntries(&CacheIndex::ApplyIndexChanges
, this);
1691 mIndexOnDiskIsValid
= true;
1693 if (mIndexFileOpener
) {
1694 // If opening of the file is still in progress (e.g. WRITE process was
1695 // canceled by RemoveAll()) then we need to cancel the opener to make sure
1696 // that OnFileOpenedInternal() won't be called.
1697 mIndexFileOpener
->Cancel();
1698 mIndexFileOpener
= nullptr;
1702 ProcessPendingOperations();
1705 if (mState
== WRITING
) {
1707 mLastDumpTime
= TimeStamp::NowLoRes();
1713 CacheIndex::CopyRecordsToRWBuf(CacheIndexEntry
*aEntry
, void* aClosure
)
1715 if (aEntry
->IsRemoved()) {
1716 return PL_DHASH_NEXT
;
1719 if (!aEntry
->IsInitialized()) {
1720 return PL_DHASH_NEXT
;
1723 if (aEntry
->IsFileEmpty()) {
1724 return PL_DHASH_NEXT
;
1727 WriteRecordsHelper
*data
= static_cast<WriteRecordsHelper
*>(aClosure
);
1730 return PL_DHASH_NEXT
;
1733 if (data
->mProcessed
== data
->mProcessMax
) {
1735 data
->mHasMore
= true;
1737 return PL_DHASH_STOP
;
1740 aEntry
->WriteToBuf(data
->mBuf
);
1741 data
->mBuf
+= sizeof(CacheIndexRecord
);
1744 return PL_DHASH_NEXT
;
1749 CacheIndex::ApplyIndexChanges(CacheIndexEntry
*aEntry
, void* aClosure
)
1751 CacheIndex
*index
= static_cast<CacheIndex
*>(aClosure
);
1753 CacheIndexEntryAutoManage
emng(aEntry
->Hash(), index
);
1755 if (aEntry
->IsRemoved()) {
1756 emng
.DoNotSearchInIndex();
1757 return PL_DHASH_REMOVE
;
1760 if (aEntry
->IsDirty()) {
1761 aEntry
->ClearDirty();
1764 return PL_DHASH_NEXT
;
1768 CacheIndex::GetFile(const nsACString
&aName
, nsIFile
**_retval
)
1772 nsCOMPtr
<nsIFile
> file
;
1773 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
1774 NS_ENSURE_SUCCESS(rv
, rv
);
1776 rv
= file
->AppendNative(aName
);
1777 NS_ENSURE_SUCCESS(rv
, rv
);
1779 file
.swap(*_retval
);
1784 CacheIndex::RemoveFile(const nsACString
&aName
)
1786 MOZ_ASSERT(mState
== SHUTDOWN
);
1790 nsCOMPtr
<nsIFile
> file
;
1791 rv
= GetFile(aName
, getter_AddRefs(file
));
1792 NS_ENSURE_SUCCESS(rv
, rv
);
1795 rv
= file
->Exists(&exists
);
1796 NS_ENSURE_SUCCESS(rv
, rv
);
1799 rv
= file
->Remove(false);
1800 if (NS_FAILED(rv
)) {
1801 LOG(("CacheIndex::RemoveFile() - Cannot remove old entry file from disk."
1802 "[name=%s]", PromiseFlatCString(aName
).get()));
1803 NS_WARNING("Cannot remove old entry file from the disk");
1812 CacheIndex::RemoveIndexFromDisk()
1814 LOG(("CacheIndex::RemoveIndexFromDisk()"));
1816 RemoveFile(NS_LITERAL_CSTRING(kIndexName
));
1817 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName
));
1818 RemoveFile(NS_LITERAL_CSTRING(kJournalName
));
1821 class WriteLogHelper
1824 explicit WriteLogHelper(PRFileDesc
*aFD
)
1827 , mBufSize(kMaxBufSize
)
1830 mHash
= new CacheHash();
1831 mBuf
= static_cast<char *>(moz_xmalloc(mBufSize
));
1838 nsresult
AddEntry(CacheIndexEntry
*aEntry
);
1843 nsresult
FlushBuffer();
1850 nsRefPtr
<CacheHash
> mHash
;
1854 WriteLogHelper::AddEntry(CacheIndexEntry
*aEntry
)
1858 if (NS_FAILED(mStatus
)) {
1862 if (mBufPos
+ sizeof(CacheIndexRecord
) > mBufSize
) {
1863 mHash
->Update(mBuf
, mBufPos
);
1866 if (NS_FAILED(rv
)) {
1870 MOZ_ASSERT(mBufPos
+ sizeof(CacheIndexRecord
) <= mBufSize
);
1873 aEntry
->WriteToBuf(mBuf
+ mBufPos
);
1874 mBufPos
+= sizeof(CacheIndexRecord
);
1880 WriteLogHelper::Finish()
1884 if (NS_FAILED(mStatus
)) {
1888 mHash
->Update(mBuf
, mBufPos
);
1889 if (mBufPos
+ sizeof(CacheHash::Hash32_t
) > mBufSize
) {
1891 if (NS_FAILED(rv
)) {
1895 MOZ_ASSERT(mBufPos
+ sizeof(CacheHash::Hash32_t
) <= mBufSize
);
1898 NetworkEndian::writeUint32(mBuf
+ mBufPos
, mHash
->GetHash());
1899 mBufPos
+= sizeof(CacheHash::Hash32_t
);
1902 NS_ENSURE_SUCCESS(rv
, rv
);
1904 mStatus
= NS_ERROR_UNEXPECTED
; // Don't allow any other operation
1909 WriteLogHelper::FlushBuffer()
1911 MOZ_ASSERT(NS_SUCCEEDED(mStatus
));
1913 int32_t bytesWritten
= PR_Write(mFD
, mBuf
, mBufPos
);
1915 if (bytesWritten
!= mBufPos
) {
1916 return NS_ERROR_FAILURE
;
1924 CacheIndex::WriteLogToDisk()
1926 LOG(("CacheIndex::WriteLogToDisk()"));
1930 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
1931 MOZ_ASSERT(mState
== SHUTDOWN
);
1933 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName
));
1935 nsCOMPtr
<nsIFile
> indexFile
;
1936 rv
= GetFile(NS_LITERAL_CSTRING(kIndexName
), getter_AddRefs(indexFile
));
1937 NS_ENSURE_SUCCESS(rv
, rv
);
1939 nsCOMPtr
<nsIFile
> logFile
;
1940 rv
= GetFile(NS_LITERAL_CSTRING(kJournalName
), getter_AddRefs(logFile
));
1941 NS_ENSURE_SUCCESS(rv
, rv
);
1945 PRFileDesc
*fd
= nullptr;
1946 rv
= logFile
->OpenNSPRFileDesc(PR_RDWR
| PR_CREATE_FILE
| PR_TRUNCATE
,
1948 NS_ENSURE_SUCCESS(rv
, rv
);
1950 WriteLogHelper
wlh(fd
);
1951 mIndex
.EnumerateEntries(&CacheIndex::WriteEntryToLog
, &wlh
);
1955 NS_ENSURE_SUCCESS(rv
, rv
);
1957 rv
= indexFile
->OpenNSPRFileDesc(PR_RDWR
, 0600, &fd
);
1958 NS_ENSURE_SUCCESS(rv
, rv
);
1960 CacheIndexHeader header
;
1961 int32_t bytesRead
= PR_Read(fd
, &header
, sizeof(CacheIndexHeader
));
1962 if (bytesRead
!= sizeof(CacheIndexHeader
)) {
1964 return NS_ERROR_FAILURE
;
1967 NetworkEndian::writeUint32(&header
.mIsDirty
, 0);
1969 int64_t offset
= PR_Seek64(fd
, 0, PR_SEEK_SET
);
1972 return NS_ERROR_FAILURE
;
1975 int32_t bytesWritten
= PR_Write(fd
, &header
, sizeof(CacheIndexHeader
));
1977 if (bytesWritten
!= sizeof(CacheIndexHeader
)) {
1978 return NS_ERROR_FAILURE
;
1986 CacheIndex::WriteEntryToLog(CacheIndexEntry
*aEntry
, void* aClosure
)
1988 WriteLogHelper
*wlh
= static_cast<WriteLogHelper
*>(aClosure
);
1990 if (aEntry
->IsRemoved() || aEntry
->IsDirty()) {
1991 wlh
->AddEntry(aEntry
);
1994 return PL_DHASH_REMOVE
;
1998 CacheIndex::ReadIndexFromDisk()
2000 LOG(("CacheIndex::ReadIndexFromDisk()"));
2005 MOZ_ASSERT(mState
== INITIAL
);
2007 ChangeState(READING
);
2009 mIndexFileOpener
= new FileOpenHelper(this);
2010 rv
= CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kIndexName
),
2011 CacheFileIOManager::SPECIAL_FILE
|
2012 CacheFileIOManager::OPEN
,
2014 if (NS_FAILED(rv
)) {
2015 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2016 "failed [rv=0x%08x, file=%s]", rv
, kIndexName
));
2021 mJournalFileOpener
= new FileOpenHelper(this);
2022 rv
= CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kJournalName
),
2023 CacheFileIOManager::SPECIAL_FILE
|
2024 CacheFileIOManager::OPEN
,
2025 mJournalFileOpener
);
2026 if (NS_FAILED(rv
)) {
2027 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2028 "failed [rv=0x%08x, file=%s]", rv
, kJournalName
));
2032 mTmpFileOpener
= new FileOpenHelper(this);
2033 rv
= CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(kTempIndexName
),
2034 CacheFileIOManager::SPECIAL_FILE
|
2035 CacheFileIOManager::OPEN
,
2037 if (NS_FAILED(rv
)) {
2038 LOG(("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2039 "failed [rv=0x%08x, file=%s]", rv
, kTempIndexName
));
2045 CacheIndex::StartReadingIndex()
2047 LOG(("CacheIndex::StartReadingIndex()"));
2053 MOZ_ASSERT(mIndexHandle
);
2054 MOZ_ASSERT(mState
== READING
);
2055 MOZ_ASSERT(!mIndexOnDiskIsValid
);
2056 MOZ_ASSERT(!mDontMarkIndexClean
);
2057 MOZ_ASSERT(!mJournalReadSuccessfully
);
2058 MOZ_ASSERT(mIndexHandle
->FileSize() >= 0);
2060 int64_t entriesSize
= mIndexHandle
->FileSize() - sizeof(CacheIndexHeader
) -
2061 sizeof(CacheHash::Hash32_t
);
2063 if (entriesSize
< 0 || entriesSize
% sizeof(CacheIndexRecord
)) {
2064 LOG(("CacheIndex::StartReadingIndex() - Index is corrupted"));
2071 mRWHash
= new CacheHash();
2073 mRWBufPos
= std::min(mRWBufSize
,
2074 static_cast<uint32_t>(mIndexHandle
->FileSize()));
2076 rv
= CacheFileIOManager::Read(mIndexHandle
, 0, mRWBuf
, mRWBufPos
, this);
2077 if (NS_FAILED(rv
)) {
2078 LOG(("CacheIndex::StartReadingIndex() - CacheFileIOManager::Read() failed "
2079 "synchronously [rv=0x%08x]", rv
));
2085 CacheIndex::ParseRecords()
2087 LOG(("CacheIndex::ParseRecords()"));
2093 uint32_t entryCnt
= (mIndexHandle
->FileSize() - sizeof(CacheIndexHeader
) -
2094 sizeof(CacheHash::Hash32_t
)) / sizeof(CacheIndexRecord
);
2097 if (!mSkipEntries
) {
2098 CacheIndexHeader
*hdr
= reinterpret_cast<CacheIndexHeader
*>(
2099 moz_xmalloc(sizeof(CacheIndexHeader
)));
2100 memcpy(hdr
, mRWBuf
, sizeof(CacheIndexHeader
));
2102 if (NetworkEndian::readUint32(&hdr
->mVersion
) != kIndexVersion
) {
2108 mIndexTimeStamp
= NetworkEndian::readUint32(&hdr
->mTimeStamp
);
2110 if (NetworkEndian::readUint32(&hdr
->mIsDirty
)) {
2111 if (mJournalHandle
) {
2112 CacheFileIOManager::DoomFile(mJournalHandle
, nullptr);
2113 mJournalHandle
= nullptr;
2117 NetworkEndian::writeUint32(&hdr
->mIsDirty
, 1);
2119 // Mark index dirty. The buffer is freed by CacheFileIOManager when
2120 // nullptr is passed as the listener and the call doesn't fail
2122 rv
= CacheFileIOManager::Write(mIndexHandle
, 0,
2123 reinterpret_cast<char *>(hdr
),
2124 sizeof(CacheIndexHeader
), true, nullptr);
2125 if (NS_FAILED(rv
)) {
2126 // This is not fatal, just free the memory
2131 pos
+= sizeof(CacheIndexHeader
);
2134 uint32_t hashOffset
= pos
;
2136 while (pos
+ sizeof(CacheIndexRecord
) <= mRWBufPos
&&
2137 mSkipEntries
!= entryCnt
) {
2138 CacheIndexRecord
*rec
= reinterpret_cast<CacheIndexRecord
*>(mRWBuf
+ pos
);
2139 CacheIndexEntry
tmpEntry(&rec
->mHash
);
2140 tmpEntry
.ReadFromBuf(mRWBuf
+ pos
);
2142 if (tmpEntry
.IsDirty() || !tmpEntry
.IsInitialized() ||
2143 tmpEntry
.IsFileEmpty() || tmpEntry
.IsFresh() || tmpEntry
.IsRemoved()) {
2144 LOG(("CacheIndex::ParseRecords() - Invalid entry found in index, removing"
2145 " whole index [dirty=%d, initialized=%d, fileEmpty=%d, fresh=%d, "
2146 "removed=%d]", tmpEntry
.IsDirty(), tmpEntry
.IsInitialized(),
2147 tmpEntry
.IsFileEmpty(), tmpEntry
.IsFresh(), tmpEntry
.IsRemoved()));
2152 CacheIndexEntryAutoManage
emng(tmpEntry
.Hash(), this);
2154 CacheIndexEntry
*entry
= mIndex
.PutEntry(*tmpEntry
.Hash());
2157 pos
+= sizeof(CacheIndexRecord
);
2161 mRWHash
->Update(mRWBuf
+ hashOffset
, pos
- hashOffset
);
2163 if (pos
!= mRWBufPos
) {
2164 memmove(mRWBuf
, mRWBuf
+ pos
, mRWBufPos
- pos
);
2169 int64_t fileOffset
= sizeof(CacheIndexHeader
) +
2170 mSkipEntries
* sizeof(CacheIndexRecord
) + mRWBufPos
;
2172 MOZ_ASSERT(fileOffset
<= mIndexHandle
->FileSize());
2173 if (fileOffset
== mIndexHandle
->FileSize()) {
2174 if (mRWHash
->GetHash() != NetworkEndian::readUint32(mRWBuf
)) {
2175 LOG(("CacheIndex::ParseRecords() - Hash mismatch, [is %x, should be %x]",
2177 NetworkEndian::readUint32(mRWBuf
)));
2182 mIndexOnDiskIsValid
= true;
2183 mJournalReadSuccessfully
= false;
2185 if (mJournalHandle
) {
2186 StartReadingJournal();
2195 uint32_t toRead
= std::min(mRWBufSize
- pos
,
2196 static_cast<uint32_t>(mIndexHandle
->FileSize() -
2198 mRWBufPos
= pos
+ toRead
;
2200 rv
= CacheFileIOManager::Read(mIndexHandle
, fileOffset
, mRWBuf
+ pos
, toRead
,
2202 if (NS_FAILED(rv
)) {
2203 LOG(("CacheIndex::ParseRecords() - CacheFileIOManager::Read() failed "
2204 "synchronously [rv=0x%08x]", rv
));
2211 CacheIndex::StartReadingJournal()
2213 LOG(("CacheIndex::StartReadingJournal()"));
2219 MOZ_ASSERT(mJournalHandle
);
2220 MOZ_ASSERT(mIndexOnDiskIsValid
);
2221 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2222 MOZ_ASSERT(mJournalHandle
->FileSize() >= 0);
2224 int64_t entriesSize
= mJournalHandle
->FileSize() -
2225 sizeof(CacheHash::Hash32_t
);
2227 if (entriesSize
< 0 || entriesSize
% sizeof(CacheIndexRecord
)) {
2228 LOG(("CacheIndex::StartReadingJournal() - Journal is corrupted"));
2234 mRWHash
= new CacheHash();
2236 mRWBufPos
= std::min(mRWBufSize
,
2237 static_cast<uint32_t>(mJournalHandle
->FileSize()));
2239 rv
= CacheFileIOManager::Read(mJournalHandle
, 0, mRWBuf
, mRWBufPos
, this);
2240 if (NS_FAILED(rv
)) {
2241 LOG(("CacheIndex::StartReadingJournal() - CacheFileIOManager::Read() failed"
2242 " synchronously [rv=0x%08x]", rv
));
2248 CacheIndex::ParseJournal()
2250 LOG(("CacheIndex::ParseRecords()"));
2256 uint32_t entryCnt
= (mJournalHandle
->FileSize() -
2257 sizeof(CacheHash::Hash32_t
)) / sizeof(CacheIndexRecord
);
2261 while (pos
+ sizeof(CacheIndexRecord
) <= mRWBufPos
&&
2262 mSkipEntries
!= entryCnt
) {
2263 CacheIndexRecord
*rec
= reinterpret_cast<CacheIndexRecord
*>(mRWBuf
+ pos
);
2264 CacheIndexEntry
tmpEntry(&rec
->mHash
);
2265 tmpEntry
.ReadFromBuf(mRWBuf
+ pos
);
2267 CacheIndexEntry
*entry
= mTmpJournal
.PutEntry(*tmpEntry
.Hash());
2270 if (entry
->IsDirty() || entry
->IsFresh()) {
2271 LOG(("CacheIndex::ParseJournal() - Invalid entry found in journal, "
2272 "ignoring whole journal [dirty=%d, fresh=%d]", entry
->IsDirty(),
2278 pos
+= sizeof(CacheIndexRecord
);
2282 mRWHash
->Update(mRWBuf
, pos
);
2284 if (pos
!= mRWBufPos
) {
2285 memmove(mRWBuf
, mRWBuf
+ pos
, mRWBufPos
- pos
);
2290 int64_t fileOffset
= mSkipEntries
* sizeof(CacheIndexRecord
) + mRWBufPos
;
2292 MOZ_ASSERT(fileOffset
<= mJournalHandle
->FileSize());
2293 if (fileOffset
== mJournalHandle
->FileSize()) {
2294 if (mRWHash
->GetHash() != NetworkEndian::readUint32(mRWBuf
)) {
2295 LOG(("CacheIndex::ParseJournal() - Hash mismatch, [is %x, should be %x]",
2297 NetworkEndian::readUint32(mRWBuf
)));
2302 mJournalReadSuccessfully
= true;
2308 uint32_t toRead
= std::min(mRWBufSize
- pos
,
2309 static_cast<uint32_t>(mJournalHandle
->FileSize() -
2311 mRWBufPos
= pos
+ toRead
;
2313 rv
= CacheFileIOManager::Read(mJournalHandle
, fileOffset
, mRWBuf
+ pos
,
2315 if (NS_FAILED(rv
)) {
2316 LOG(("CacheIndex::ParseJournal() - CacheFileIOManager::Read() failed "
2317 "synchronously [rv=0x%08x]", rv
));
2324 CacheIndex::MergeJournal()
2326 LOG(("CacheIndex::MergeJournal()"));
2330 mTmpJournal
.EnumerateEntries(&CacheIndex::ProcessJournalEntry
, this);
2332 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2337 CacheIndex::ProcessJournalEntry(CacheIndexEntry
*aEntry
, void* aClosure
)
2339 CacheIndex
*index
= static_cast<CacheIndex
*>(aClosure
);
2341 LOG(("CacheIndex::ProcessJournalEntry() [hash=%08x%08x%08x%08x%08x]",
2342 LOGSHA1(aEntry
->Hash())));
2344 CacheIndexEntry
*entry
= index
->mIndex
.GetEntry(*aEntry
->Hash());
2346 CacheIndexEntryAutoManage
emng(aEntry
->Hash(), index
);
2348 if (aEntry
->IsRemoved()) {
2350 entry
->MarkRemoved();
2355 entry
= index
->mIndex
.PutEntry(*aEntry
->Hash());
2362 return PL_DHASH_REMOVE
;
2366 CacheIndex::EnsureNoFreshEntry()
2369 CacheIndexStats debugStats
;
2370 debugStats
.DisableLogging();
2371 mIndex
.EnumerateEntries(&CacheIndex::SumIndexStats
, &debugStats
);
2372 MOZ_ASSERT(debugStats
.Fresh() == 0);
2377 CacheIndex::EnsureCorrectStats()
2380 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2381 CacheIndexStats debugStats
;
2382 debugStats
.DisableLogging();
2383 mIndex
.EnumerateEntries(&CacheIndex::SumIndexStats
, &debugStats
);
2384 MOZ_ASSERT(debugStats
== mIndexStats
);
2390 CacheIndex::SumIndexStats(CacheIndexEntry
*aEntry
, void* aClosure
)
2392 CacheIndexStats
*stats
= static_cast<CacheIndexStats
*>(aClosure
);
2393 stats
->BeforeChange(nullptr);
2394 stats
->AfterChange(aEntry
);
2395 return PL_DHASH_NEXT
;
2399 CacheIndex::FinishRead(bool aSucceeded
)
2401 LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded
));
2404 MOZ_ASSERT((!aSucceeded
&& mState
== SHUTDOWN
) || mState
== READING
);
2408 (!aSucceeded
&& !mIndexOnDiskIsValid
&& !mJournalReadSuccessfully
) ||
2410 (!aSucceeded
&& mIndexOnDiskIsValid
&& !mJournalReadSuccessfully
) ||
2412 (aSucceeded
&& mIndexOnDiskIsValid
&& mJournalReadSuccessfully
));
2414 if (mState
== SHUTDOWN
) {
2415 RemoveFile(NS_LITERAL_CSTRING(kTempIndexName
));
2416 RemoveFile(NS_LITERAL_CSTRING(kJournalName
));
2418 if (mIndexHandle
&& !mIndexOnDiskIsValid
) {
2419 CacheFileIOManager::DoomFile(mIndexHandle
, nullptr);
2422 if (mJournalHandle
) {
2423 CacheFileIOManager::DoomFile(mJournalHandle
, nullptr);
2427 if (mIndexFileOpener
) {
2428 mIndexFileOpener
->Cancel();
2429 mIndexFileOpener
= nullptr;
2431 if (mJournalFileOpener
) {
2432 mJournalFileOpener
->Cancel();
2433 mJournalFileOpener
= nullptr;
2435 if (mTmpFileOpener
) {
2436 mTmpFileOpener
->Cancel();
2437 mTmpFileOpener
= nullptr;
2440 mIndexHandle
= nullptr;
2441 mJournalHandle
= nullptr;
2445 if (mState
== SHUTDOWN
) {
2449 if (!mIndexOnDiskIsValid
) {
2450 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2451 EnsureNoFreshEntry();
2452 ProcessPendingOperations();
2453 // Remove all entries that we haven't seen during this session
2454 mIndex
.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries
, this);
2455 StartUpdatingIndex(true);
2459 if (!mJournalReadSuccessfully
) {
2460 mTmpJournal
.Clear();
2461 EnsureNoFreshEntry();
2462 ProcessPendingOperations();
2463 StartUpdatingIndex(false);
2468 EnsureNoFreshEntry();
2469 ProcessPendingOperations();
2473 mLastDumpTime
= TimeStamp::NowLoRes(); // Do not dump new index immediately
2478 CacheIndex::DelayedUpdate(nsITimer
*aTimer
, void *aClosure
)
2480 LOG(("CacheIndex::DelayedUpdate()"));
2483 nsRefPtr
<CacheIndex
> index
= gInstance
;
2489 CacheIndexAutoLock
lock(index
);
2491 index
->mUpdateTimer
= nullptr;
2493 if (!index
->IsIndexUsable()) {
2497 if (index
->mState
== READY
&& index
->mShuttingDown
) {
2501 // mUpdateEventPending must be false here since StartUpdatingIndex() won't
2502 // schedule timer if it is true.
2503 MOZ_ASSERT(!index
->mUpdateEventPending
);
2504 if (index
->mState
!= BUILDING
&& index
->mState
!= UPDATING
) {
2505 LOG(("CacheIndex::DelayedUpdate() - Update was canceled"));
2509 // We need to redispatch to run with lower priority
2510 nsRefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
2511 MOZ_ASSERT(ioThread
);
2513 index
->mUpdateEventPending
= true;
2514 rv
= ioThread
->Dispatch(index
, CacheIOThread::INDEX
);
2515 if (NS_FAILED(rv
)) {
2516 index
->mUpdateEventPending
= false;
2517 NS_WARNING("CacheIndex::DelayedUpdate() - Can't dispatch event");
2518 LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event" ));
2519 index
->FinishUpdate(false);
2524 CacheIndex::ScheduleUpdateTimer(uint32_t aDelay
)
2526 LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay
));
2528 MOZ_ASSERT(!mUpdateTimer
);
2532 nsCOMPtr
<nsITimer
> timer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
2533 NS_ENSURE_SUCCESS(rv
, rv
);
2535 nsCOMPtr
<nsIEventTarget
> ioTarget
= CacheFileIOManager::IOTarget();
2536 MOZ_ASSERT(ioTarget
);
2538 rv
= timer
->SetTarget(ioTarget
);
2539 NS_ENSURE_SUCCESS(rv
, rv
);
2541 rv
= timer
->InitWithFuncCallback(CacheIndex::DelayedUpdate
, nullptr,
2542 aDelay
, nsITimer::TYPE_ONE_SHOT
);
2543 NS_ENSURE_SUCCESS(rv
, rv
);
2545 mUpdateTimer
.swap(timer
);
2550 CacheIndex::SetupDirectoryEnumerator()
2552 MOZ_ASSERT(!NS_IsMainThread());
2553 MOZ_ASSERT(!mDirEnumerator
);
2556 nsCOMPtr
<nsIFile
> file
;
2558 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
2559 NS_ENSURE_SUCCESS(rv
, rv
);
2561 rv
= file
->AppendNative(NS_LITERAL_CSTRING(kEntriesDir
));
2562 NS_ENSURE_SUCCESS(rv
, rv
);
2565 rv
= file
->Exists(&exists
);
2566 NS_ENSURE_SUCCESS(rv
, rv
);
2569 NS_WARNING("CacheIndex::SetupDirectoryEnumerator() - Entries directory "
2571 LOG(("CacheIndex::SetupDirectoryEnumerator() - Entries directory doesn't "
2573 return NS_ERROR_UNEXPECTED
;
2576 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
2577 rv
= file
->GetDirectoryEntries(getter_AddRefs(enumerator
));
2578 NS_ENSURE_SUCCESS(rv
, rv
);
2580 mDirEnumerator
= do_QueryInterface(enumerator
, &rv
);
2581 NS_ENSURE_SUCCESS(rv
, rv
);
2587 CacheIndex::InitEntryFromDiskData(CacheIndexEntry
*aEntry
,
2588 CacheFileMetadata
*aMetaData
,
2592 aEntry
->MarkDirty();
2593 aEntry
->MarkFresh();
2594 aEntry
->Init(aMetaData
->AppId(), aMetaData
->IsAnonymous(),
2595 aMetaData
->IsInBrowser());
2597 uint32_t expirationTime
;
2598 aMetaData
->GetExpirationTime(&expirationTime
);
2599 aEntry
->SetExpirationTime(expirationTime
);
2602 aMetaData
->GetFrecency(&frecency
);
2603 aEntry
->SetFrecency(frecency
);
2605 aEntry
->SetFileSize(static_cast<uint32_t>(
2606 std::min(static_cast<int64_t>(PR_UINT32_MAX
),
2607 (aFileSize
+ 0x3FF) >> 10)));
2611 CacheIndex::IsUpdatePending()
2615 if (mUpdateTimer
|| mUpdateEventPending
) {
2623 CacheIndex::BuildIndex()
2625 LOG(("CacheIndex::BuildIndex()"));
2629 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2633 if (!mDirEnumerator
) {
2635 // Do not do IO under the lock.
2636 CacheIndexAutoUnlock
unlock(this);
2637 rv
= SetupDirectoryEnumerator();
2639 if (mState
== SHUTDOWN
) {
2640 // The index was shut down while we released the lock. FinishUpdate() was
2641 // already called from Shutdown(), so just simply return here.
2645 if (NS_FAILED(rv
)) {
2646 FinishUpdate(false);
2652 if (CacheIOThread::YieldAndRerun()) {
2653 LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
2654 mUpdateEventPending
= true;
2658 nsCOMPtr
<nsIFile
> file
;
2660 // Do not do IO under the lock.
2661 CacheIndexAutoUnlock
unlock(this);
2662 rv
= mDirEnumerator
->GetNextFile(getter_AddRefs(file
));
2664 if (mState
== SHUTDOWN
) {
2668 FinishUpdate(NS_SUCCEEDED(rv
));
2673 rv
= file
->GetNativeLeafName(leaf
);
2674 if (NS_FAILED(rv
)) {
2675 LOG(("CacheIndex::BuildIndex() - GetNativeLeafName() failed! Skipping "
2677 mDontMarkIndexClean
= true;
2682 rv
= CacheFileIOManager::StrToHash(leaf
, &hash
);
2683 if (NS_FAILED(rv
)) {
2684 LOG(("CacheIndex::BuildIndex() - Filename is not a hash, removing file. "
2685 "[name=%s]", leaf
.get()));
2686 file
->Remove(false);
2690 CacheIndexEntry
*entry
= mIndex
.GetEntry(hash
);
2691 if (entry
&& entry
->IsRemoved()) {
2692 LOG(("CacheIndex::BuildIndex() - Found file that should not exist. "
2693 "[name=%s]", leaf
.get()));
2695 MOZ_ASSERT(entry
->IsFresh());
2700 nsRefPtr
<CacheFileHandle
> handle
;
2701 CacheFileIOManager::gInstance
->mHandles
.GetHandle(&hash
, false,
2702 getter_AddRefs(handle
));
2706 // the entry is up to date
2707 LOG(("CacheIndex::BuildIndex() - Skipping file because the entry is up to"
2708 " date. [name=%s]", leaf
.get()));
2710 MOZ_ASSERT(entry
->IsFresh()); // The entry must be from this session
2711 // there must be an active CacheFile if the entry is not initialized
2712 MOZ_ASSERT(entry
->IsInitialized() || handle
);
2716 MOZ_ASSERT(!handle
);
2718 nsRefPtr
<CacheFileMetadata
> meta
= new CacheFileMetadata();
2722 // Do not do IO under the lock.
2723 CacheIndexAutoUnlock
unlock(this);
2724 rv
= meta
->SyncReadMetadata(file
);
2726 if (NS_SUCCEEDED(rv
)) {
2727 rv
= file
->GetFileSize(&size
);
2728 if (NS_FAILED(rv
)) {
2729 LOG(("CacheIndex::BuildIndex() - Cannot get filesize of file that was"
2730 " successfully parsed. [name=%s]", leaf
.get()));
2734 if (mState
== SHUTDOWN
) {
2738 // Nobody could add the entry while the lock was released since we modify
2739 // the index only on IO thread and this loop is executed on IO thread too.
2740 entry
= mIndex
.GetEntry(hash
);
2741 MOZ_ASSERT(!entry
|| entry
->IsRemoved());
2743 if (NS_FAILED(rv
)) {
2744 LOG(("CacheIndex::BuildIndex() - CacheFileMetadata::SyncReadMetadata() "
2745 "failed, removing file. [name=%s]", leaf
.get()));
2746 file
->Remove(false);
2748 CacheIndexEntryAutoManage
entryMng(&hash
, this);
2749 entry
= mIndex
.PutEntry(hash
);
2750 InitEntryFromDiskData(entry
, meta
, size
);
2751 LOG(("CacheIndex::BuildIndex() - Added entry to index. [hash=%s]",
2757 NS_NOTREACHED("We should never get here");
2761 CacheIndex::StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState
)
2763 // Start updating process when we are in or we are switching to READY state
2764 // and index needs update, but not during shutdown or when removing all
2766 if ((mState
== READY
|| aSwitchingToReadyState
) && mIndexNeedsUpdate
&&
2767 !mShuttingDown
&& !mRemovingAll
) {
2768 LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process"));
2769 mIndexNeedsUpdate
= false;
2770 StartUpdatingIndex(false);
2778 CacheIndex::StartUpdatingIndex(bool aRebuild
)
2780 LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild
));
2788 ChangeState(aRebuild
? BUILDING
: UPDATING
);
2789 mDontMarkIndexClean
= false;
2791 if (mShuttingDown
|| mRemovingAll
) {
2792 FinishUpdate(false);
2796 if (IsUpdatePending()) {
2797 LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending"));
2801 uint32_t elapsed
= (TimeStamp::NowLoRes() - mStartTime
).ToMilliseconds();
2802 if (elapsed
< kUpdateIndexStartDelay
) {
2803 LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
2804 "scheduling timer to fire in %u ms.", elapsed
,
2805 kUpdateIndexStartDelay
- elapsed
));
2806 rv
= ScheduleUpdateTimer(kUpdateIndexStartDelay
- elapsed
);
2807 if (NS_SUCCEEDED(rv
)) {
2811 LOG(("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. "
2812 "Starting update immediately."));
2814 LOG(("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
2815 "starting update now.", elapsed
));
2818 nsRefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
2819 MOZ_ASSERT(ioThread
);
2821 // We need to dispatch an event even if we are on IO thread since we need to
2822 // update the index with the correct priority.
2823 mUpdateEventPending
= true;
2824 rv
= ioThread
->Dispatch(this, CacheIOThread::INDEX
);
2825 if (NS_FAILED(rv
)) {
2826 mUpdateEventPending
= false;
2827 NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event");
2828 LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event" ));
2829 FinishUpdate(false);
2834 CacheIndex::UpdateIndex()
2836 LOG(("CacheIndex::UpdateIndex()"));
2840 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2844 if (!mDirEnumerator
) {
2846 // Do not do IO under the lock.
2847 CacheIndexAutoUnlock
unlock(this);
2848 rv
= SetupDirectoryEnumerator();
2850 if (mState
== SHUTDOWN
) {
2851 // The index was shut down while we released the lock. FinishUpdate() was
2852 // already called from Shutdown(), so just simply return here.
2856 if (NS_FAILED(rv
)) {
2857 FinishUpdate(false);
2863 if (CacheIOThread::YieldAndRerun()) {
2864 LOG(("CacheIndex::UpdateIndex() - Breaking loop for higher level "
2866 mUpdateEventPending
= true;
2870 nsCOMPtr
<nsIFile
> file
;
2872 // Do not do IO under the lock.
2873 CacheIndexAutoUnlock
unlock(this);
2874 rv
= mDirEnumerator
->GetNextFile(getter_AddRefs(file
));
2876 if (mState
== SHUTDOWN
) {
2880 FinishUpdate(NS_SUCCEEDED(rv
));
2885 rv
= file
->GetNativeLeafName(leaf
);
2886 if (NS_FAILED(rv
)) {
2887 LOG(("CacheIndex::UpdateIndex() - GetNativeLeafName() failed! Skipping "
2889 mDontMarkIndexClean
= true;
2894 rv
= CacheFileIOManager::StrToHash(leaf
, &hash
);
2895 if (NS_FAILED(rv
)) {
2896 LOG(("CacheIndex::UpdateIndex() - Filename is not a hash, removing file. "
2897 "[name=%s]", leaf
.get()));
2898 file
->Remove(false);
2902 CacheIndexEntry
*entry
= mIndex
.GetEntry(hash
);
2903 if (entry
&& entry
->IsRemoved()) {
2904 if (entry
->IsFresh()) {
2905 LOG(("CacheIndex::UpdateIndex() - Found file that should not exist. "
2906 "[name=%s]", leaf
.get()));
2913 nsRefPtr
<CacheFileHandle
> handle
;
2914 CacheFileIOManager::gInstance
->mHandles
.GetHandle(&hash
, false,
2915 getter_AddRefs(handle
));
2918 if (entry
&& entry
->IsFresh()) {
2919 // the entry is up to date
2920 LOG(("CacheIndex::UpdateIndex() - Skipping file because the entry is up "
2921 " to date. [name=%s]", leaf
.get()));
2923 // there must be an active CacheFile if the entry is not initialized
2924 MOZ_ASSERT(entry
->IsInitialized() || handle
);
2928 MOZ_ASSERT(!handle
);
2931 PRTime lastModifiedTime
;
2933 // Do not do IO under the lock.
2934 CacheIndexAutoUnlock
unlock(this);
2935 rv
= file
->GetLastModifiedTime(&lastModifiedTime
);
2937 if (mState
== SHUTDOWN
) {
2940 if (NS_FAILED(rv
)) {
2941 LOG(("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. "
2942 "[name=%s]", leaf
.get()));
2943 // Assume the file is newer than index
2945 if (mIndexTimeStamp
> (lastModifiedTime
/ PR_MSEC_PER_SEC
)) {
2946 LOG(("CacheIndex::UpdateIndex() - Skipping file because of last "
2947 "modified time. [name=%s, indexTimeStamp=%u, "
2948 "lastModifiedTime=%u]", leaf
.get(), mIndexTimeStamp
,
2949 lastModifiedTime
/ PR_MSEC_PER_SEC
));
2951 CacheIndexEntryAutoManage
entryMng(&hash
, this);
2958 nsRefPtr
<CacheFileMetadata
> meta
= new CacheFileMetadata();
2962 // Do not do IO under the lock.
2963 CacheIndexAutoUnlock
unlock(this);
2964 rv
= meta
->SyncReadMetadata(file
);
2966 if (NS_SUCCEEDED(rv
)) {
2967 rv
= file
->GetFileSize(&size
);
2968 if (NS_FAILED(rv
)) {
2969 LOG(("CacheIndex::UpdateIndex() - Cannot get filesize of file that "
2970 "was successfully parsed. [name=%s]", leaf
.get()));
2974 if (mState
== SHUTDOWN
) {
2978 // Nobody could add the entry while the lock was released since we modify
2979 // the index only on IO thread and this loop is executed on IO thread too.
2980 entry
= mIndex
.GetEntry(hash
);
2981 MOZ_ASSERT(!entry
|| !entry
->IsFresh());
2983 CacheIndexEntryAutoManage
entryMng(&hash
, this);
2985 if (NS_FAILED(rv
)) {
2986 LOG(("CacheIndex::UpdateIndex() - CacheFileMetadata::SyncReadMetadata() "
2987 "failed, removing file. [name=%s]", leaf
.get()));
2988 file
->Remove(false);
2990 entry
->MarkRemoved();
2995 entry
= mIndex
.PutEntry(hash
);
2996 InitEntryFromDiskData(entry
, meta
, size
);
2997 LOG(("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
2998 "[hash=%s]", leaf
.get()));
3003 NS_NOTREACHED("We should never get here");
3007 CacheIndex::FinishUpdate(bool aSucceeded
)
3009 LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded
));
3011 MOZ_ASSERT(mState
== UPDATING
|| mState
== BUILDING
||
3012 (!aSucceeded
&& mState
== SHUTDOWN
));
3016 if (mDirEnumerator
) {
3017 if (NS_IsMainThread()) {
3018 LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
3019 " Cannot safely release mDirEnumerator, leaking it!"));
3020 NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
3021 // This can happen only in case dispatching event to IO thread failed in
3022 // CacheIndex::PreShutdown().
3023 mDirEnumerator
.forget(); // Leak it since dir enumerator is not threadsafe
3025 mDirEnumerator
->Close();
3026 mDirEnumerator
= nullptr;
3031 mDontMarkIndexClean
= true;
3034 if (mState
== SHUTDOWN
) {
3038 if (mState
== UPDATING
&& aSucceeded
) {
3039 // If we've iterated over all entries successfully then all entries that
3040 // really exist on the disk are now marked as fresh. All non-fresh entries
3041 // don't exist anymore and must be removed from the index.
3042 mIndex
.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries
, this);
3045 // Make sure we won't start update. If the build or update failed, there is no
3046 // reason to believe that it will succeed next time.
3047 mIndexNeedsUpdate
= false;
3050 mLastDumpTime
= TimeStamp::NowLoRes(); // Do not dump new index immediately
3055 CacheIndex::RemoveNonFreshEntries(CacheIndexEntry
*aEntry
, void* aClosure
)
3057 if (aEntry
->IsFresh()) {
3058 return PL_DHASH_NEXT
;
3061 LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. "
3062 "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry
->Hash())));
3064 CacheIndex
*index
= static_cast<CacheIndex
*>(aClosure
);
3066 CacheIndexEntryAutoManage
emng(aEntry
->Hash(), index
);
3067 emng
.DoNotSearchInIndex();
3069 return PL_DHASH_REMOVE
;
3075 CacheIndex::StateString(EState aState
)
3078 case INITIAL
: return "INITIAL";
3079 case READING
: return "READING";
3080 case WRITING
: return "WRITING";
3081 case BUILDING
: return "BUILDING";
3082 case UPDATING
: return "UPDATING";
3083 case READY
: return "READY";
3084 case SHUTDOWN
: return "SHUTDOWN";
3087 MOZ_ASSERT(false, "Unexpected state!");
3093 CacheIndex::ChangeState(EState aNewState
)
3095 LOG(("CacheIndex::ChangeState() changing state %s -> %s", StateString(mState
),
3096 StateString(aNewState
)));
3098 // All pending updates should be processed before changing state
3099 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
3101 // PreShutdownInternal() should change the state to READY from every state. It
3102 // may go through different states, but once we are in READY state the only
3103 // possible transition is to SHUTDOWN state.
3104 MOZ_ASSERT(!mShuttingDown
|| mState
!= READY
|| aNewState
== SHUTDOWN
);
3106 // Start updating process when switching to READY state if needed
3107 if (aNewState
== READY
&& StartUpdatingIndexIfNeeded(true)) {
3111 // Try to evict entries over limit everytime we're leaving state READING,
3112 // BUILDING or UPDATING, but not during shutdown or when removing all
3114 if (!mShuttingDown
&& !mRemovingAll
&& aNewState
!= SHUTDOWN
&&
3115 (mState
== READING
|| mState
== BUILDING
|| mState
== UPDATING
)) {
3116 CacheFileIOManager::EvictIfOverLimit();
3121 if (mState
!= SHUTDOWN
) {
3122 CacheFileIOManager::CacheIndexStateChanged();
3125 if (mState
== READY
&& mDiskConsumptionObservers
.Length()) {
3126 for (uint32_t i
= 0; i
< mDiskConsumptionObservers
.Length(); ++i
) {
3127 DiskConsumptionObserver
* o
= mDiskConsumptionObservers
[i
];
3128 // Safe to call under the lock. We always post to the main thread.
3129 o
->OnDiskConsumption(mIndexStats
.Size() << 10);
3132 mDiskConsumptionObservers
.Clear();
3137 CacheIndex::AllocBuffer()
3141 mRWBufSize
= sizeof(CacheIndexHeader
) + sizeof(CacheHash::Hash32_t
) +
3142 mProcessEntries
* sizeof(CacheIndexRecord
);
3143 if (mRWBufSize
> kMaxBufSize
) {
3144 mRWBufSize
= kMaxBufSize
;
3148 mRWBufSize
= kMaxBufSize
;
3151 MOZ_ASSERT(false, "Unexpected state!");
3154 mRWBuf
= static_cast<char *>(moz_xmalloc(mRWBufSize
));
3158 CacheIndex::ReleaseBuffer()
3172 class FrecencyComparator
3175 bool Equals(CacheIndexRecord
* a
, CacheIndexRecord
* b
) const {
3176 return a
->mFrecency
== b
->mFrecency
;
3178 bool LessThan(CacheIndexRecord
* a
, CacheIndexRecord
* b
) const {
3179 // Place entries with frecency 0 at the end of the array.
3180 if (a
->mFrecency
== 0) {
3183 if (b
->mFrecency
== 0) {
3186 return a
->mFrecency
< b
->mFrecency
;
3190 class ExpirationComparator
3193 bool Equals(CacheIndexRecord
* a
, CacheIndexRecord
* b
) const {
3194 return a
->mExpirationTime
== b
->mExpirationTime
;
3196 bool LessThan(CacheIndexRecord
* a
, CacheIndexRecord
* b
) const {
3197 return a
->mExpirationTime
< b
->mExpirationTime
;
3204 CacheIndex::InsertRecordToFrecencyArray(CacheIndexRecord
*aRecord
)
3206 LOG(("CacheIndex::InsertRecordToFrecencyArray() [record=%p, hash=%08x%08x%08x"
3207 "%08x%08x]", aRecord
, LOGSHA1(aRecord
->mHash
)));
3209 MOZ_ASSERT(!mFrecencyArray
.Contains(aRecord
));
3210 mFrecencyArray
.InsertElementSorted(aRecord
, FrecencyComparator());
3214 CacheIndex::InsertRecordToExpirationArray(CacheIndexRecord
*aRecord
)
3216 LOG(("CacheIndex::InsertRecordToExpirationArray() [record=%p, hash=%08x%08x"
3217 "%08x%08x%08x]", aRecord
, LOGSHA1(aRecord
->mHash
)));
3219 MOZ_ASSERT(!mExpirationArray
.Contains(aRecord
));
3220 mExpirationArray
.InsertElementSorted(aRecord
, ExpirationComparator());
3224 CacheIndex::RemoveRecordFromFrecencyArray(CacheIndexRecord
*aRecord
)
3226 LOG(("CacheIndex::RemoveRecordFromFrecencyArray() [record=%p]", aRecord
));
3228 DebugOnly
<bool> removed
;
3229 removed
= mFrecencyArray
.RemoveElement(aRecord
);
3230 MOZ_ASSERT(removed
);
3234 CacheIndex::RemoveRecordFromExpirationArray(CacheIndexRecord
*aRecord
)
3236 LOG(("CacheIndex::RemoveRecordFromExpirationArray() [record=%p]", aRecord
));
3238 DebugOnly
<bool> removed
;
3239 removed
= mExpirationArray
.RemoveElement(aRecord
);
3240 MOZ_ASSERT(removed
);
3244 CacheIndex::AddRecordToIterators(CacheIndexRecord
*aRecord
)
3248 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3249 // Add a new record only when iterator is supposed to be updated.
3250 if (mIterators
[i
]->ShouldBeNewAdded()) {
3251 mIterators
[i
]->AddRecord(aRecord
);
3257 CacheIndex::RemoveRecordFromIterators(CacheIndexRecord
*aRecord
)
3261 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3262 // Remove the record from iterator always, it makes no sence to return
3263 // non-existing entries. Also the pointer to the record is no longer valid
3264 // once the entry is removed from index.
3265 mIterators
[i
]->RemoveRecord(aRecord
);
3270 CacheIndex::ReplaceRecordInIterators(CacheIndexRecord
*aOldRecord
,
3271 CacheIndexRecord
*aNewRecord
)
3275 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3276 // We have to replace the record always since the pointer is no longer
3277 // valid after this point. NOTE: Replacing the record doesn't mean that
3278 // a new entry was added, it just means that the data in the entry was
3279 // changed (e.g. a file size) and we had to track this change in
3280 // mPendingUpdates since mIndex was read-only.
3281 mIterators
[i
]->ReplaceRecord(aOldRecord
, aNewRecord
);
3288 LOG(("CacheIndex::Run()"));
3290 CacheIndexAutoLock
lock(this);
3292 if (!IsIndexUsable()) {
3293 return NS_ERROR_NOT_AVAILABLE
;
3296 if (mState
== READY
&& mShuttingDown
) {
3300 mUpdateEventPending
= false;
3310 LOG(("CacheIndex::Run() - Update/Build was canceled"));
3317 CacheIndex::OnFileOpenedInternal(FileOpenHelper
*aOpener
,
3318 CacheFileHandle
*aHandle
, nsresult aResult
)
3320 LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
3321 "result=0x%08x]", aOpener
, aHandle
, aResult
));
3327 if (!IsIndexUsable()) {
3328 return NS_ERROR_NOT_AVAILABLE
;
3331 if (mState
== READY
&& mShuttingDown
) {
3337 MOZ_ASSERT(aOpener
== mIndexFileOpener
);
3338 mIndexFileOpener
= nullptr;
3340 if (NS_FAILED(aResult
)) {
3341 LOG(("CacheIndex::OnFileOpenedInternal() - Can't open index file for "
3342 "writing [rv=0x%08x]", aResult
));
3345 mIndexHandle
= aHandle
;
3350 if (aOpener
== mIndexFileOpener
) {
3351 mIndexFileOpener
= nullptr;
3353 if (NS_SUCCEEDED(aResult
)) {
3354 if (aHandle
->FileSize() == 0) {
3356 CacheFileIOManager::DoomFile(aHandle
, nullptr);
3359 mIndexHandle
= aHandle
;
3365 } else if (aOpener
== mJournalFileOpener
) {
3366 mJournalFileOpener
= nullptr;
3367 mJournalHandle
= aHandle
;
3368 } else if (aOpener
== mTmpFileOpener
) {
3369 mTmpFileOpener
= nullptr;
3370 mTmpHandle
= aHandle
;
3372 MOZ_ASSERT(false, "Unexpected state!");
3375 if (mIndexFileOpener
|| mJournalFileOpener
|| mTmpFileOpener
) {
3376 // Some opener still didn't finish
3380 // We fail and cancel all other openers when we opening index file fails.
3381 MOZ_ASSERT(mIndexHandle
);
3384 CacheFileIOManager::DoomFile(mTmpHandle
, nullptr);
3385 mTmpHandle
= nullptr;
3387 if (mJournalHandle
) { // this shouldn't normally happen
3388 LOG(("CacheIndex::OnFileOpenedInternal() - Unexpected state, all "
3389 "files [%s, %s, %s] should never exist. Removing whole index.",
3390 kIndexName
, kJournalName
, kTempIndexName
));
3396 if (mJournalHandle
) {
3397 // Rename journal to make sure we update index on next start in case
3399 rv
= CacheFileIOManager::RenameFile(
3400 mJournalHandle
, NS_LITERAL_CSTRING(kTempIndexName
), this);
3401 if (NS_FAILED(rv
)) {
3402 LOG(("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::"
3403 "RenameFile() failed synchronously [rv=0x%08x]", rv
));
3408 StartReadingIndex();
3413 MOZ_ASSERT(false, "Unexpected state!");
3420 CacheIndex::OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
)
3422 MOZ_CRASH("CacheIndex::OnFileOpened should not be called!");
3423 return NS_ERROR_UNEXPECTED
;
3427 CacheIndex::OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
3430 LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle
,
3435 CacheIndexAutoLock
lock(this);
3437 if (!IsIndexUsable()) {
3438 return NS_ERROR_NOT_AVAILABLE
;
3441 if (mState
== READY
&& mShuttingDown
) {
3447 if (mIndexHandle
!= aHandle
) {
3448 LOG(("CacheIndex::OnDataWritten() - ignoring notification since it "
3449 "belongs to previously canceled operation [state=%d]", mState
));
3453 if (NS_FAILED(aResult
)) {
3456 if (mSkipEntries
== mProcessEntries
) {
3457 rv
= CacheFileIOManager::RenameFile(mIndexHandle
,
3458 NS_LITERAL_CSTRING(kIndexName
),
3460 if (NS_FAILED(rv
)) {
3461 LOG(("CacheIndex::OnDataWritten() - CacheFileIOManager::"
3462 "RenameFile() failed synchronously [rv=0x%08x]", rv
));
3471 // Writing was canceled.
3472 LOG(("CacheIndex::OnDataWritten() - ignoring notification since the "
3473 "operation was previously canceled [state=%d]", mState
));
3480 CacheIndex::OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
, nsresult aResult
)
3482 LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08x]", aHandle
,
3485 CacheIndexAutoLock
lock(this);
3487 if (!IsIndexUsable()) {
3488 return NS_ERROR_NOT_AVAILABLE
;
3493 MOZ_ASSERT(mIndexHandle
== aHandle
|| mJournalHandle
== aHandle
);
3495 if (NS_FAILED(aResult
)) {
3498 if (!mIndexOnDiskIsValid
) {
3506 // Reading was canceled.
3507 LOG(("CacheIndex::OnDataRead() - ignoring notification since the "
3508 "operation was previously canceled [state=%d]", mState
));
3515 CacheIndex::OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
)
3517 MOZ_CRASH("CacheIndex::OnFileDoomed should not be called!");
3518 return NS_ERROR_UNEXPECTED
;
3522 CacheIndex::OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
)
3524 MOZ_CRASH("CacheIndex::OnEOFSet should not be called!");
3525 return NS_ERROR_UNEXPECTED
;
3529 CacheIndex::OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
)
3531 LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08x]", aHandle
,
3534 CacheIndexAutoLock
lock(this);
3536 if (!IsIndexUsable()) {
3537 return NS_ERROR_NOT_AVAILABLE
;
3540 if (mState
== READY
&& mShuttingDown
) {
3546 // This is a result of renaming the new index written to tmpfile to index
3547 // file. This is the last step when writing the index and the whole
3548 // writing process is successful iff renaming was successful.
3550 if (mIndexHandle
!= aHandle
) {
3551 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
3552 "belongs to previously canceled operation [state=%d]", mState
));
3556 FinishWrite(NS_SUCCEEDED(aResult
));
3559 // This is a result of renaming journal file to tmpfile. It is renamed
3560 // before we start reading index and journal file and it should normally
3561 // succeed. If it fails give up reading of index.
3563 if (mJournalHandle
!= aHandle
) {
3564 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since it "
3565 "belongs to previously canceled operation [state=%d]", mState
));
3569 if (NS_FAILED(aResult
)) {
3572 StartReadingIndex();
3576 // Reading/writing was canceled.
3577 LOG(("CacheIndex::OnFileRenamed() - ignoring notification since the "
3578 "operation was previously canceled [state=%d]", mState
));
3587 CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf
) const
3589 CacheIndexAutoLock
lock(const_cast<CacheIndex
*>(this));
3592 nsCOMPtr
<nsISizeOf
> sizeOf
;
3594 // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
3595 // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
3598 sizeOf
= do_QueryInterface(mCacheDirectory
);
3600 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3603 sizeOf
= do_QueryInterface(mUpdateTimer
);
3605 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3608 n
+= mallocSizeOf(mRWBuf
);
3609 n
+= mallocSizeOf(mRWHash
);
3611 n
+= mIndex
.SizeOfExcludingThis(mallocSizeOf
);
3612 n
+= mPendingUpdates
.SizeOfExcludingThis(mallocSizeOf
);
3613 n
+= mTmpJournal
.SizeOfExcludingThis(mallocSizeOf
);
3615 // mFrecencyArray and mExpirationArray items are reported by
3616 // mIndex/mPendingUpdates
3617 n
+= mFrecencyArray
.SizeOfExcludingThis(mallocSizeOf
);
3618 n
+= mExpirationArray
.SizeOfExcludingThis(mallocSizeOf
);
3619 n
+= mDiskConsumptionObservers
.SizeOfExcludingThis(mallocSizeOf
);
3626 CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
)
3631 return gInstance
->SizeOfExcludingThisInternal(mallocSizeOf
);
3636 CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
)
3638 return mallocSizeOf(gInstance
) + SizeOfExcludingThis(mallocSizeOf
);