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 "CacheFileUtils.h"
11 #include "CacheIndexIterator.h"
12 #include "CacheIndexContextIterator.h"
13 #include "nsThreadUtils.h"
14 #include "nsISizeOf.h"
15 #include "nsPrintfCString.h"
16 #include "mozilla/DebugOnly.h"
20 #include "mozilla/AutoRestore.h"
22 #include "mozilla/StaticPrefs_network.h"
23 #include "mozilla/Telemetry.h"
24 #include "mozilla/Unused.h"
26 #define kMinUnwrittenChanges 300
27 #define kMinDumpInterval 20000 // in milliseconds
28 #define kMaxBufSize 16384
29 #define kIndexVersion 0x0000000A
30 #define kUpdateIndexStartDelay 50000 // in milliseconds
31 #define kTelemetryReportBytesLimit (2U * 1024U * 1024U * 1024U) // 2GB
33 #define INDEX_NAME "index"
34 #define TEMP_INDEX_NAME "index.tmp"
35 #define JOURNAL_NAME "index.log"
37 namespace mozilla::net
{
41 class FrecencyComparator
{
43 bool Equals(const RefPtr
<CacheIndexRecordWrapper
>& a
,
44 const RefPtr
<CacheIndexRecordWrapper
>& b
) const {
49 return a
->Get()->mFrecency
== b
->Get()->mFrecency
;
51 bool LessThan(const RefPtr
<CacheIndexRecordWrapper
>& a
,
52 const RefPtr
<CacheIndexRecordWrapper
>& b
) const {
53 // Removed (=null) entries must be at the end of the array.
61 // Place entries with frecency 0 at the end of the non-removed entries.
62 if (a
->Get()->mFrecency
== 0) {
65 if (b
->Get()->mFrecency
== 0) {
69 return a
->Get()->mFrecency
< b
->Get()->mFrecency
;
75 // used to dispatch a wrapper deletion the caller's thread
76 // cannot be used on IOThread after shutdown begins
77 class DeleteCacheIndexRecordWrapper
: public Runnable
{
78 CacheIndexRecordWrapper
* mWrapper
;
81 explicit DeleteCacheIndexRecordWrapper(CacheIndexRecordWrapper
* wrapper
)
82 : Runnable("net::CacheIndex::DeleteCacheIndexRecordWrapper"),
84 NS_IMETHOD
Run() override
{
85 StaticMutexAutoLock
lock(CacheIndex::sLock
);
87 // if somehow the item is still in the frecency array, remove it
88 RefPtr
<CacheIndex
> index
= CacheIndex::gInstance
;
90 bool found
= index
->mFrecencyArray
.RecordExistedUnlocked(mWrapper
);
93 ("DeleteCacheIndexRecordWrapper::Run() - \
94 record wrapper found in frecency array during deletion"));
95 index
->mFrecencyArray
.RemoveRecord(mWrapper
, lock
);
104 void CacheIndexRecordWrapper::DispatchDeleteSelfToCurrentThread() {
105 // Dispatch during shutdown will not trigger DeleteCacheIndexRecordWrapper
106 nsCOMPtr
<nsIRunnable
> event
= new DeleteCacheIndexRecordWrapper(this);
107 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(event
));
110 CacheIndexRecordWrapper::~CacheIndexRecordWrapper() {
111 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
112 CacheIndex::sLock
.AssertCurrentThreadOwns();
113 RefPtr
<CacheIndex
> index
= CacheIndex::gInstance
;
115 bool found
= index
->mFrecencyArray
.RecordExistedUnlocked(this);
116 MOZ_DIAGNOSTIC_ASSERT(!found
);
122 * This helper class is responsible for keeping CacheIndex::mIndexStats and
123 * CacheIndex::mFrecencyArray up to date.
125 class MOZ_RAII CacheIndexEntryAutoManage
{
127 CacheIndexEntryAutoManage(const SHA1Sum::Hash
* aHash
, CacheIndex
* aIndex
,
128 const StaticMutexAutoLock
& aProofOfLock
)
129 MOZ_REQUIRES(CacheIndex::sLock
)
130 : mIndex(aIndex
), mProofOfLock(aProofOfLock
) {
132 const CacheIndexEntry
* entry
= FindEntry();
133 mIndex
->mIndexStats
.BeforeChange(entry
);
134 if (entry
&& entry
->IsInitialized() && !entry
->IsRemoved()) {
135 mOldRecord
= entry
->mRec
;
136 mOldFrecency
= entry
->mRec
->Get()->mFrecency
;
140 ~CacheIndexEntryAutoManage() MOZ_REQUIRES(CacheIndex::sLock
) {
141 const CacheIndexEntry
* entry
= FindEntry();
142 mIndex
->mIndexStats
.AfterChange(entry
);
143 if (!entry
|| !entry
->IsInitialized() || entry
->IsRemoved()) {
147 if (entry
&& !mOldRecord
) {
148 mIndex
->mFrecencyArray
.AppendRecord(entry
->mRec
, mProofOfLock
);
149 mIndex
->AddRecordToIterators(entry
->mRec
, mProofOfLock
);
150 } else if (!entry
&& mOldRecord
) {
151 mIndex
->mFrecencyArray
.RemoveRecord(mOldRecord
, mProofOfLock
);
152 mIndex
->RemoveRecordFromIterators(mOldRecord
, mProofOfLock
);
153 } else if (entry
&& mOldRecord
) {
154 if (entry
->mRec
!= mOldRecord
) {
155 // record has a different address, we have to replace it
156 mIndex
->ReplaceRecordInIterators(mOldRecord
, entry
->mRec
, mProofOfLock
);
158 if (entry
->mRec
->Get()->mFrecency
== mOldFrecency
) {
159 // If frecency hasn't changed simply replace the pointer
160 mIndex
->mFrecencyArray
.ReplaceRecord(mOldRecord
, entry
->mRec
,
163 // Remove old pointer and insert the new one at the end of the array
164 mIndex
->mFrecencyArray
.RemoveRecord(mOldRecord
, mProofOfLock
);
165 mIndex
->mFrecencyArray
.AppendRecord(entry
->mRec
, mProofOfLock
);
167 } else if (entry
->mRec
->Get()->mFrecency
!= mOldFrecency
) {
168 // Move the element at the end of the array
169 mIndex
->mFrecencyArray
.RemoveRecord(entry
->mRec
, mProofOfLock
);
170 mIndex
->mFrecencyArray
.AppendRecord(entry
->mRec
, mProofOfLock
);
173 // both entries were removed or not initialized, do nothing
177 // We cannot rely on nsTHashtable::GetEntry() in case we are removing entries
178 // while iterating. Destructor is called before the entry is removed. Caller
179 // must call one of following methods to skip lookup in the hashtable.
180 void DoNotSearchInIndex() { mDoNotSearchInIndex
= true; }
181 void DoNotSearchInUpdates() { mDoNotSearchInUpdates
= true; }
184 const CacheIndexEntry
* FindEntry() MOZ_REQUIRES(CacheIndex::sLock
) {
185 const CacheIndexEntry
* entry
= nullptr;
187 switch (mIndex
->mState
) {
188 case CacheIndex::READING
:
189 case CacheIndex::WRITING
:
190 if (!mDoNotSearchInUpdates
) {
191 entry
= mIndex
->mPendingUpdates
.GetEntry(*mHash
);
194 case CacheIndex::BUILDING
:
195 case CacheIndex::UPDATING
:
196 case CacheIndex::READY
:
197 if (!entry
&& !mDoNotSearchInIndex
) {
198 entry
= mIndex
->mIndex
.GetEntry(*mHash
);
201 case CacheIndex::INITIAL
:
202 case CacheIndex::SHUTDOWN
:
204 MOZ_ASSERT(false, "Unexpected state!");
210 const SHA1Sum::Hash
* mHash
;
211 RefPtr
<CacheIndex
> mIndex
;
212 RefPtr
<CacheIndexRecordWrapper
> mOldRecord
;
213 uint32_t mOldFrecency
{0};
214 bool mDoNotSearchInIndex
{false};
215 bool mDoNotSearchInUpdates
{false};
216 const StaticMutexAutoLock
& mProofOfLock
;
219 class FileOpenHelper final
: public CacheFileIOListener
{
221 NS_DECL_THREADSAFE_ISUPPORTS
223 explicit FileOpenHelper(CacheIndex
* aIndex
)
224 : mIndex(aIndex
), mCanceled(false) {}
227 CacheIndex::sLock
.AssertCurrentThreadOwns();
232 virtual ~FileOpenHelper() = default;
234 NS_IMETHOD
OnFileOpened(CacheFileHandle
* aHandle
, nsresult aResult
) override
;
235 NS_IMETHOD
OnDataWritten(CacheFileHandle
* aHandle
, const char* aBuf
,
236 nsresult aResult
) override
{
237 MOZ_CRASH("FileOpenHelper::OnDataWritten should not be called!");
238 return NS_ERROR_UNEXPECTED
;
240 NS_IMETHOD
OnDataRead(CacheFileHandle
* aHandle
, char* aBuf
,
241 nsresult aResult
) override
{
242 MOZ_CRASH("FileOpenHelper::OnDataRead should not be called!");
243 return NS_ERROR_UNEXPECTED
;
245 NS_IMETHOD
OnFileDoomed(CacheFileHandle
* aHandle
, nsresult aResult
) override
{
246 MOZ_CRASH("FileOpenHelper::OnFileDoomed should not be called!");
247 return NS_ERROR_UNEXPECTED
;
249 NS_IMETHOD
OnEOFSet(CacheFileHandle
* aHandle
, nsresult aResult
) override
{
250 MOZ_CRASH("FileOpenHelper::OnEOFSet should not be called!");
251 return NS_ERROR_UNEXPECTED
;
253 NS_IMETHOD
OnFileRenamed(CacheFileHandle
* aHandle
,
254 nsresult aResult
) override
{
255 MOZ_CRASH("FileOpenHelper::OnFileRenamed should not be called!");
256 return NS_ERROR_UNEXPECTED
;
259 RefPtr
<CacheIndex
> mIndex
;
263 NS_IMETHODIMP
FileOpenHelper::OnFileOpened(CacheFileHandle
* aHandle
,
265 StaticMutexAutoLock
lock(CacheIndex::sLock
);
269 CacheFileIOManager::DoomFile(aHandle
, nullptr);
275 mIndex
->OnFileOpenedInternal(this, aHandle
, aResult
, lock
);
280 NS_IMPL_ISUPPORTS(FileOpenHelper
, CacheFileIOListener
);
282 StaticRefPtr
<CacheIndex
> CacheIndex::gInstance
;
283 StaticMutex
CacheIndex::sLock
;
285 NS_IMPL_ADDREF(CacheIndex
)
286 NS_IMPL_RELEASE(CacheIndex
)
288 NS_INTERFACE_MAP_BEGIN(CacheIndex
)
289 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener
)
290 NS_INTERFACE_MAP_ENTRY(nsIRunnable
)
293 CacheIndex::CacheIndex() {
294 sLock
.AssertCurrentThreadOwns();
295 LOG(("CacheIndex::CacheIndex [this=%p]", this));
296 MOZ_ASSERT(!gInstance
, "multiple CacheIndex instances!");
299 CacheIndex::~CacheIndex() {
300 sLock
.AssertCurrentThreadOwns();
301 LOG(("CacheIndex::~CacheIndex [this=%p]", this));
307 nsresult
CacheIndex::Init(nsIFile
* aCacheDirectory
) {
308 LOG(("CacheIndex::Init()"));
310 MOZ_ASSERT(NS_IsMainThread());
312 StaticMutexAutoLock
lock(sLock
);
315 return NS_ERROR_ALREADY_INITIALIZED
;
318 RefPtr
<CacheIndex
> idx
= new CacheIndex();
320 nsresult rv
= idx
->InitInternal(aCacheDirectory
, lock
);
321 NS_ENSURE_SUCCESS(rv
, rv
);
323 gInstance
= std::move(idx
);
327 nsresult
CacheIndex::InitInternal(nsIFile
* aCacheDirectory
,
328 const StaticMutexAutoLock
& aProofOfLock
) {
330 sLock
.AssertCurrentThreadOwns();
332 rv
= aCacheDirectory
->Clone(getter_AddRefs(mCacheDirectory
));
333 NS_ENSURE_SUCCESS(rv
, rv
);
335 mStartTime
= TimeStamp::NowLoRes();
337 ReadIndexFromDisk(aProofOfLock
);
343 nsresult
CacheIndex::PreShutdown() {
344 MOZ_ASSERT(NS_IsMainThread());
346 StaticMutexAutoLock
lock(sLock
);
348 LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance
.get()));
351 RefPtr
<CacheIndex
> index
= gInstance
;
354 return NS_ERROR_NOT_INITIALIZED
;
358 ("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, "
359 "dontMarkIndexClean=%d]",
360 index
->mState
, index
->mIndexOnDiskIsValid
, index
->mDontMarkIndexClean
));
362 LOG(("CacheIndex::PreShutdown() - Closing iterators."));
363 for (uint32_t i
= 0; i
< index
->mIterators
.Length();) {
364 rv
= index
->mIterators
[i
]->CloseInternal(NS_ERROR_FAILURE
);
366 // CacheIndexIterator::CloseInternal() removes itself from mIteratos iff
367 // it returns success.
369 ("CacheIndex::PreShutdown() - Failed to remove iterator %p. "
370 "[rv=0x%08" PRIx32
"]",
371 index
->mIterators
[i
], static_cast<uint32_t>(rv
)));
376 index
->mShuttingDown
= true;
378 if (index
->mState
== READY
) {
379 return NS_OK
; // nothing to do
382 nsCOMPtr
<nsIRunnable
> event
;
383 event
= NewRunnableMethod("net::CacheIndex::PreShutdownInternal", index
,
384 &CacheIndex::PreShutdownInternal
);
386 nsCOMPtr
<nsIEventTarget
> ioTarget
= CacheFileIOManager::IOTarget();
387 MOZ_ASSERT(ioTarget
);
389 // PreShutdownInternal() will be executed before any queued event on INDEX
390 // level. That's OK since we don't want to wait for any operation in progess.
391 rv
= ioTarget
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
393 NS_WARNING("CacheIndex::PreShutdown() - Can't dispatch event");
394 LOG(("CacheIndex::PreShutdown() - Can't dispatch event"));
401 void CacheIndex::PreShutdownInternal() {
402 StaticMutexAutoLock
lock(sLock
);
405 ("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
406 "dontMarkIndexClean=%d]",
407 mState
, mIndexOnDiskIsValid
, mDontMarkIndexClean
));
409 MOZ_ASSERT(mShuttingDown
);
412 mUpdateTimer
->Cancel();
413 mUpdateTimer
= nullptr;
418 FinishWrite(false, lock
);
421 // nothing to do, write the journal in Shutdown()
424 FinishRead(false, lock
);
428 FinishUpdate(false, lock
);
431 MOZ_ASSERT(false, "Implement me!");
434 // We should end up in READY state
435 MOZ_ASSERT(mState
== READY
);
439 nsresult
CacheIndex::Shutdown() {
440 MOZ_ASSERT(NS_IsMainThread());
442 StaticMutexAutoLock
lock(sLock
);
444 LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance
.get()));
446 RefPtr
<CacheIndex
> index
= gInstance
.forget();
449 return NS_ERROR_NOT_INITIALIZED
;
452 bool sanitize
= CacheObserver::ClearCacheOnShutdown();
455 ("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, "
456 "dontMarkIndexClean=%d, sanitize=%d]",
457 index
->mState
, index
->mIndexOnDiskIsValid
, index
->mDontMarkIndexClean
,
460 MOZ_ASSERT(index
->mShuttingDown
);
462 EState oldState
= index
->mState
;
463 index
->ChangeState(SHUTDOWN
, lock
);
465 if (oldState
!= READY
) {
467 ("CacheIndex::Shutdown() - Unexpected state. Did posting of "
468 "PreShutdownInternal() fail?"));
473 index
->FinishWrite(false, lock
);
476 if (index
->mIndexOnDiskIsValid
&& !index
->mDontMarkIndexClean
) {
477 if (!sanitize
&& NS_FAILED(index
->WriteLogToDisk())) {
478 index
->RemoveJournalAndTempFile();
481 index
->RemoveJournalAndTempFile();
485 index
->FinishRead(false, lock
);
489 index
->FinishUpdate(false, lock
);
492 MOZ_ASSERT(false, "Unexpected state!");
496 index
->RemoveAllIndexFiles();
503 nsresult
CacheIndex::AddEntry(const SHA1Sum::Hash
* aHash
) {
504 LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash
)));
506 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
508 StaticMutexAutoLock
lock(sLock
);
510 RefPtr
<CacheIndex
> index
= gInstance
;
513 return NS_ERROR_NOT_INITIALIZED
;
516 if (!index
->IsIndexUsable()) {
517 return NS_ERROR_NOT_AVAILABLE
;
520 // Getters in CacheIndexStats assert when mStateLogged is true since the
521 // information is incomplete between calls to BeforeChange() and AfterChange()
522 // (i.e. while CacheIndexEntryAutoManage exists). We need to check whether
523 // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
524 bool updateIfNonFreshEntriesExist
= false;
527 CacheIndexEntryAutoManage
entryMng(aHash
, index
, lock
);
529 CacheIndexEntry
* entry
= index
->mIndex
.GetEntry(*aHash
);
530 bool entryRemoved
= entry
&& entry
->IsRemoved();
531 CacheIndexEntryUpdate
* updated
= nullptr;
533 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
534 index
->mState
== BUILDING
) {
535 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
537 if (entry
&& !entryRemoved
) {
538 // Found entry in index that shouldn't exist.
540 if (entry
->IsFresh()) {
541 // Someone removed the file on disk while FF is running. Update
542 // process can fix only non-fresh entries (i.e. entries that were not
543 // added within this session). Start update only if we have such
546 // TODO: This should be very rare problem. If it turns out not to be
547 // true, change the update process so that it also iterates all
548 // initialized non-empty entries and checks whether the file exists.
551 ("CacheIndex::AddEntry() - Cache file was removed outside FF "
554 updateIfNonFreshEntriesExist
= true;
555 } else if (index
->mState
== READY
) {
556 // Index is outdated, update it.
558 ("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
559 "update is needed"));
560 index
->mIndexNeedsUpdate
= true;
562 // We cannot be here when building index since all entries are fresh
564 MOZ_ASSERT(index
->mState
== UPDATING
);
569 entry
= index
->mIndex
.PutEntry(*aHash
);
571 } else { // WRITING, READING
572 updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
573 bool updatedRemoved
= updated
&& updated
->IsRemoved();
575 if ((updated
&& !updatedRemoved
) ||
576 (!updated
&& entry
&& !entryRemoved
&& entry
->IsFresh())) {
577 // Fresh entry found, so the file was removed outside FF
579 ("CacheIndex::AddEntry() - Cache file was removed outside FF "
582 updateIfNonFreshEntriesExist
= true;
583 } else if (!updated
&& entry
&& !entryRemoved
) {
584 if (index
->mState
== WRITING
) {
586 ("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
587 "update is needed"));
588 index
->mIndexNeedsUpdate
= true;
590 // Ignore if state is READING since the index information is partial
593 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
598 updated
->MarkDirty();
599 updated
->MarkFresh();
607 if (updateIfNonFreshEntriesExist
&&
608 index
->mIndexStats
.Count() != index
->mIndexStats
.Fresh()) {
609 index
->mIndexNeedsUpdate
= true;
612 index
->StartUpdatingIndexIfNeeded(lock
);
613 index
->WriteIndexToDiskIfNeeded(lock
);
619 nsresult
CacheIndex::EnsureEntryExists(const SHA1Sum::Hash
* aHash
) {
620 LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]",
623 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
625 StaticMutexAutoLock
lock(sLock
);
627 RefPtr
<CacheIndex
> index
= gInstance
;
630 return NS_ERROR_NOT_INITIALIZED
;
633 if (!index
->IsIndexUsable()) {
634 return NS_ERROR_NOT_AVAILABLE
;
638 CacheIndexEntryAutoManage
entryMng(aHash
, index
, lock
);
640 CacheIndexEntry
* entry
= index
->mIndex
.GetEntry(*aHash
);
641 bool entryRemoved
= entry
&& entry
->IsRemoved();
643 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
644 index
->mState
== BUILDING
) {
645 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
647 if (!entry
|| entryRemoved
) {
648 if (entryRemoved
&& entry
->IsFresh()) {
649 // This could happen only if somebody copies files to the entries
650 // directory while FF is running.
652 ("CacheIndex::EnsureEntryExists() - Cache file was added outside "
653 "FF process! Update is needed."));
654 index
->mIndexNeedsUpdate
= true;
655 } else if (index
->mState
== READY
||
656 (entryRemoved
&& !entry
->IsFresh())) {
657 // Removed non-fresh entries can be present as a result of
660 ("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
661 " exist, update is needed"));
662 index
->mIndexNeedsUpdate
= true;
666 entry
= index
->mIndex
.PutEntry(*aHash
);
672 } else { // WRITING, READING
673 CacheIndexEntryUpdate
* updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
674 bool updatedRemoved
= updated
&& updated
->IsRemoved();
676 if (updatedRemoved
|| (!updated
&& entryRemoved
&& entry
->IsFresh())) {
677 // Fresh information about missing entry found. This could happen only
678 // if somebody copies files to the entries directory while FF is
681 ("CacheIndex::EnsureEntryExists() - Cache file was added outside "
682 "FF process! Update is needed."));
683 index
->mIndexNeedsUpdate
= true;
684 } else if (!updated
&& (!entry
|| entryRemoved
)) {
685 if (index
->mState
== WRITING
) {
687 ("CacheIndex::EnsureEntryExists() - Didn't find entry that should"
688 " exist, update is needed"));
689 index
->mIndexNeedsUpdate
= true;
691 // Ignore if state is READING since the index information is partial
694 // We don't need entryRemoved and updatedRemoved info anymore
695 if (entryRemoved
) entry
= nullptr;
696 if (updatedRemoved
) updated
= nullptr;
699 updated
->MarkFresh();
702 // Create a new entry
703 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
705 updated
->MarkFresh();
706 updated
->MarkDirty();
708 if (!entry
->IsFresh()) {
709 // To mark the entry fresh we must make a copy of index entry
710 // since the index is read-only.
711 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
713 updated
->MarkFresh();
720 index
->StartUpdatingIndexIfNeeded(lock
);
721 index
->WriteIndexToDiskIfNeeded(lock
);
727 nsresult
CacheIndex::InitEntry(const SHA1Sum::Hash
* aHash
,
728 OriginAttrsHash aOriginAttrsHash
,
729 bool aAnonymous
, bool aPinned
) {
731 ("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, "
732 "originAttrsHash=%" PRIx64
", anonymous=%d, pinned=%d]",
733 LOGSHA1(aHash
), aOriginAttrsHash
, aAnonymous
, aPinned
));
735 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
737 StaticMutexAutoLock
lock(sLock
);
739 RefPtr
<CacheIndex
> index
= gInstance
;
742 return NS_ERROR_NOT_INITIALIZED
;
745 if (!index
->IsIndexUsable()) {
746 return NS_ERROR_NOT_AVAILABLE
;
750 CacheIndexEntryAutoManage
entryMng(aHash
, index
, lock
);
752 CacheIndexEntry
* entry
= index
->mIndex
.GetEntry(*aHash
);
753 CacheIndexEntryUpdate
* updated
= nullptr;
754 bool reinitEntry
= false;
756 if (entry
&& entry
->IsRemoved()) {
760 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
761 index
->mState
== BUILDING
) {
762 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
764 MOZ_ASSERT(entry
->IsFresh());
767 LOG(("CacheIndex::InitEntry() - Entry was not found in mIndex!"));
769 ("CacheIndex::InitEntry() - Entry was not found in mIndex!"));
770 return NS_ERROR_UNEXPECTED
;
773 if (IsCollision(entry
, aOriginAttrsHash
, aAnonymous
)) {
774 index
->mIndexNeedsUpdate
=
775 true; // TODO Does this really help in case of collision?
778 if (entry
->IsInitialized()) {
783 updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
784 DebugOnly
<bool> removed
= updated
&& updated
->IsRemoved();
786 MOZ_ASSERT(updated
|| !removed
);
787 MOZ_ASSERT(updated
|| entry
);
789 if (!updated
&& !entry
) {
791 ("CacheIndex::InitEntry() - Entry was found neither in mIndex nor "
792 "in mPendingUpdates!"));
794 ("CacheIndex::InitEntry() - Entry was found neither in "
795 "mIndex nor in mPendingUpdates!"));
796 return NS_ERROR_UNEXPECTED
;
800 MOZ_ASSERT(updated
->IsFresh());
802 if (IsCollision(updated
, aOriginAttrsHash
, aAnonymous
)) {
803 index
->mIndexNeedsUpdate
= true;
806 if (updated
->IsInitialized()) {
811 MOZ_ASSERT(entry
->IsFresh());
813 if (IsCollision(entry
, aOriginAttrsHash
, aAnonymous
)) {
814 index
->mIndexNeedsUpdate
= true;
817 if (entry
->IsInitialized()) {
822 // make a copy of a read-only entry
823 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
829 // There is a collision and we are going to rewrite this entry. Initialize
830 // it as a new entry.
833 updated
->MarkFresh();
841 updated
->Init(aOriginAttrsHash
, aAnonymous
, aPinned
);
842 updated
->MarkDirty();
844 entry
->Init(aOriginAttrsHash
, aAnonymous
, aPinned
);
849 index
->StartUpdatingIndexIfNeeded(lock
);
850 index
->WriteIndexToDiskIfNeeded(lock
);
856 nsresult
CacheIndex::RemoveEntry(const SHA1Sum::Hash
* aHash
) {
857 LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]",
860 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
862 StaticMutexAutoLock
lock(sLock
);
864 RefPtr
<CacheIndex
> index
= gInstance
;
867 return NS_ERROR_NOT_INITIALIZED
;
870 if (!index
->IsIndexUsable()) {
871 return NS_ERROR_NOT_AVAILABLE
;
875 CacheIndexEntryAutoManage
entryMng(aHash
, index
, lock
);
877 CacheIndexEntry
* entry
= index
->mIndex
.GetEntry(*aHash
);
878 bool entryRemoved
= entry
&& entry
->IsRemoved();
880 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
881 index
->mState
== BUILDING
) {
882 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
884 if (!entry
|| entryRemoved
) {
885 if (entryRemoved
&& entry
->IsFresh()) {
886 // This could happen only if somebody copies files to the entries
887 // directory while FF is running.
889 ("CacheIndex::RemoveEntry() - Cache file was added outside FF "
890 "process! Update is needed."));
891 index
->mIndexNeedsUpdate
= true;
892 } else if (index
->mState
== READY
||
893 (entryRemoved
&& !entry
->IsFresh())) {
894 // Removed non-fresh entries can be present as a result of
897 ("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
898 ", update is needed"));
899 index
->mIndexNeedsUpdate
= true;
903 if (!entry
->IsDirty() && entry
->IsFileEmpty()) {
904 index
->mIndex
.RemoveEntry(entry
);
907 entry
->MarkRemoved();
913 } else { // WRITING, READING
914 CacheIndexEntryUpdate
* updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
915 bool updatedRemoved
= updated
&& updated
->IsRemoved();
917 if (updatedRemoved
|| (!updated
&& entryRemoved
&& entry
->IsFresh())) {
918 // Fresh information about missing entry found. This could happen only
919 // if somebody copies files to the entries directory while FF is
922 ("CacheIndex::RemoveEntry() - Cache file was added outside FF "
923 "process! Update is needed."));
924 index
->mIndexNeedsUpdate
= true;
925 } else if (!updated
&& (!entry
|| entryRemoved
)) {
926 if (index
->mState
== WRITING
) {
928 ("CacheIndex::RemoveEntry() - Didn't find entry that should exist"
929 ", update is needed"));
930 index
->mIndexNeedsUpdate
= true;
932 // Ignore if state is READING since the index information is partial
936 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
940 updated
->MarkRemoved();
941 updated
->MarkDirty();
942 updated
->MarkFresh();
945 index
->StartUpdatingIndexIfNeeded(lock
);
946 index
->WriteIndexToDiskIfNeeded(lock
);
952 nsresult
CacheIndex::UpdateEntry(const SHA1Sum::Hash
* aHash
,
953 const uint32_t* aFrecency
,
954 const bool* aHasAltData
,
955 const uint16_t* aOnStartTime
,
956 const uint16_t* aOnStopTime
,
957 const uint8_t* aContentType
,
958 const uint32_t* aSize
) {
960 ("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
961 "frecency=%s, hasAltData=%s, onStartTime=%s, onStopTime=%s, "
962 "contentType=%s, size=%s]",
963 LOGSHA1(aHash
), aFrecency
? nsPrintfCString("%u", *aFrecency
).get() : "",
964 aHasAltData
? (*aHasAltData
? "true" : "false") : "",
965 aOnStartTime
? nsPrintfCString("%u", *aOnStartTime
).get() : "",
966 aOnStopTime
? nsPrintfCString("%u", *aOnStopTime
).get() : "",
967 aContentType
? nsPrintfCString("%u", *aContentType
).get() : "",
968 aSize
? nsPrintfCString("%u", *aSize
).get() : ""));
970 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
972 StaticMutexAutoLock
lock(sLock
);
974 RefPtr
<CacheIndex
> index
= gInstance
;
977 return NS_ERROR_NOT_INITIALIZED
;
980 if (!index
->IsIndexUsable()) {
981 return NS_ERROR_NOT_AVAILABLE
;
985 CacheIndexEntryAutoManage
entryMng(aHash
, index
, lock
);
987 CacheIndexEntry
* entry
= index
->mIndex
.GetEntry(*aHash
);
989 if (entry
&& entry
->IsRemoved()) {
993 if (index
->mState
== READY
|| index
->mState
== UPDATING
||
994 index
->mState
== BUILDING
) {
995 MOZ_ASSERT(index
->mPendingUpdates
.Count() == 0);
999 LOG(("CacheIndex::UpdateEntry() - Entry was not found in mIndex!"));
1001 ("CacheIndex::UpdateEntry() - Entry was not found in mIndex!"));
1002 return NS_ERROR_UNEXPECTED
;
1005 if (!HasEntryChanged(entry
, aFrecency
, aHasAltData
, aOnStartTime
,
1006 aOnStopTime
, aContentType
, aSize
)) {
1010 MOZ_ASSERT(entry
->IsFresh());
1011 MOZ_ASSERT(entry
->IsInitialized());
1015 entry
->SetFrecency(*aFrecency
);
1019 entry
->SetHasAltData(*aHasAltData
);
1023 entry
->SetOnStartTime(*aOnStartTime
);
1027 entry
->SetOnStopTime(*aOnStopTime
);
1031 entry
->SetContentType(*aContentType
);
1035 entry
->SetFileSize(*aSize
);
1038 CacheIndexEntryUpdate
* updated
= index
->mPendingUpdates
.GetEntry(*aHash
);
1039 DebugOnly
<bool> removed
= updated
&& updated
->IsRemoved();
1041 MOZ_ASSERT(updated
|| !removed
);
1042 MOZ_ASSERT(updated
|| entry
);
1047 ("CacheIndex::UpdateEntry() - Entry was found neither in mIndex "
1048 "nor in mPendingUpdates!"));
1050 ("CacheIndex::UpdateEntry() - Entry was found neither in "
1051 "mIndex nor in mPendingUpdates!"));
1052 return NS_ERROR_UNEXPECTED
;
1055 // make a copy of a read-only entry
1056 updated
= index
->mPendingUpdates
.PutEntry(*aHash
);
1060 MOZ_ASSERT(updated
->IsFresh());
1061 MOZ_ASSERT(updated
->IsInitialized());
1062 updated
->MarkDirty();
1065 updated
->SetFrecency(*aFrecency
);
1069 updated
->SetHasAltData(*aHasAltData
);
1073 updated
->SetOnStartTime(*aOnStartTime
);
1077 updated
->SetOnStopTime(*aOnStopTime
);
1081 updated
->SetContentType(*aContentType
);
1085 updated
->SetFileSize(*aSize
);
1090 index
->WriteIndexToDiskIfNeeded(lock
);
1096 nsresult
CacheIndex::RemoveAll() {
1097 LOG(("CacheIndex::RemoveAll()"));
1099 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1101 nsCOMPtr
<nsIFile
> file
;
1104 StaticMutexAutoLock
lock(sLock
);
1106 RefPtr
<CacheIndex
> index
= gInstance
;
1109 return NS_ERROR_NOT_INITIALIZED
;
1112 MOZ_ASSERT(!index
->mRemovingAll
);
1114 if (!index
->IsIndexUsable()) {
1115 return NS_ERROR_NOT_AVAILABLE
;
1118 AutoRestore
<bool> saveRemovingAll(index
->mRemovingAll
);
1119 index
->mRemovingAll
= true;
1121 // Doom index and journal handles but don't null them out since this will be
1122 // done in FinishWrite/FinishRead methods.
1123 if (index
->mIndexHandle
) {
1124 CacheFileIOManager::DoomFile(index
->mIndexHandle
, nullptr);
1126 // We don't have a handle to index file, so get the file here, but delete
1127 // it outside the lock. Ignore the result since this is not fatal.
1128 index
->GetFile(nsLiteralCString(INDEX_NAME
), getter_AddRefs(file
));
1131 if (index
->mJournalHandle
) {
1132 CacheFileIOManager::DoomFile(index
->mJournalHandle
, nullptr);
1135 switch (index
->mState
) {
1137 index
->FinishWrite(false, lock
);
1143 index
->FinishRead(false, lock
);
1147 index
->FinishUpdate(false, lock
);
1150 MOZ_ASSERT(false, "Unexpected state!");
1153 // We should end up in READY state
1154 MOZ_ASSERT(index
->mState
== READY
);
1156 // There should not be any handle
1157 MOZ_ASSERT(!index
->mIndexHandle
);
1158 MOZ_ASSERT(!index
->mJournalHandle
);
1160 index
->mIndexOnDiskIsValid
= false;
1161 index
->mIndexNeedsUpdate
= false;
1163 index
->mIndexStats
.Clear();
1164 index
->mFrecencyArray
.Clear(lock
);
1165 index
->mIndex
.Clear();
1167 for (uint32_t i
= 0; i
< index
->mIterators
.Length();) {
1168 nsresult rv
= index
->mIterators
[i
]->CloseInternal(NS_ERROR_NOT_AVAILABLE
);
1169 if (NS_FAILED(rv
)) {
1170 // CacheIndexIterator::CloseInternal() removes itself from mIterators
1171 // iff it returns success.
1173 ("CacheIndex::RemoveAll() - Failed to remove iterator %p. "
1174 "[rv=0x%08" PRIx32
"]",
1175 index
->mIterators
[i
], static_cast<uint32_t>(rv
)));
1182 // Ignore the result. The file might not exist and the failure is not fatal.
1183 file
->Remove(false);
1190 nsresult
CacheIndex::HasEntry(
1191 const nsACString
& aKey
, EntryStatus
* _retval
,
1192 const std::function
<void(const CacheIndexEntry
*)>& aCB
) {
1193 LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey
).get()));
1197 sum
.update(aKey
.BeginReading(), aKey
.Length());
1200 return HasEntry(hash
, _retval
, aCB
);
1204 nsresult
CacheIndex::HasEntry(
1205 const SHA1Sum::Hash
& hash
, EntryStatus
* _retval
,
1206 const std::function
<void(const CacheIndexEntry
*)>& aCB
) {
1207 StaticMutexAutoLock
lock(sLock
);
1209 RefPtr
<CacheIndex
> index
= gInstance
;
1212 return NS_ERROR_NOT_INITIALIZED
;
1215 if (!index
->IsIndexUsable()) {
1216 return NS_ERROR_NOT_AVAILABLE
;
1219 const CacheIndexEntry
* entry
= nullptr;
1221 switch (index
->mState
) {
1224 entry
= index
->mPendingUpdates
.GetEntry(hash
);
1230 entry
= index
->mIndex
.GetEntry(hash
);
1235 MOZ_ASSERT(false, "Unexpected state!");
1239 if (index
->mState
== READY
|| index
->mState
== WRITING
) {
1240 *_retval
= DOES_NOT_EXIST
;
1242 *_retval
= DO_NOT_KNOW
;
1245 if (entry
->IsRemoved()) {
1246 if (entry
->IsFresh()) {
1247 *_retval
= DOES_NOT_EXIST
;
1249 *_retval
= DO_NOT_KNOW
;
1259 LOG(("CacheIndex::HasEntry() - result is %u", *_retval
));
1264 nsresult
CacheIndex::GetEntryForEviction(bool aIgnoreEmptyEntries
,
1265 SHA1Sum::Hash
* aHash
, uint32_t* aCnt
) {
1266 LOG(("CacheIndex::GetEntryForEviction()"));
1268 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1270 StaticMutexAutoLock
lock(sLock
);
1272 RefPtr
<CacheIndex
> index
= gInstance
;
1274 if (!index
) return NS_ERROR_NOT_INITIALIZED
;
1276 if (!index
->IsIndexUsable()) {
1277 return NS_ERROR_NOT_AVAILABLE
;
1280 if (index
->mIndexStats
.Size() == 0) {
1281 return NS_ERROR_NOT_AVAILABLE
;
1284 int32_t mediaUsage
=
1285 round(static_cast<double>(index
->mIndexStats
.SizeByType(
1286 nsICacheEntry::CONTENT_TYPE_MEDIA
)) *
1287 100.0 / static_cast<double>(index
->mIndexStats
.Size()));
1288 int32_t mediaUsageLimit
=
1289 StaticPrefs::browser_cache_disk_content_type_media_limit();
1290 bool evictMedia
= false;
1291 if (mediaUsage
> mediaUsageLimit
) {
1293 ("CacheIndex::GetEntryForEviction() - media content type is over the "
1294 "limit [mediaUsage=%d, mediaUsageLimit=%d]",
1295 mediaUsage
, mediaUsageLimit
));
1300 CacheIndexRecord
* foundRecord
= nullptr;
1301 uint32_t skipped
= 0;
1303 // find first non-forced valid and unpinned entry with the lowest frecency
1304 index
->mFrecencyArray
.SortIfNeeded(lock
);
1306 for (auto iter
= index
->mFrecencyArray
.Iter(); !iter
.Done(); iter
.Next()) {
1307 CacheIndexRecord
* rec
= iter
.Get()->Get();
1309 memcpy(&hash
, rec
->mHash
, sizeof(SHA1Sum::Hash
));
1313 if (evictMedia
&& CacheIndexEntry::GetContentType(rec
) !=
1314 nsICacheEntry::CONTENT_TYPE_MEDIA
) {
1318 if (IsForcedValidEntry(&hash
)) {
1322 if (CacheIndexEntry::IsPinned(rec
)) {
1326 if (aIgnoreEmptyEntries
&& !CacheIndexEntry::GetFileSize(*rec
)) {
1335 if (!foundRecord
) return NS_ERROR_NOT_AVAILABLE
;
1340 ("CacheIndex::GetEntryForEviction() - returning entry "
1341 "[hash=%08x%08x%08x%08x%08x, cnt=%u, frecency=%u, contentType=%u]",
1342 LOGSHA1(&hash
), *aCnt
, foundRecord
->mFrecency
,
1343 CacheIndexEntry::GetContentType(foundRecord
)));
1345 memcpy(aHash
, &hash
, sizeof(SHA1Sum::Hash
));
1351 bool CacheIndex::IsForcedValidEntry(const SHA1Sum::Hash
* aHash
) {
1352 RefPtr
<CacheFileHandle
> handle
;
1354 CacheFileIOManager::gInstance
->mHandles
.GetHandle(aHash
,
1355 getter_AddRefs(handle
));
1357 if (!handle
) return false;
1359 nsCString hashKey
= handle
->Key();
1360 return CacheStorageService::Self()->IsForcedValidEntry(hashKey
);
1364 nsresult
CacheIndex::GetCacheSize(uint32_t* _retval
) {
1365 LOG(("CacheIndex::GetCacheSize()"));
1367 StaticMutexAutoLock
lock(sLock
);
1369 RefPtr
<CacheIndex
> index
= gInstance
;
1371 if (!index
) return NS_ERROR_NOT_INITIALIZED
;
1373 if (!index
->IsIndexUsable()) {
1374 return NS_ERROR_NOT_AVAILABLE
;
1377 *_retval
= index
->mIndexStats
.Size();
1378 LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval
));
1383 nsresult
CacheIndex::GetEntryFileCount(uint32_t* _retval
) {
1384 LOG(("CacheIndex::GetEntryFileCount()"));
1386 StaticMutexAutoLock
lock(sLock
);
1388 RefPtr
<CacheIndex
> index
= gInstance
;
1391 return NS_ERROR_NOT_INITIALIZED
;
1394 if (!index
->IsIndexUsable()) {
1395 return NS_ERROR_NOT_AVAILABLE
;
1398 *_retval
= index
->mIndexStats
.ActiveEntriesCount();
1399 LOG(("CacheIndex::GetEntryFileCount() - returning %u", *_retval
));
1404 nsresult
CacheIndex::GetCacheStats(nsILoadContextInfo
* aInfo
, uint32_t* aSize
,
1406 LOG(("CacheIndex::GetCacheStats() [info=%p]", aInfo
));
1408 StaticMutexAutoLock
lock(sLock
);
1410 RefPtr
<CacheIndex
> index
= gInstance
;
1413 return NS_ERROR_NOT_INITIALIZED
;
1416 if (!index
->IsIndexUsable()) {
1417 return NS_ERROR_NOT_AVAILABLE
;
1423 for (auto iter
= index
->mFrecencyArray
.Iter(); !iter
.Done(); iter
.Next()) {
1425 !CacheIndexEntry::RecordMatchesLoadContextInfo(iter
.Get(), aInfo
)) {
1429 *aSize
+= CacheIndexEntry::GetFileSize(*(iter
.Get()->Get()));
1437 nsresult
CacheIndex::AsyncGetDiskConsumption(
1438 nsICacheStorageConsumptionObserver
* aObserver
) {
1439 LOG(("CacheIndex::AsyncGetDiskConsumption()"));
1441 StaticMutexAutoLock
lock(sLock
);
1443 RefPtr
<CacheIndex
> index
= gInstance
;
1446 return NS_ERROR_NOT_INITIALIZED
;
1449 if (!index
->IsIndexUsable()) {
1450 return NS_ERROR_NOT_AVAILABLE
;
1453 RefPtr
<DiskConsumptionObserver
> observer
=
1454 DiskConsumptionObserver::Init(aObserver
);
1456 NS_ENSURE_ARG(observer
);
1458 if ((index
->mState
== READY
|| index
->mState
== WRITING
) &&
1459 !index
->mAsyncGetDiskConsumptionBlocked
) {
1460 LOG(("CacheIndex::AsyncGetDiskConsumption - calling immediately"));
1461 // Safe to call the callback under the lock,
1462 // we always post to the main thread.
1463 observer
->OnDiskConsumption(index
->mIndexStats
.Size() << 10);
1467 LOG(("CacheIndex::AsyncGetDiskConsumption - remembering callback"));
1468 // Will be called when the index get to the READY state.
1469 index
->mDiskConsumptionObservers
.AppendElement(observer
);
1471 // Move forward with index re/building if it is pending
1472 RefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
1475 NS_NewRunnableFunction("net::CacheIndex::AsyncGetDiskConsumption",
1477 StaticMutexAutoLock
lock(sLock
);
1479 RefPtr
<CacheIndex
> index
= gInstance
;
1480 if (index
&& index
->mUpdateTimer
) {
1481 index
->mUpdateTimer
->Cancel();
1482 index
->DelayedUpdateLocked(lock
);
1485 CacheIOThread::INDEX
);
1492 nsresult
CacheIndex::GetIterator(nsILoadContextInfo
* aInfo
, bool aAddNew
,
1493 CacheIndexIterator
** _retval
) {
1494 LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo
, aAddNew
));
1496 StaticMutexAutoLock
lock(sLock
);
1498 RefPtr
<CacheIndex
> index
= gInstance
;
1501 return NS_ERROR_NOT_INITIALIZED
;
1504 if (!index
->IsIndexUsable()) {
1505 return NS_ERROR_NOT_AVAILABLE
;
1508 RefPtr
<CacheIndexIterator
> idxIter
;
1510 idxIter
= new CacheIndexContextIterator(index
, aAddNew
, aInfo
);
1512 idxIter
= new CacheIndexIterator(index
, aAddNew
);
1515 index
->mFrecencyArray
.SortIfNeeded(lock
);
1517 for (auto iter
= index
->mFrecencyArray
.Iter(); !iter
.Done(); iter
.Next()) {
1518 idxIter
->AddRecord(iter
.Get(), lock
);
1521 index
->mIterators
.AppendElement(idxIter
);
1522 idxIter
.swap(*_retval
);
1527 nsresult
CacheIndex::IsUpToDate(bool* _retval
) {
1528 LOG(("CacheIndex::IsUpToDate()"));
1530 StaticMutexAutoLock
lock(sLock
);
1532 RefPtr
<CacheIndex
> index
= gInstance
;
1535 return NS_ERROR_NOT_INITIALIZED
;
1538 if (!index
->IsIndexUsable()) {
1539 return NS_ERROR_NOT_AVAILABLE
;
1542 *_retval
= (index
->mState
== READY
|| index
->mState
== WRITING
) &&
1543 !index
->mIndexNeedsUpdate
&& !index
->mShuttingDown
;
1545 LOG(("CacheIndex::IsUpToDate() - returning %d", *_retval
));
1549 bool CacheIndex::IsIndexUsable() {
1550 MOZ_ASSERT(mState
!= INITIAL
);
1569 bool CacheIndex::IsCollision(CacheIndexEntry
* aEntry
,
1570 OriginAttrsHash aOriginAttrsHash
,
1572 if (!aEntry
->IsInitialized()) {
1576 if (aEntry
->Anonymous() != aAnonymous
||
1577 aEntry
->OriginAttrsHash() != aOriginAttrsHash
) {
1579 ("CacheIndex::IsCollision() - Collision detected for entry hash=%08x"
1580 "%08x%08x%08x%08x, expected values: originAttrsHash=%" PRIu64
", "
1581 "anonymous=%d; actual values: originAttrsHash=%" PRIu64
1583 LOGSHA1(aEntry
->Hash()), aOriginAttrsHash
, aAnonymous
,
1584 aEntry
->OriginAttrsHash(), aEntry
->Anonymous()));
1592 bool CacheIndex::HasEntryChanged(
1593 CacheIndexEntry
* aEntry
, const uint32_t* aFrecency
, const bool* aHasAltData
,
1594 const uint16_t* aOnStartTime
, const uint16_t* aOnStopTime
,
1595 const uint8_t* aContentType
, const uint32_t* aSize
) {
1596 if (aFrecency
&& *aFrecency
!= aEntry
->GetFrecency()) {
1600 if (aHasAltData
&& *aHasAltData
!= aEntry
->GetHasAltData()) {
1604 if (aOnStartTime
&& *aOnStartTime
!= aEntry
->GetOnStartTime()) {
1608 if (aOnStopTime
&& *aOnStopTime
!= aEntry
->GetOnStopTime()) {
1612 if (aContentType
&& *aContentType
!= aEntry
->GetContentType()) {
1617 (*aSize
& CacheIndexEntry::kFileSizeMask
) != aEntry
->GetFileSize()) {
1624 void CacheIndex::ProcessPendingOperations(
1625 const StaticMutexAutoLock
& aProofOfLock
) {
1626 sLock
.AssertCurrentThreadOwns();
1627 LOG(("CacheIndex::ProcessPendingOperations()"));
1629 for (auto iter
= mPendingUpdates
.Iter(); !iter
.Done(); iter
.Next()) {
1630 CacheIndexEntryUpdate
* update
= iter
.Get();
1632 LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
1633 LOGSHA1(update
->Hash())));
1635 MOZ_ASSERT(update
->IsFresh());
1637 CacheIndexEntry
* entry
= mIndex
.GetEntry(*update
->Hash());
1639 CacheIndexEntryAutoManage
emng(update
->Hash(), this, aProofOfLock
);
1640 emng
.DoNotSearchInUpdates();
1642 if (update
->IsRemoved()) {
1644 if (entry
->IsRemoved()) {
1645 MOZ_ASSERT(entry
->IsFresh());
1646 MOZ_ASSERT(entry
->IsDirty());
1647 } else if (!entry
->IsDirty() && entry
->IsFileEmpty()) {
1648 // Entries with empty file are not stored in index on disk. Just
1649 // remove the entry, but only in case the entry is not dirty, i.e.
1650 // the entry file was empty when we wrote the index.
1651 mIndex
.RemoveEntry(entry
);
1654 entry
->MarkRemoved();
1660 // Some information in mIndex can be newer than in mPendingUpdates (see
1661 // bug 1074832). This will copy just those values that were really
1663 update
->ApplyUpdate(entry
);
1665 // There is no entry in mIndex, copy all information from
1666 // mPendingUpdates to mIndex.
1667 entry
= mIndex
.PutEntry(*update
->Hash());
1674 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
1676 EnsureCorrectStats();
1679 bool CacheIndex::WriteIndexToDiskIfNeeded(
1680 const StaticMutexAutoLock
& aProofOfLock
) {
1681 sLock
.AssertCurrentThreadOwns();
1682 if (mState
!= READY
|| mShuttingDown
|| mRWPending
) {
1686 if (!mLastDumpTime
.IsNull() &&
1687 (TimeStamp::NowLoRes() - mLastDumpTime
).ToMilliseconds() <
1692 if (mIndexStats
.Dirty() < kMinUnwrittenChanges
) {
1696 WriteIndexToDisk(aProofOfLock
);
1700 void CacheIndex::WriteIndexToDisk(const StaticMutexAutoLock
& aProofOfLock
) {
1701 sLock
.AssertCurrentThreadOwns();
1702 LOG(("CacheIndex::WriteIndexToDisk()"));
1707 MOZ_ASSERT(mState
== READY
);
1708 MOZ_ASSERT(!mRWBuf
);
1709 MOZ_ASSERT(!mRWHash
);
1710 MOZ_ASSERT(!mRWPending
);
1712 ChangeState(WRITING
, aProofOfLock
);
1714 mProcessEntries
= mIndexStats
.ActiveEntriesCount();
1716 mIndexFileOpener
= new FileOpenHelper(this);
1717 rv
= CacheFileIOManager::OpenFile(
1718 nsLiteralCString(TEMP_INDEX_NAME
),
1719 CacheFileIOManager::SPECIAL_FILE
| CacheFileIOManager::CREATE
,
1721 if (NS_FAILED(rv
)) {
1722 LOG(("CacheIndex::WriteIndexToDisk() - Can't open file [rv=0x%08" PRIx32
1724 static_cast<uint32_t>(rv
)));
1725 FinishWrite(false, aProofOfLock
);
1729 // Write index header to a buffer, it will be written to disk together with
1730 // records in WriteRecords() once we open the file successfully.
1732 mRWHash
= new CacheHash();
1736 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
, kIndexVersion
);
1737 mRWBufPos
+= sizeof(uint32_t);
1739 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
,
1740 static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC
));
1741 mRWBufPos
+= sizeof(uint32_t);
1743 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
, 1);
1744 mRWBufPos
+= sizeof(uint32_t);
1745 // amount of data written to the cache
1746 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
,
1747 static_cast<uint32_t>(mTotalBytesWritten
>> 10));
1748 mRWBufPos
+= sizeof(uint32_t);
1753 void CacheIndex::WriteRecords(const StaticMutexAutoLock
& aProofOfLock
) {
1754 sLock
.AssertCurrentThreadOwns();
1755 LOG(("CacheIndex::WriteRecords()"));
1759 MOZ_ASSERT(mState
== WRITING
);
1760 MOZ_ASSERT(!mRWPending
);
1765 MOZ_ASSERT(mRWBufPos
== 0);
1766 fileOffset
= sizeof(CacheIndexHeader
);
1767 fileOffset
+= sizeof(CacheIndexRecord
) * mSkipEntries
;
1769 MOZ_ASSERT(mRWBufPos
== sizeof(CacheIndexHeader
));
1772 uint32_t hashOffset
= mRWBufPos
;
1774 char* buf
= mRWBuf
+ mRWBufPos
;
1775 uint32_t skip
= mSkipEntries
;
1776 uint32_t processMax
= (mRWBufSize
- mRWBufPos
) / sizeof(CacheIndexRecord
);
1777 MOZ_ASSERT(processMax
!= 0 ||
1779 0); // TODO make sure we can write an empty index
1780 uint32_t processed
= 0;
1782 bool hasMore
= false;
1784 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
1785 CacheIndexEntry
* entry
= iter
.Get();
1786 if (entry
->IsRemoved() || !entry
->IsInitialized() || entry
->IsFileEmpty()) {
1795 if (processed
== processMax
) {
1802 entry
->WriteToBuf(buf
);
1803 buf
+= sizeof(CacheIndexRecord
);
1807 MOZ_ASSERT(mRWBufPos
!= static_cast<uint32_t>(buf
- mRWBuf
) ||
1808 mProcessEntries
== 0);
1809 mRWBufPos
= buf
- mRWBuf
;
1810 mSkipEntries
+= processed
;
1811 MOZ_ASSERT(mSkipEntries
<= mProcessEntries
);
1813 mRWHash
->Update(mRWBuf
+ hashOffset
, mRWBufPos
- hashOffset
);
1815 if (mSkipEntries
== mProcessEntries
) {
1816 MOZ_ASSERT(!hasMore
);
1818 // We've processed all records
1819 if (mRWBufPos
+ sizeof(CacheHash::Hash32_t
) > mRWBufSize
) {
1820 // realloc buffer to spare another write cycle
1821 mRWBufSize
= mRWBufPos
+ sizeof(CacheHash::Hash32_t
);
1822 mRWBuf
= static_cast<char*>(moz_xrealloc(mRWBuf
, mRWBufSize
));
1825 NetworkEndian::writeUint32(mRWBuf
+ mRWBufPos
, mRWHash
->GetHash());
1826 mRWBufPos
+= sizeof(CacheHash::Hash32_t
);
1828 MOZ_ASSERT(hasMore
);
1831 rv
= CacheFileIOManager::Write(mIndexHandle
, fileOffset
, mRWBuf
, mRWBufPos
,
1832 mSkipEntries
== mProcessEntries
, false, this);
1833 if (NS_FAILED(rv
)) {
1835 ("CacheIndex::WriteRecords() - CacheFileIOManager::Write() failed "
1836 "synchronously [rv=0x%08" PRIx32
"]",
1837 static_cast<uint32_t>(rv
)));
1838 FinishWrite(false, aProofOfLock
);
1846 void CacheIndex::FinishWrite(bool aSucceeded
,
1847 const StaticMutexAutoLock
& aProofOfLock
) {
1848 sLock
.AssertCurrentThreadOwns();
1849 LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded
));
1851 MOZ_ASSERT((!aSucceeded
&& mState
== SHUTDOWN
) || mState
== WRITING
);
1853 // If there is write operation pending we must be cancelling writing of the
1854 // index when shutting down or removing the whole index.
1855 MOZ_ASSERT(!mRWPending
|| (!aSucceeded
&& (mShuttingDown
|| mRemovingAll
)));
1857 mIndexHandle
= nullptr;
1862 // Opening of the file must not be in progress if writing succeeded.
1863 MOZ_ASSERT(!mIndexFileOpener
);
1865 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
1866 CacheIndexEntry
* entry
= iter
.Get();
1868 bool remove
= false;
1870 CacheIndexEntryAutoManage
emng(entry
->Hash(), this, aProofOfLock
);
1872 if (entry
->IsRemoved()) {
1873 emng
.DoNotSearchInIndex();
1875 } else if (entry
->IsDirty()) {
1876 entry
->ClearDirty();
1884 mIndexOnDiskIsValid
= true;
1886 if (mIndexFileOpener
) {
1887 // If opening of the file is still in progress (e.g. WRITE process was
1888 // canceled by RemoveAll()) then we need to cancel the opener to make sure
1889 // that OnFileOpenedInternal() won't be called.
1890 mIndexFileOpener
->Cancel();
1891 mIndexFileOpener
= nullptr;
1895 ProcessPendingOperations(aProofOfLock
);
1898 if (mState
== WRITING
) {
1899 ChangeState(READY
, aProofOfLock
);
1900 mLastDumpTime
= TimeStamp::NowLoRes();
1904 nsresult
CacheIndex::GetFile(const nsACString
& aName
, nsIFile
** _retval
) {
1907 nsCOMPtr
<nsIFile
> file
;
1908 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
1909 NS_ENSURE_SUCCESS(rv
, rv
);
1911 rv
= file
->AppendNative(aName
);
1912 NS_ENSURE_SUCCESS(rv
, rv
);
1914 file
.swap(*_retval
);
1918 void CacheIndex::RemoveFile(const nsACString
& aName
) {
1919 MOZ_ASSERT(mState
== SHUTDOWN
);
1923 nsCOMPtr
<nsIFile
> file
;
1924 rv
= GetFile(aName
, getter_AddRefs(file
));
1925 NS_ENSURE_SUCCESS_VOID(rv
);
1927 rv
= file
->Remove(false);
1928 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
1930 ("CacheIndex::RemoveFile() - Cannot remove old entry file from disk "
1931 "[rv=0x%08" PRIx32
", name=%s]",
1932 static_cast<uint32_t>(rv
), PromiseFlatCString(aName
).get()));
1936 void CacheIndex::RemoveAllIndexFiles() {
1937 LOG(("CacheIndex::RemoveAllIndexFiles()"));
1938 RemoveFile(nsLiteralCString(INDEX_NAME
));
1939 RemoveJournalAndTempFile();
1942 void CacheIndex::RemoveJournalAndTempFile() {
1943 LOG(("CacheIndex::RemoveJournalAndTempFile()"));
1944 RemoveFile(nsLiteralCString(TEMP_INDEX_NAME
));
1945 RemoveFile(nsLiteralCString(JOURNAL_NAME
));
1948 class WriteLogHelper
{
1950 explicit WriteLogHelper(PRFileDesc
* aFD
)
1951 : mFD(aFD
), mBufSize(kMaxBufSize
), mBufPos(0) {
1952 mHash
= new CacheHash();
1953 mBuf
= static_cast<char*>(moz_xmalloc(mBufSize
));
1956 ~WriteLogHelper() { free(mBuf
); }
1958 nsresult
AddEntry(CacheIndexEntry
* aEntry
);
1962 nsresult
FlushBuffer();
1968 RefPtr
<CacheHash
> mHash
;
1971 nsresult
WriteLogHelper::AddEntry(CacheIndexEntry
* aEntry
) {
1974 if (mBufPos
+ sizeof(CacheIndexRecord
) > mBufSize
) {
1975 mHash
->Update(mBuf
, mBufPos
);
1978 NS_ENSURE_SUCCESS(rv
, rv
);
1979 MOZ_ASSERT(mBufPos
+ sizeof(CacheIndexRecord
) <= mBufSize
);
1982 aEntry
->WriteToBuf(mBuf
+ mBufPos
);
1983 mBufPos
+= sizeof(CacheIndexRecord
);
1988 nsresult
WriteLogHelper::Finish() {
1991 mHash
->Update(mBuf
, mBufPos
);
1992 if (mBufPos
+ sizeof(CacheHash::Hash32_t
) > mBufSize
) {
1994 NS_ENSURE_SUCCESS(rv
, rv
);
1995 MOZ_ASSERT(mBufPos
+ sizeof(CacheHash::Hash32_t
) <= mBufSize
);
1998 NetworkEndian::writeUint32(mBuf
+ mBufPos
, mHash
->GetHash());
1999 mBufPos
+= sizeof(CacheHash::Hash32_t
);
2002 NS_ENSURE_SUCCESS(rv
, rv
);
2007 nsresult
WriteLogHelper::FlushBuffer() {
2008 if (CacheObserver::IsPastShutdownIOLag()) {
2009 LOG(("WriteLogHelper::FlushBuffer() - Interrupting writing journal."));
2010 return NS_ERROR_FAILURE
;
2013 int32_t bytesWritten
= PR_Write(mFD
, mBuf
, mBufPos
);
2015 if (bytesWritten
!= mBufPos
) {
2016 return NS_ERROR_FAILURE
;
2023 nsresult
CacheIndex::WriteLogToDisk() {
2024 LOG(("CacheIndex::WriteLogToDisk()"));
2028 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2029 MOZ_ASSERT(mState
== SHUTDOWN
);
2031 if (CacheObserver::IsPastShutdownIOLag()) {
2032 LOG(("CacheIndex::WriteLogToDisk() - Skipping writing journal."));
2033 return NS_ERROR_FAILURE
;
2036 RemoveFile(nsLiteralCString(TEMP_INDEX_NAME
));
2038 nsCOMPtr
<nsIFile
> indexFile
;
2039 rv
= GetFile(nsLiteralCString(INDEX_NAME
), getter_AddRefs(indexFile
));
2040 NS_ENSURE_SUCCESS(rv
, rv
);
2042 nsCOMPtr
<nsIFile
> logFile
;
2043 rv
= GetFile(nsLiteralCString(JOURNAL_NAME
), getter_AddRefs(logFile
));
2044 NS_ENSURE_SUCCESS(rv
, rv
);
2048 PRFileDesc
* fd
= nullptr;
2049 rv
= logFile
->OpenNSPRFileDesc(PR_RDWR
| PR_CREATE_FILE
| PR_TRUNCATE
, 0600,
2051 NS_ENSURE_SUCCESS(rv
, rv
);
2053 WriteLogHelper
wlh(fd
);
2054 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
2055 CacheIndexEntry
* entry
= iter
.Get();
2056 if (entry
->IsRemoved() || entry
->IsDirty()) {
2057 rv
= wlh
.AddEntry(entry
);
2058 if (NS_WARN_IF(NS_FAILED(rv
))) {
2066 NS_ENSURE_SUCCESS(rv
, rv
);
2068 rv
= indexFile
->OpenNSPRFileDesc(PR_RDWR
, 0600, &fd
);
2069 NS_ENSURE_SUCCESS(rv
, rv
);
2071 // Seek to dirty flag in the index header and clear it.
2072 static_assert(2 * sizeof(uint32_t) == offsetof(CacheIndexHeader
, mIsDirty
),
2073 "Unexpected offset of CacheIndexHeader::mIsDirty");
2074 int64_t offset
= PR_Seek64(fd
, 2 * sizeof(uint32_t), PR_SEEK_SET
);
2077 return NS_ERROR_FAILURE
;
2080 uint32_t isDirty
= 0;
2081 int32_t bytesWritten
= PR_Write(fd
, &isDirty
, sizeof(isDirty
));
2083 if (bytesWritten
!= sizeof(isDirty
)) {
2084 return NS_ERROR_FAILURE
;
2090 void CacheIndex::ReadIndexFromDisk(const StaticMutexAutoLock
& aProofOfLock
) {
2091 sLock
.AssertCurrentThreadOwns();
2092 LOG(("CacheIndex::ReadIndexFromDisk()"));
2096 MOZ_ASSERT(mState
== INITIAL
);
2098 ChangeState(READING
, aProofOfLock
);
2100 mIndexFileOpener
= new FileOpenHelper(this);
2101 rv
= CacheFileIOManager::OpenFile(
2102 nsLiteralCString(INDEX_NAME
),
2103 CacheFileIOManager::SPECIAL_FILE
| CacheFileIOManager::OPEN
,
2105 if (NS_FAILED(rv
)) {
2107 ("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2108 "failed [rv=0x%08" PRIx32
", file=%s]",
2109 static_cast<uint32_t>(rv
), INDEX_NAME
));
2110 FinishRead(false, aProofOfLock
);
2114 mJournalFileOpener
= new FileOpenHelper(this);
2115 rv
= CacheFileIOManager::OpenFile(
2116 nsLiteralCString(JOURNAL_NAME
),
2117 CacheFileIOManager::SPECIAL_FILE
| CacheFileIOManager::OPEN
,
2118 mJournalFileOpener
);
2119 if (NS_FAILED(rv
)) {
2121 ("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2122 "failed [rv=0x%08" PRIx32
", file=%s]",
2123 static_cast<uint32_t>(rv
), JOURNAL_NAME
));
2124 FinishRead(false, aProofOfLock
);
2127 mTmpFileOpener
= new FileOpenHelper(this);
2128 rv
= CacheFileIOManager::OpenFile(
2129 nsLiteralCString(TEMP_INDEX_NAME
),
2130 CacheFileIOManager::SPECIAL_FILE
| CacheFileIOManager::OPEN
,
2132 if (NS_FAILED(rv
)) {
2134 ("CacheIndex::ReadIndexFromDisk() - CacheFileIOManager::OpenFile() "
2135 "failed [rv=0x%08" PRIx32
", file=%s]",
2136 static_cast<uint32_t>(rv
), TEMP_INDEX_NAME
));
2137 FinishRead(false, aProofOfLock
);
2141 void CacheIndex::StartReadingIndex(const StaticMutexAutoLock
& aProofOfLock
) {
2142 sLock
.AssertCurrentThreadOwns();
2143 LOG(("CacheIndex::StartReadingIndex()"));
2147 MOZ_ASSERT(mIndexHandle
);
2148 MOZ_ASSERT(mState
== READING
);
2149 MOZ_ASSERT(!mIndexOnDiskIsValid
);
2150 MOZ_ASSERT(!mDontMarkIndexClean
);
2151 MOZ_ASSERT(!mJournalReadSuccessfully
);
2152 MOZ_ASSERT(mIndexHandle
->FileSize() >= 0);
2153 MOZ_ASSERT(!mRWPending
);
2155 int64_t entriesSize
= mIndexHandle
->FileSize() - sizeof(CacheIndexHeader
) -
2156 sizeof(CacheHash::Hash32_t
);
2158 if (entriesSize
< 0 || entriesSize
% sizeof(CacheIndexRecord
)) {
2159 LOG(("CacheIndex::StartReadingIndex() - Index is corrupted"));
2160 FinishRead(false, aProofOfLock
);
2166 mRWHash
= new CacheHash();
2169 std::min(mRWBufSize
, static_cast<uint32_t>(mIndexHandle
->FileSize()));
2171 rv
= CacheFileIOManager::Read(mIndexHandle
, 0, mRWBuf
, mRWBufPos
, this);
2172 if (NS_FAILED(rv
)) {
2174 ("CacheIndex::StartReadingIndex() - CacheFileIOManager::Read() failed "
2175 "synchronously [rv=0x%08" PRIx32
"]",
2176 static_cast<uint32_t>(rv
)));
2177 FinishRead(false, aProofOfLock
);
2183 void CacheIndex::ParseRecords(const StaticMutexAutoLock
& aProofOfLock
) {
2184 sLock
.AssertCurrentThreadOwns();
2185 LOG(("CacheIndex::ParseRecords()"));
2189 MOZ_ASSERT(!mRWPending
);
2191 uint32_t entryCnt
= (mIndexHandle
->FileSize() - sizeof(CacheIndexHeader
) -
2192 sizeof(CacheHash::Hash32_t
)) /
2193 sizeof(CacheIndexRecord
);
2196 if (!mSkipEntries
) {
2197 if (NetworkEndian::readUint32(mRWBuf
+ pos
) != kIndexVersion
) {
2198 FinishRead(false, aProofOfLock
);
2201 pos
+= sizeof(uint32_t);
2203 mIndexTimeStamp
= NetworkEndian::readUint32(mRWBuf
+ pos
);
2204 pos
+= sizeof(uint32_t);
2206 if (NetworkEndian::readUint32(mRWBuf
+ pos
)) {
2207 if (mJournalHandle
) {
2208 CacheFileIOManager::DoomFile(mJournalHandle
, nullptr);
2209 mJournalHandle
= nullptr;
2213 reinterpret_cast<uint32_t*>(moz_xmalloc(sizeof(uint32_t)));
2214 NetworkEndian::writeUint32(isDirty
, 1);
2216 // Mark index dirty. The buffer will be freed by CacheFileIOManager.
2217 CacheFileIOManager::WriteWithoutCallback(
2218 mIndexHandle
, 2 * sizeof(uint32_t), reinterpret_cast<char*>(isDirty
),
2219 sizeof(uint32_t), true, false);
2221 pos
+= sizeof(uint32_t);
2223 uint64_t dataWritten
= NetworkEndian::readUint32(mRWBuf
+ pos
);
2224 pos
+= sizeof(uint32_t);
2226 mTotalBytesWritten
+= dataWritten
;
2229 uint32_t hashOffset
= pos
;
2231 while (pos
+ sizeof(CacheIndexRecord
) <= mRWBufPos
&&
2232 mSkipEntries
!= entryCnt
) {
2233 CacheIndexRecord
* rec
= reinterpret_cast<CacheIndexRecord
*>(mRWBuf
+ pos
);
2234 CacheIndexEntry
tmpEntry(&rec
->mHash
);
2235 tmpEntry
.ReadFromBuf(mRWBuf
+ pos
);
2237 if (tmpEntry
.IsDirty() || !tmpEntry
.IsInitialized() ||
2238 tmpEntry
.IsFileEmpty() || tmpEntry
.IsFresh() || tmpEntry
.IsRemoved()) {
2240 ("CacheIndex::ParseRecords() - Invalid entry found in index, removing"
2241 " whole index [dirty=%d, initialized=%d, fileEmpty=%d, fresh=%d, "
2243 tmpEntry
.IsDirty(), tmpEntry
.IsInitialized(), tmpEntry
.IsFileEmpty(),
2244 tmpEntry
.IsFresh(), tmpEntry
.IsRemoved()));
2245 FinishRead(false, aProofOfLock
);
2249 CacheIndexEntryAutoManage
emng(tmpEntry
.Hash(), this, aProofOfLock
);
2251 CacheIndexEntry
* entry
= mIndex
.PutEntry(*tmpEntry
.Hash());
2254 pos
+= sizeof(CacheIndexRecord
);
2258 mRWHash
->Update(mRWBuf
+ hashOffset
, pos
- hashOffset
);
2260 if (pos
!= mRWBufPos
) {
2261 memmove(mRWBuf
, mRWBuf
+ pos
, mRWBufPos
- pos
);
2267 int64_t fileOffset
= sizeof(CacheIndexHeader
) +
2268 mSkipEntries
* sizeof(CacheIndexRecord
) + mRWBufPos
;
2270 MOZ_ASSERT(fileOffset
<= mIndexHandle
->FileSize());
2271 if (fileOffset
== mIndexHandle
->FileSize()) {
2272 uint32_t expectedHash
= NetworkEndian::readUint32(mRWBuf
);
2273 if (mRWHash
->GetHash() != expectedHash
) {
2274 LOG(("CacheIndex::ParseRecords() - Hash mismatch, [is %x, should be %x]",
2275 mRWHash
->GetHash(), expectedHash
));
2276 FinishRead(false, aProofOfLock
);
2280 mIndexOnDiskIsValid
= true;
2281 mJournalReadSuccessfully
= false;
2283 if (mJournalHandle
) {
2284 StartReadingJournal(aProofOfLock
);
2286 FinishRead(false, aProofOfLock
);
2294 std::min(mRWBufSize
- pos
,
2295 static_cast<uint32_t>(mIndexHandle
->FileSize() - fileOffset
));
2296 mRWBufPos
= pos
+ toRead
;
2298 rv
= CacheFileIOManager::Read(mIndexHandle
, fileOffset
, mRWBuf
+ pos
, toRead
,
2300 if (NS_FAILED(rv
)) {
2302 ("CacheIndex::ParseRecords() - CacheFileIOManager::Read() failed "
2303 "synchronously [rv=0x%08" PRIx32
"]",
2304 static_cast<uint32_t>(rv
)));
2305 FinishRead(false, aProofOfLock
);
2311 void CacheIndex::StartReadingJournal(const StaticMutexAutoLock
& aProofOfLock
) {
2312 sLock
.AssertCurrentThreadOwns();
2313 LOG(("CacheIndex::StartReadingJournal()"));
2317 MOZ_ASSERT(mJournalHandle
);
2318 MOZ_ASSERT(mIndexOnDiskIsValid
);
2319 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2320 MOZ_ASSERT(mJournalHandle
->FileSize() >= 0);
2321 MOZ_ASSERT(!mRWPending
);
2323 int64_t entriesSize
=
2324 mJournalHandle
->FileSize() - sizeof(CacheHash::Hash32_t
);
2326 if (entriesSize
< 0 || entriesSize
% sizeof(CacheIndexRecord
)) {
2327 LOG(("CacheIndex::StartReadingJournal() - Journal is corrupted"));
2328 FinishRead(false, aProofOfLock
);
2333 mRWHash
= new CacheHash();
2336 std::min(mRWBufSize
, static_cast<uint32_t>(mJournalHandle
->FileSize()));
2338 rv
= CacheFileIOManager::Read(mJournalHandle
, 0, mRWBuf
, mRWBufPos
, this);
2339 if (NS_FAILED(rv
)) {
2341 ("CacheIndex::StartReadingJournal() - CacheFileIOManager::Read() failed"
2342 " synchronously [rv=0x%08" PRIx32
"]",
2343 static_cast<uint32_t>(rv
)));
2344 FinishRead(false, aProofOfLock
);
2350 void CacheIndex::ParseJournal(const StaticMutexAutoLock
& aProofOfLock
) {
2351 sLock
.AssertCurrentThreadOwns();
2352 LOG(("CacheIndex::ParseJournal()"));
2356 MOZ_ASSERT(!mRWPending
);
2359 (mJournalHandle
->FileSize() - sizeof(CacheHash::Hash32_t
)) /
2360 sizeof(CacheIndexRecord
);
2364 while (pos
+ sizeof(CacheIndexRecord
) <= mRWBufPos
&&
2365 mSkipEntries
!= entryCnt
) {
2366 CacheIndexEntry
tmpEntry(reinterpret_cast<SHA1Sum::Hash
*>(mRWBuf
+ pos
));
2367 tmpEntry
.ReadFromBuf(mRWBuf
+ pos
);
2369 CacheIndexEntry
* entry
= mTmpJournal
.PutEntry(*tmpEntry
.Hash());
2372 if (entry
->IsDirty() || entry
->IsFresh()) {
2374 ("CacheIndex::ParseJournal() - Invalid entry found in journal, "
2375 "ignoring whole journal [dirty=%d, fresh=%d]",
2376 entry
->IsDirty(), entry
->IsFresh()));
2377 FinishRead(false, aProofOfLock
);
2381 pos
+= sizeof(CacheIndexRecord
);
2385 mRWHash
->Update(mRWBuf
, pos
);
2387 if (pos
!= mRWBufPos
) {
2388 memmove(mRWBuf
, mRWBuf
+ pos
, mRWBufPos
- pos
);
2394 int64_t fileOffset
= mSkipEntries
* sizeof(CacheIndexRecord
) + mRWBufPos
;
2396 MOZ_ASSERT(fileOffset
<= mJournalHandle
->FileSize());
2397 if (fileOffset
== mJournalHandle
->FileSize()) {
2398 uint32_t expectedHash
= NetworkEndian::readUint32(mRWBuf
);
2399 if (mRWHash
->GetHash() != expectedHash
) {
2400 LOG(("CacheIndex::ParseJournal() - Hash mismatch, [is %x, should be %x]",
2401 mRWHash
->GetHash(), expectedHash
));
2402 FinishRead(false, aProofOfLock
);
2406 mJournalReadSuccessfully
= true;
2407 FinishRead(true, aProofOfLock
);
2413 std::min(mRWBufSize
- pos
,
2414 static_cast<uint32_t>(mJournalHandle
->FileSize() - fileOffset
));
2415 mRWBufPos
= pos
+ toRead
;
2417 rv
= CacheFileIOManager::Read(mJournalHandle
, fileOffset
, mRWBuf
+ pos
,
2419 if (NS_FAILED(rv
)) {
2421 ("CacheIndex::ParseJournal() - CacheFileIOManager::Read() failed "
2422 "synchronously [rv=0x%08" PRIx32
"]",
2423 static_cast<uint32_t>(rv
)));
2424 FinishRead(false, aProofOfLock
);
2430 void CacheIndex::MergeJournal(const StaticMutexAutoLock
& aProofOfLock
) {
2431 sLock
.AssertCurrentThreadOwns();
2432 LOG(("CacheIndex::MergeJournal()"));
2434 for (auto iter
= mTmpJournal
.Iter(); !iter
.Done(); iter
.Next()) {
2435 CacheIndexEntry
* entry
= iter
.Get();
2437 LOG(("CacheIndex::MergeJournal() [hash=%08x%08x%08x%08x%08x]",
2438 LOGSHA1(entry
->Hash())));
2440 CacheIndexEntry
* entry2
= mIndex
.GetEntry(*entry
->Hash());
2442 CacheIndexEntryAutoManage
emng(entry
->Hash(), this, aProofOfLock
);
2443 if (entry
->IsRemoved()) {
2445 entry2
->MarkRemoved();
2446 entry2
->MarkDirty();
2450 entry2
= mIndex
.PutEntry(*entry
->Hash());
2454 entry2
->MarkDirty();
2460 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2463 void CacheIndex::EnsureNoFreshEntry() {
2465 CacheIndexStats debugStats
;
2466 debugStats
.DisableLogging();
2467 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
2468 debugStats
.BeforeChange(nullptr);
2469 debugStats
.AfterChange(iter
.Get());
2471 MOZ_ASSERT(debugStats
.Fresh() == 0);
2475 void CacheIndex::EnsureCorrectStats() {
2477 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2478 CacheIndexStats debugStats
;
2479 debugStats
.DisableLogging();
2480 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
2481 debugStats
.BeforeChange(nullptr);
2482 debugStats
.AfterChange(iter
.Get());
2484 MOZ_ASSERT(debugStats
== mIndexStats
);
2488 void CacheIndex::FinishRead(bool aSucceeded
,
2489 const StaticMutexAutoLock
& aProofOfLock
) {
2490 sLock
.AssertCurrentThreadOwns();
2491 LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded
));
2493 MOZ_ASSERT((!aSucceeded
&& mState
== SHUTDOWN
) || mState
== READING
);
2497 (!aSucceeded
&& !mIndexOnDiskIsValid
&& !mJournalReadSuccessfully
) ||
2499 (!aSucceeded
&& mIndexOnDiskIsValid
&& !mJournalReadSuccessfully
) ||
2501 (aSucceeded
&& mIndexOnDiskIsValid
&& mJournalReadSuccessfully
));
2503 // If there is read operation pending we must be cancelling reading of the
2504 // index when shutting down or removing the whole index.
2505 MOZ_ASSERT(!mRWPending
|| (!aSucceeded
&& (mShuttingDown
|| mRemovingAll
)));
2507 if (mState
== SHUTDOWN
) {
2508 RemoveFile(nsLiteralCString(TEMP_INDEX_NAME
));
2509 RemoveFile(nsLiteralCString(JOURNAL_NAME
));
2511 if (mIndexHandle
&& !mIndexOnDiskIsValid
) {
2512 CacheFileIOManager::DoomFile(mIndexHandle
, nullptr);
2515 if (mJournalHandle
) {
2516 CacheFileIOManager::DoomFile(mJournalHandle
, nullptr);
2520 if (mIndexFileOpener
) {
2521 mIndexFileOpener
->Cancel();
2522 mIndexFileOpener
= nullptr;
2524 if (mJournalFileOpener
) {
2525 mJournalFileOpener
->Cancel();
2526 mJournalFileOpener
= nullptr;
2528 if (mTmpFileOpener
) {
2529 mTmpFileOpener
->Cancel();
2530 mTmpFileOpener
= nullptr;
2533 mIndexHandle
= nullptr;
2534 mJournalHandle
= nullptr;
2538 if (mState
== SHUTDOWN
) {
2542 if (!mIndexOnDiskIsValid
) {
2543 MOZ_ASSERT(mTmpJournal
.Count() == 0);
2544 EnsureNoFreshEntry();
2545 ProcessPendingOperations(aProofOfLock
);
2546 // Remove all entries that we haven't seen during this session
2547 RemoveNonFreshEntries(aProofOfLock
);
2548 StartUpdatingIndex(true, aProofOfLock
);
2552 if (!mJournalReadSuccessfully
) {
2553 mTmpJournal
.Clear();
2554 EnsureNoFreshEntry();
2555 ProcessPendingOperations(aProofOfLock
);
2556 StartUpdatingIndex(false, aProofOfLock
);
2560 MergeJournal(aProofOfLock
);
2561 EnsureNoFreshEntry();
2562 ProcessPendingOperations(aProofOfLock
);
2565 ChangeState(READY
, aProofOfLock
);
2566 mLastDumpTime
= TimeStamp::NowLoRes(); // Do not dump new index immediately
2570 void CacheIndex::DelayedUpdate(nsITimer
* aTimer
, void* aClosure
) {
2571 LOG(("CacheIndex::DelayedUpdate()"));
2573 StaticMutexAutoLock
lock(sLock
);
2574 RefPtr
<CacheIndex
> index
= gInstance
;
2580 index
->DelayedUpdateLocked(lock
);
2584 void CacheIndex::DelayedUpdateLocked(const StaticMutexAutoLock
& aProofOfLock
) {
2585 sLock
.AssertCurrentThreadOwns();
2586 LOG(("CacheIndex::DelayedUpdateLocked()"));
2590 mUpdateTimer
= nullptr;
2592 if (!IsIndexUsable()) {
2596 if (mState
== READY
&& mShuttingDown
) {
2600 // mUpdateEventPending must be false here since StartUpdatingIndex() won't
2601 // schedule timer if it is true.
2602 MOZ_ASSERT(!mUpdateEventPending
);
2603 if (mState
!= BUILDING
&& mState
!= UPDATING
) {
2604 LOG(("CacheIndex::DelayedUpdateLocked() - Update was canceled"));
2608 // We need to redispatch to run with lower priority
2609 RefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
2610 MOZ_ASSERT(ioThread
);
2612 mUpdateEventPending
= true;
2613 rv
= ioThread
->Dispatch(this, CacheIOThread::INDEX
);
2614 if (NS_FAILED(rv
)) {
2615 mUpdateEventPending
= false;
2616 NS_WARNING("CacheIndex::DelayedUpdateLocked() - Can't dispatch event");
2617 LOG(("CacheIndex::DelayedUpdate() - Can't dispatch event"));
2618 FinishUpdate(false, aProofOfLock
);
2622 nsresult
CacheIndex::ScheduleUpdateTimer(uint32_t aDelay
) {
2623 LOG(("CacheIndex::ScheduleUpdateTimer() [delay=%u]", aDelay
));
2625 MOZ_ASSERT(!mUpdateTimer
);
2627 nsCOMPtr
<nsIEventTarget
> ioTarget
= CacheFileIOManager::IOTarget();
2628 MOZ_ASSERT(ioTarget
);
2630 return NS_NewTimerWithFuncCallback(
2631 getter_AddRefs(mUpdateTimer
), CacheIndex::DelayedUpdate
, nullptr, aDelay
,
2632 nsITimer::TYPE_ONE_SHOT
, "net::CacheIndex::ScheduleUpdateTimer",
2636 nsresult
CacheIndex::SetupDirectoryEnumerator() {
2637 MOZ_ASSERT(!NS_IsMainThread());
2638 MOZ_ASSERT(!mDirEnumerator
);
2641 nsCOMPtr
<nsIFile
> file
;
2643 rv
= mCacheDirectory
->Clone(getter_AddRefs(file
));
2644 NS_ENSURE_SUCCESS(rv
, rv
);
2646 rv
= file
->AppendNative(nsLiteralCString(ENTRIES_DIR
));
2647 NS_ENSURE_SUCCESS(rv
, rv
);
2650 rv
= file
->Exists(&exists
);
2651 NS_ENSURE_SUCCESS(rv
, rv
);
2655 "CacheIndex::SetupDirectoryEnumerator() - Entries directory "
2658 ("CacheIndex::SetupDirectoryEnumerator() - Entries directory doesn't "
2660 return NS_ERROR_UNEXPECTED
;
2663 // Do not do IO under the lock.
2664 nsCOMPtr
<nsIDirectoryEnumerator
> dirEnumerator
;
2666 StaticMutexAutoUnlock
unlock(sLock
);
2667 rv
= file
->GetDirectoryEntries(getter_AddRefs(dirEnumerator
));
2669 mDirEnumerator
= dirEnumerator
.forget();
2670 NS_ENSURE_SUCCESS(rv
, rv
);
2675 nsresult
CacheIndex::InitEntryFromDiskData(CacheIndexEntry
* aEntry
,
2676 CacheFileMetadata
* aMetaData
,
2677 int64_t aFileSize
) {
2681 aEntry
->MarkDirty();
2682 aEntry
->MarkFresh();
2684 aEntry
->Init(GetOriginAttrsHash(aMetaData
->OriginAttributes()),
2685 aMetaData
->IsAnonymous(), aMetaData
->Pinned());
2687 aEntry
->SetFrecency(aMetaData
->GetFrecency());
2689 const char* altData
= aMetaData
->GetElement(CacheFileUtils::kAltDataKey
);
2690 bool hasAltData
= altData
!= nullptr;
2691 if (hasAltData
&& NS_FAILED(CacheFileUtils::ParseAlternativeDataInfo(
2692 altData
, nullptr, nullptr))) {
2693 return NS_ERROR_FAILURE
;
2695 aEntry
->SetHasAltData(hasAltData
);
2697 static auto toUint16
= [](const char* aUint16String
) -> uint16_t {
2698 if (!aUint16String
) {
2699 return kIndexTimeNotAvailable
;
2702 uint64_t n64
= nsDependentCString(aUint16String
).ToInteger64(&rv
);
2703 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2704 return n64
<= kIndexTimeOutOfBound
? n64
: kIndexTimeOutOfBound
;
2707 aEntry
->SetOnStartTime(
2708 toUint16(aMetaData
->GetElement("net-response-time-onstart")));
2709 aEntry
->SetOnStopTime(
2710 toUint16(aMetaData
->GetElement("net-response-time-onstop")));
2712 const char* contentTypeStr
= aMetaData
->GetElement("ctid");
2713 uint8_t contentType
= nsICacheEntry::CONTENT_TYPE_UNKNOWN
;
2714 if (contentTypeStr
) {
2715 int64_t n64
= nsDependentCString(contentTypeStr
).ToInteger64(&rv
);
2716 if (NS_FAILED(rv
) || n64
< nsICacheEntry::CONTENT_TYPE_UNKNOWN
||
2717 n64
>= nsICacheEntry::CONTENT_TYPE_LAST
) {
2718 n64
= nsICacheEntry::CONTENT_TYPE_UNKNOWN
;
2722 aEntry
->SetContentType(contentType
);
2724 aEntry
->SetFileSize(static_cast<uint32_t>(std::min(
2725 static_cast<int64_t>(PR_UINT32_MAX
), (aFileSize
+ 0x3FF) >> 10)));
2729 bool CacheIndex::IsUpdatePending() {
2730 sLock
.AssertCurrentThreadOwns();
2732 return mUpdateTimer
|| mUpdateEventPending
;
2735 void CacheIndex::BuildIndex(const StaticMutexAutoLock
& aProofOfLock
) {
2736 sLock
.AssertCurrentThreadOwns();
2737 LOG(("CacheIndex::BuildIndex()"));
2739 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2743 if (!mDirEnumerator
) {
2744 rv
= SetupDirectoryEnumerator();
2745 if (mState
== SHUTDOWN
) {
2746 // The index was shut down while we released the lock. FinishUpdate() was
2747 // already called from Shutdown(), so just simply return here.
2751 if (NS_FAILED(rv
)) {
2752 FinishUpdate(false, aProofOfLock
);
2758 if (CacheIOThread::YieldAndRerun()) {
2760 "CacheIndex::BuildIndex() - Breaking loop for higher level events."));
2761 mUpdateEventPending
= true;
2765 bool fileExists
= false;
2766 nsCOMPtr
<nsIFile
> file
;
2768 // Do not do IO under the lock.
2769 nsCOMPtr
<nsIDirectoryEnumerator
> dirEnumerator(mDirEnumerator
);
2770 sLock
.AssertCurrentThreadOwns();
2771 StaticMutexAutoUnlock
unlock(sLock
);
2772 rv
= dirEnumerator
->GetNextFile(getter_AddRefs(file
));
2775 file
->Exists(&fileExists
);
2778 if (mState
== SHUTDOWN
) {
2782 FinishUpdate(NS_SUCCEEDED(rv
), aProofOfLock
);
2787 rv
= file
->GetNativeLeafName(leaf
);
2788 if (NS_FAILED(rv
)) {
2790 ("CacheIndex::BuildIndex() - GetNativeLeafName() failed! Skipping "
2792 mDontMarkIndexClean
= true;
2798 ("CacheIndex::BuildIndex() - File returned by the iterator was "
2799 "removed in the meantime [name=%s]",
2805 rv
= CacheFileIOManager::StrToHash(leaf
, &hash
);
2806 if (NS_FAILED(rv
)) {
2808 ("CacheIndex::BuildIndex() - Filename is not a hash, removing file. "
2811 file
->Remove(false);
2815 CacheIndexEntry
* entry
= mIndex
.GetEntry(hash
);
2816 if (entry
&& entry
->IsRemoved()) {
2818 ("CacheIndex::BuildIndex() - Found file that should not exist. "
2822 MOZ_ASSERT(entry
->IsFresh());
2827 RefPtr
<CacheFileHandle
> handle
;
2828 CacheFileIOManager::gInstance
->mHandles
.GetHandle(&hash
,
2829 getter_AddRefs(handle
));
2833 // the entry is up to date
2835 ("CacheIndex::BuildIndex() - Skipping file because the entry is up to"
2839 MOZ_ASSERT(entry
->IsFresh()); // The entry must be from this session
2840 // there must be an active CacheFile if the entry is not initialized
2841 MOZ_ASSERT(entry
->IsInitialized() || handle
);
2845 MOZ_ASSERT(!handle
);
2847 RefPtr
<CacheFileMetadata
> meta
= new CacheFileMetadata();
2851 // Do not do IO under the lock.
2852 StaticMutexAutoUnlock
unlock(sLock
);
2853 rv
= meta
->SyncReadMetadata(file
);
2855 if (NS_SUCCEEDED(rv
)) {
2856 rv
= file
->GetFileSize(&size
);
2857 if (NS_FAILED(rv
)) {
2859 ("CacheIndex::BuildIndex() - Cannot get filesize of file that was"
2860 " successfully parsed. [name=%s]",
2865 if (mState
== SHUTDOWN
) {
2869 // Nobody could add the entry while the lock was released since we modify
2870 // the index only on IO thread and this loop is executed on IO thread too.
2871 entry
= mIndex
.GetEntry(hash
);
2872 MOZ_ASSERT(!entry
|| entry
->IsRemoved());
2874 if (NS_FAILED(rv
)) {
2876 ("CacheIndex::BuildIndex() - CacheFileMetadata::SyncReadMetadata() "
2877 "failed, removing file. [name=%s]",
2879 file
->Remove(false);
2881 CacheIndexEntryAutoManage
entryMng(&hash
, this, aProofOfLock
);
2882 entry
= mIndex
.PutEntry(hash
);
2883 if (NS_FAILED(InitEntryFromDiskData(entry
, meta
, size
))) {
2885 ("CacheIndex::BuildIndex() - CacheFile::InitEntryFromDiskData() "
2886 "failed, removing file. [name=%s]",
2888 file
->Remove(false);
2889 entry
->MarkRemoved();
2891 LOG(("CacheIndex::BuildIndex() - Added entry to index. [name=%s]",
2898 MOZ_ASSERT_UNREACHABLE("We should never get here");
2901 bool CacheIndex::StartUpdatingIndexIfNeeded(
2902 const StaticMutexAutoLock
& aProofOfLock
, bool aSwitchingToReadyState
) {
2903 sLock
.AssertCurrentThreadOwns();
2904 // Start updating process when we are in or we are switching to READY state
2905 // and index needs update, but not during shutdown or when removing all
2907 if ((mState
== READY
|| aSwitchingToReadyState
) && mIndexNeedsUpdate
&&
2908 !mShuttingDown
&& !mRemovingAll
) {
2909 LOG(("CacheIndex::StartUpdatingIndexIfNeeded() - starting update process"));
2910 mIndexNeedsUpdate
= false;
2911 StartUpdatingIndex(false, aProofOfLock
);
2918 void CacheIndex::StartUpdatingIndex(bool aRebuild
,
2919 const StaticMutexAutoLock
& aProofOfLock
) {
2920 sLock
.AssertCurrentThreadOwns();
2921 LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild
));
2927 ChangeState(aRebuild
? BUILDING
: UPDATING
, aProofOfLock
);
2928 mDontMarkIndexClean
= false;
2930 if (mShuttingDown
|| mRemovingAll
) {
2931 FinishUpdate(false, aProofOfLock
);
2935 if (IsUpdatePending()) {
2936 LOG(("CacheIndex::StartUpdatingIndex() - Update is already pending"));
2940 uint32_t elapsed
= (TimeStamp::NowLoRes() - mStartTime
).ToMilliseconds();
2941 if (elapsed
< kUpdateIndexStartDelay
) {
2943 ("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
2944 "scheduling timer to fire in %u ms.",
2945 elapsed
, kUpdateIndexStartDelay
- elapsed
));
2946 rv
= ScheduleUpdateTimer(kUpdateIndexStartDelay
- elapsed
);
2947 if (NS_SUCCEEDED(rv
)) {
2952 ("CacheIndex::StartUpdatingIndex() - ScheduleUpdateTimer() failed. "
2953 "Starting update immediately."));
2956 ("CacheIndex::StartUpdatingIndex() - %u ms elapsed since startup, "
2957 "starting update now.",
2961 RefPtr
<CacheIOThread
> ioThread
= CacheFileIOManager::IOThread();
2962 MOZ_ASSERT(ioThread
);
2964 // We need to dispatch an event even if we are on IO thread since we need to
2965 // update the index with the correct priority.
2966 mUpdateEventPending
= true;
2967 rv
= ioThread
->Dispatch(this, CacheIOThread::INDEX
);
2968 if (NS_FAILED(rv
)) {
2969 mUpdateEventPending
= false;
2970 NS_WARNING("CacheIndex::StartUpdatingIndex() - Can't dispatch event");
2971 LOG(("CacheIndex::StartUpdatingIndex() - Can't dispatch event"));
2972 FinishUpdate(false, aProofOfLock
);
2976 void CacheIndex::UpdateIndex(const StaticMutexAutoLock
& aProofOfLock
) {
2977 sLock
.AssertCurrentThreadOwns();
2978 LOG(("CacheIndex::UpdateIndex()"));
2980 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
2981 sLock
.AssertCurrentThreadOwns();
2985 if (!mDirEnumerator
) {
2986 rv
= SetupDirectoryEnumerator();
2987 if (mState
== SHUTDOWN
) {
2988 // The index was shut down while we released the lock. FinishUpdate() was
2989 // already called from Shutdown(), so just simply return here.
2993 if (NS_FAILED(rv
)) {
2994 FinishUpdate(false, aProofOfLock
);
3000 if (CacheIOThread::YieldAndRerun()) {
3002 ("CacheIndex::UpdateIndex() - Breaking loop for higher level "
3004 mUpdateEventPending
= true;
3008 bool fileExists
= false;
3009 nsCOMPtr
<nsIFile
> file
;
3011 // Do not do IO under the lock.
3012 nsCOMPtr
<nsIDirectoryEnumerator
> dirEnumerator(mDirEnumerator
);
3013 StaticMutexAutoUnlock
unlock(sLock
);
3014 rv
= dirEnumerator
->GetNextFile(getter_AddRefs(file
));
3017 file
->Exists(&fileExists
);
3020 if (mState
== SHUTDOWN
) {
3024 FinishUpdate(NS_SUCCEEDED(rv
), aProofOfLock
);
3029 rv
= file
->GetNativeLeafName(leaf
);
3030 if (NS_FAILED(rv
)) {
3032 ("CacheIndex::UpdateIndex() - GetNativeLeafName() failed! Skipping "
3034 mDontMarkIndexClean
= true;
3040 ("CacheIndex::UpdateIndex() - File returned by the iterator was "
3041 "removed in the meantime [name=%s]",
3047 rv
= CacheFileIOManager::StrToHash(leaf
, &hash
);
3048 if (NS_FAILED(rv
)) {
3050 ("CacheIndex::UpdateIndex() - Filename is not a hash, removing file. "
3053 file
->Remove(false);
3057 CacheIndexEntry
* entry
= mIndex
.GetEntry(hash
);
3058 if (entry
&& entry
->IsRemoved()) {
3059 if (entry
->IsFresh()) {
3061 ("CacheIndex::UpdateIndex() - Found file that should not exist. "
3070 RefPtr
<CacheFileHandle
> handle
;
3071 CacheFileIOManager::gInstance
->mHandles
.GetHandle(&hash
,
3072 getter_AddRefs(handle
));
3075 if (entry
&& entry
->IsFresh()) {
3076 // the entry is up to date
3078 ("CacheIndex::UpdateIndex() - Skipping file because the entry is up "
3079 " to date. [name=%s]",
3082 // there must be an active CacheFile if the entry is not initialized
3083 MOZ_ASSERT(entry
->IsInitialized() || handle
);
3087 MOZ_ASSERT(!handle
);
3090 PRTime lastModifiedTime
;
3092 // Do not do IO under the lock.
3093 StaticMutexAutoUnlock
unlock(sLock
);
3094 rv
= file
->GetLastModifiedTime(&lastModifiedTime
);
3096 if (mState
== SHUTDOWN
) {
3099 if (NS_FAILED(rv
)) {
3101 ("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. "
3104 // Assume the file is newer than index
3106 if (mIndexTimeStamp
> (lastModifiedTime
/ PR_MSEC_PER_SEC
)) {
3108 ("CacheIndex::UpdateIndex() - Skipping file because of last "
3109 "modified time. [name=%s, indexTimeStamp=%" PRIu32
", "
3110 "lastModifiedTime=%" PRId64
"]",
3111 leaf
.get(), mIndexTimeStamp
,
3112 lastModifiedTime
/ PR_MSEC_PER_SEC
));
3114 CacheIndexEntryAutoManage
entryMng(&hash
, this, aProofOfLock
);
3121 RefPtr
<CacheFileMetadata
> meta
= new CacheFileMetadata();
3125 // Do not do IO under the lock.
3126 StaticMutexAutoUnlock
unlock(sLock
);
3127 rv
= meta
->SyncReadMetadata(file
);
3129 if (NS_SUCCEEDED(rv
)) {
3130 rv
= file
->GetFileSize(&size
);
3131 if (NS_FAILED(rv
)) {
3133 ("CacheIndex::UpdateIndex() - Cannot get filesize of file that "
3134 "was successfully parsed. [name=%s]",
3139 if (mState
== SHUTDOWN
) {
3143 // Nobody could add the entry while the lock was released since we modify
3144 // the index only on IO thread and this loop is executed on IO thread too.
3145 entry
= mIndex
.GetEntry(hash
);
3146 MOZ_ASSERT(!entry
|| !entry
->IsFresh());
3148 CacheIndexEntryAutoManage
entryMng(&hash
, this, aProofOfLock
);
3150 if (NS_FAILED(rv
)) {
3152 ("CacheIndex::UpdateIndex() - CacheFileMetadata::SyncReadMetadata() "
3153 "failed, removing file. [name=%s]",
3156 entry
= mIndex
.PutEntry(hash
);
3157 rv
= InitEntryFromDiskData(entry
, meta
, size
);
3158 if (NS_FAILED(rv
)) {
3160 ("CacheIndex::UpdateIndex() - CacheIndex::InitEntryFromDiskData "
3161 "failed, removing file. [name=%s]",
3166 if (NS_FAILED(rv
)) {
3167 file
->Remove(false);
3169 entry
->MarkRemoved();
3175 ("CacheIndex::UpdateIndex() - Added/updated entry to/in index. "
3182 MOZ_ASSERT_UNREACHABLE("We should never get here");
3185 void CacheIndex::FinishUpdate(bool aSucceeded
,
3186 const StaticMutexAutoLock
& aProofOfLock
) {
3187 LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded
));
3189 MOZ_ASSERT(mState
== UPDATING
|| mState
== BUILDING
||
3190 (!aSucceeded
&& mState
== SHUTDOWN
));
3192 if (mDirEnumerator
) {
3193 if (NS_IsMainThread()) {
3195 ("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
3196 " Cannot safely release mDirEnumerator, leaking it!"));
3197 NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
3198 // This can happen only in case dispatching event to IO thread failed in
3199 // CacheIndex::PreShutdown().
3200 Unused
<< mDirEnumerator
.forget(); // Leak it since dir enumerator is not
3203 mDirEnumerator
->Close();
3204 mDirEnumerator
= nullptr;
3209 mDontMarkIndexClean
= true;
3212 if (mState
== SHUTDOWN
) {
3216 if (mState
== UPDATING
&& aSucceeded
) {
3217 // If we've iterated over all entries successfully then all entries that
3218 // really exist on the disk are now marked as fresh. All non-fresh entries
3219 // don't exist anymore and must be removed from the index.
3220 RemoveNonFreshEntries(aProofOfLock
);
3223 // Make sure we won't start update. If the build or update failed, there is no
3224 // reason to believe that it will succeed next time.
3225 mIndexNeedsUpdate
= false;
3227 ChangeState(READY
, aProofOfLock
);
3228 mLastDumpTime
= TimeStamp::NowLoRes(); // Do not dump new index immediately
3231 void CacheIndex::RemoveNonFreshEntries(
3232 const StaticMutexAutoLock
& aProofOfLock
) {
3233 sLock
.AssertCurrentThreadOwns();
3234 for (auto iter
= mIndex
.Iter(); !iter
.Done(); iter
.Next()) {
3235 CacheIndexEntry
* entry
= iter
.Get();
3236 if (entry
->IsFresh()) {
3241 ("CacheIndex::RemoveNonFreshEntries() - Removing entry. "
3242 "[hash=%08x%08x%08x%08x%08x]",
3243 LOGSHA1(entry
->Hash())));
3246 CacheIndexEntryAutoManage
emng(entry
->Hash(), this, aProofOfLock
);
3247 emng
.DoNotSearchInIndex();
3255 char const* CacheIndex::StateString(EState aState
) {
3273 MOZ_ASSERT(false, "Unexpected state!");
3277 void CacheIndex::ChangeState(EState aNewState
,
3278 const StaticMutexAutoLock
& aProofOfLock
) {
3279 sLock
.AssertCurrentThreadOwns();
3280 LOG(("CacheIndex::ChangeState() changing state %s -> %s", StateString(mState
),
3281 StateString(aNewState
)));
3283 // All pending updates should be processed before changing state
3284 MOZ_ASSERT(mPendingUpdates
.Count() == 0);
3286 // PreShutdownInternal() should change the state to READY from every state. It
3287 // may go through different states, but once we are in READY state the only
3288 // possible transition is to SHUTDOWN state.
3289 MOZ_ASSERT(!mShuttingDown
|| mState
!= READY
|| aNewState
== SHUTDOWN
);
3291 // Start updating process when switching to READY state if needed
3292 if (aNewState
== READY
&& StartUpdatingIndexIfNeeded(aProofOfLock
, true)) {
3296 // Try to evict entries over limit everytime we're leaving state READING,
3297 // BUILDING or UPDATING, but not during shutdown or when removing all
3299 if (!mShuttingDown
&& !mRemovingAll
&& aNewState
!= SHUTDOWN
&&
3300 (mState
== READING
|| mState
== BUILDING
|| mState
== UPDATING
)) {
3301 CacheFileIOManager::EvictIfOverLimit();
3306 if (mState
!= SHUTDOWN
) {
3307 CacheFileIOManager::CacheIndexStateChanged();
3310 NotifyAsyncGetDiskConsumptionCallbacks();
3313 void CacheIndex::NotifyAsyncGetDiskConsumptionCallbacks() {
3314 if ((mState
== READY
|| mState
== WRITING
) &&
3315 !mAsyncGetDiskConsumptionBlocked
&& mDiskConsumptionObservers
.Length()) {
3316 for (uint32_t i
= 0; i
< mDiskConsumptionObservers
.Length(); ++i
) {
3317 DiskConsumptionObserver
* o
= mDiskConsumptionObservers
[i
];
3318 // Safe to call under the lock. We always post to the main thread.
3319 o
->OnDiskConsumption(mIndexStats
.Size() << 10);
3322 mDiskConsumptionObservers
.Clear();
3326 void CacheIndex::AllocBuffer() {
3329 mRWBufSize
= sizeof(CacheIndexHeader
) + sizeof(CacheHash::Hash32_t
) +
3330 mProcessEntries
* sizeof(CacheIndexRecord
);
3331 if (mRWBufSize
> kMaxBufSize
) {
3332 mRWBufSize
= kMaxBufSize
;
3336 mRWBufSize
= kMaxBufSize
;
3339 MOZ_ASSERT(false, "Unexpected state!");
3342 mRWBuf
= static_cast<char*>(moz_xmalloc(mRWBufSize
));
3345 void CacheIndex::ReleaseBuffer() {
3346 sLock
.AssertCurrentThreadOwns();
3348 if (!mRWBuf
|| mRWPending
) {
3352 LOG(("CacheIndex::ReleaseBuffer() releasing buffer"));
3360 void CacheIndex::FrecencyArray::AppendRecord(
3361 CacheIndexRecordWrapper
* aRecord
, const StaticMutexAutoLock
& aProofOfLock
) {
3362 sLock
.AssertCurrentThreadOwns();
3364 ("CacheIndex::FrecencyArray::AppendRecord() [record=%p, hash=%08x%08x%08x"
3366 aRecord
, LOGSHA1(aRecord
->Get()->mHash
)));
3368 MOZ_DIAGNOSTIC_ASSERT(!mRecs
.Contains(aRecord
));
3369 mRecs
.AppendElement(aRecord
);
3371 // If the new frecency is 0, the element should be at the end of the array,
3372 // i.e. this change doesn't affect order of the array
3373 if (aRecord
->Get()->mFrecency
!= 0) {
3374 ++mUnsortedElements
;
3378 void CacheIndex::FrecencyArray::RemoveRecord(
3379 CacheIndexRecordWrapper
* aRecord
, const StaticMutexAutoLock
& aProofOfLock
) {
3380 sLock
.AssertCurrentThreadOwns();
3381 LOG(("CacheIndex::FrecencyArray::RemoveRecord() [record=%p]", aRecord
));
3383 decltype(mRecs
)::index_type idx
;
3384 idx
= mRecs
.IndexOf(aRecord
);
3385 MOZ_RELEASE_ASSERT(idx
!= mRecs
.NoIndex
);
3386 // sanity check to ensure correct record removal
3387 MOZ_RELEASE_ASSERT(mRecs
[idx
] == aRecord
);
3388 mRecs
[idx
] = nullptr;
3391 // Calling SortIfNeeded ensures that we get rid of removed elements in the
3392 // array once we hit the limit.
3393 SortIfNeeded(aProofOfLock
);
3396 void CacheIndex::FrecencyArray::ReplaceRecord(
3397 CacheIndexRecordWrapper
* aOldRecord
, CacheIndexRecordWrapper
* aNewRecord
,
3398 const StaticMutexAutoLock
& aProofOfLock
) {
3399 sLock
.AssertCurrentThreadOwns();
3401 ("CacheIndex::FrecencyArray::ReplaceRecord() [oldRecord=%p, "
3403 aOldRecord
, aNewRecord
));
3405 decltype(mRecs
)::index_type idx
;
3406 idx
= mRecs
.IndexOf(aOldRecord
);
3407 MOZ_RELEASE_ASSERT(idx
!= mRecs
.NoIndex
);
3408 // sanity check to ensure correct record replaced
3409 MOZ_RELEASE_ASSERT(mRecs
[idx
] == aOldRecord
);
3410 mRecs
[idx
] = aNewRecord
;
3413 void CacheIndex::FrecencyArray::SortIfNeeded(
3414 const StaticMutexAutoLock
& aProofOfLock
) {
3415 sLock
.AssertCurrentThreadOwns();
3416 const uint32_t kMaxUnsortedCount
= 512;
3417 const uint32_t kMaxUnsortedPercent
= 10;
3418 const uint32_t kMaxRemovedCount
= 512;
3420 uint32_t unsortedLimit
= std::min
<uint32_t>(
3421 kMaxUnsortedCount
, Length() * kMaxUnsortedPercent
/ 100);
3423 if (mUnsortedElements
> unsortedLimit
||
3424 mRemovedElements
> kMaxRemovedCount
) {
3426 ("CacheIndex::FrecencyArray::SortIfNeeded() - Sorting array "
3427 "[unsortedElements=%u, unsortedLimit=%u, removedElements=%u, "
3428 "maxRemovedCount=%u]",
3429 mUnsortedElements
, unsortedLimit
, mRemovedElements
, kMaxRemovedCount
));
3431 mRecs
.Sort(FrecencyComparator());
3432 mUnsortedElements
= 0;
3433 if (mRemovedElements
) {
3434 #if defined(EARLY_BETA_OR_EARLIER)
3435 // validate only null items are removed
3436 for (uint32_t i
= Length(); i
< mRecs
.Length(); ++i
) {
3437 MOZ_DIAGNOSTIC_ASSERT(!mRecs
[i
]);
3440 // Removed elements are at the end after sorting.
3441 mRecs
.RemoveElementsAt(Length(), mRemovedElements
);
3442 mRemovedElements
= 0;
3447 bool CacheIndex::FrecencyArray::RecordExistedUnlocked(
3448 CacheIndexRecordWrapper
* aRecord
) {
3449 return mRecs
.Contains(aRecord
);
3452 void CacheIndex::AddRecordToIterators(CacheIndexRecordWrapper
* aRecord
,
3453 const StaticMutexAutoLock
& aProofOfLock
) {
3454 sLock
.AssertCurrentThreadOwns();
3455 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3456 // Add a new record only when iterator is supposed to be updated.
3457 if (mIterators
[i
]->ShouldBeNewAdded()) {
3458 mIterators
[i
]->AddRecord(aRecord
, aProofOfLock
);
3463 void CacheIndex::RemoveRecordFromIterators(
3464 CacheIndexRecordWrapper
* aRecord
, const StaticMutexAutoLock
& aProofOfLock
) {
3465 sLock
.AssertCurrentThreadOwns();
3466 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3467 // Remove the record from iterator always, it makes no sence to return
3468 // non-existing entries. Also the pointer to the record is no longer valid
3469 // once the entry is removed from index.
3470 mIterators
[i
]->RemoveRecord(aRecord
, aProofOfLock
);
3474 void CacheIndex::ReplaceRecordInIterators(
3475 CacheIndexRecordWrapper
* aOldRecord
, CacheIndexRecordWrapper
* aNewRecord
,
3476 const StaticMutexAutoLock
& aProofOfLock
) {
3477 sLock
.AssertCurrentThreadOwns();
3478 for (uint32_t i
= 0; i
< mIterators
.Length(); ++i
) {
3479 // We have to replace the record always since the pointer is no longer
3480 // valid after this point. NOTE: Replacing the record doesn't mean that
3481 // a new entry was added, it just means that the data in the entry was
3482 // changed (e.g. a file size) and we had to track this change in
3483 // mPendingUpdates since mIndex was read-only.
3484 mIterators
[i
]->ReplaceRecord(aOldRecord
, aNewRecord
, aProofOfLock
);
3488 nsresult
CacheIndex::Run() {
3489 LOG(("CacheIndex::Run()"));
3491 StaticMutexAutoLock
lock(sLock
);
3493 if (!IsIndexUsable()) {
3494 return NS_ERROR_NOT_AVAILABLE
;
3497 if (mState
== READY
&& mShuttingDown
) {
3501 mUpdateEventPending
= false;
3511 LOG(("CacheIndex::Run() - Update/Build was canceled"));
3517 void CacheIndex::OnFileOpenedInternal(FileOpenHelper
* aOpener
,
3518 CacheFileHandle
* aHandle
,
3520 const StaticMutexAutoLock
& aProofOfLock
) {
3521 sLock
.AssertCurrentThreadOwns();
3523 ("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
3524 "result=0x%08" PRIx32
"]",
3525 aOpener
, aHandle
, static_cast<uint32_t>(aResult
)));
3526 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
3530 MOZ_RELEASE_ASSERT(IsIndexUsable());
3532 if (mState
== READY
&& mShuttingDown
) {
3538 MOZ_ASSERT(aOpener
== mIndexFileOpener
);
3539 mIndexFileOpener
= nullptr;
3541 if (NS_FAILED(aResult
)) {
3543 ("CacheIndex::OnFileOpenedInternal() - Can't open index file for "
3544 "writing [rv=0x%08" PRIx32
"]",
3545 static_cast<uint32_t>(aResult
)));
3546 FinishWrite(false, aProofOfLock
);
3548 mIndexHandle
= aHandle
;
3549 WriteRecords(aProofOfLock
);
3553 if (aOpener
== mIndexFileOpener
) {
3554 mIndexFileOpener
= nullptr;
3556 if (NS_SUCCEEDED(aResult
)) {
3557 if (aHandle
->FileSize() == 0) {
3558 FinishRead(false, aProofOfLock
);
3559 CacheFileIOManager::DoomFile(aHandle
, nullptr);
3562 mIndexHandle
= aHandle
;
3564 FinishRead(false, aProofOfLock
);
3567 } else if (aOpener
== mJournalFileOpener
) {
3568 mJournalFileOpener
= nullptr;
3569 mJournalHandle
= aHandle
;
3570 } else if (aOpener
== mTmpFileOpener
) {
3571 mTmpFileOpener
= nullptr;
3572 mTmpHandle
= aHandle
;
3574 MOZ_ASSERT(false, "Unexpected state!");
3577 if (mIndexFileOpener
|| mJournalFileOpener
|| mTmpFileOpener
) {
3578 // Some opener still didn't finish
3582 // We fail and cancel all other openers when we opening index file fails.
3583 MOZ_ASSERT(mIndexHandle
);
3586 CacheFileIOManager::DoomFile(mTmpHandle
, nullptr);
3587 mTmpHandle
= nullptr;
3589 if (mJournalHandle
) { // this shouldn't normally happen
3591 ("CacheIndex::OnFileOpenedInternal() - Unexpected state, all "
3592 "files [%s, %s, %s] should never exist. Removing whole index.",
3593 INDEX_NAME
, JOURNAL_NAME
, TEMP_INDEX_NAME
));
3594 FinishRead(false, aProofOfLock
);
3599 if (mJournalHandle
) {
3600 // Rename journal to make sure we update index on next start in case
3602 rv
= CacheFileIOManager::RenameFile(
3603 mJournalHandle
, nsLiteralCString(TEMP_INDEX_NAME
), this);
3604 if (NS_FAILED(rv
)) {
3606 ("CacheIndex::OnFileOpenedInternal() - CacheFileIOManager::"
3607 "RenameFile() failed synchronously [rv=0x%08" PRIx32
"]",
3608 static_cast<uint32_t>(rv
)));
3609 FinishRead(false, aProofOfLock
);
3613 StartReadingIndex(aProofOfLock
);
3618 MOZ_ASSERT(false, "Unexpected state!");
3622 nsresult
CacheIndex::OnFileOpened(CacheFileHandle
* aHandle
, nsresult aResult
) {
3623 MOZ_CRASH("CacheIndex::OnFileOpened should not be called!");
3624 return NS_ERROR_UNEXPECTED
;
3627 nsresult
CacheIndex::OnDataWritten(CacheFileHandle
* aHandle
, const char* aBuf
,
3629 LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08" PRIx32
"]",
3630 aHandle
, static_cast<uint32_t>(aResult
)));
3632 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
3636 StaticMutexAutoLock
lock(sLock
);
3638 MOZ_RELEASE_ASSERT(IsIndexUsable());
3639 MOZ_RELEASE_ASSERT(mRWPending
);
3642 if (mState
== READY
&& mShuttingDown
) {
3648 MOZ_ASSERT(mIndexHandle
== aHandle
);
3650 if (NS_FAILED(aResult
)) {
3651 FinishWrite(false, lock
);
3653 if (mSkipEntries
== mProcessEntries
) {
3654 rv
= CacheFileIOManager::RenameFile(
3655 mIndexHandle
, nsLiteralCString(INDEX_NAME
), this);
3656 if (NS_FAILED(rv
)) {
3658 ("CacheIndex::OnDataWritten() - CacheFileIOManager::"
3659 "RenameFile() failed synchronously [rv=0x%08" PRIx32
"]",
3660 static_cast<uint32_t>(rv
)));
3661 FinishWrite(false, lock
);
3669 // Writing was canceled.
3671 ("CacheIndex::OnDataWritten() - ignoring notification since the "
3672 "operation was previously canceled [state=%d]",
3680 nsresult
CacheIndex::OnDataRead(CacheFileHandle
* aHandle
, char* aBuf
,
3682 LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08" PRIx32
"]", aHandle
,
3683 static_cast<uint32_t>(aResult
)));
3685 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
3687 StaticMutexAutoLock
lock(sLock
);
3689 MOZ_RELEASE_ASSERT(IsIndexUsable());
3690 MOZ_RELEASE_ASSERT(mRWPending
);
3695 MOZ_ASSERT(mIndexHandle
== aHandle
|| mJournalHandle
== aHandle
);
3697 if (NS_FAILED(aResult
)) {
3698 FinishRead(false, lock
);
3700 if (!mIndexOnDiskIsValid
) {
3708 // Reading was canceled.
3710 ("CacheIndex::OnDataRead() - ignoring notification since the "
3711 "operation was previously canceled [state=%d]",
3719 nsresult
CacheIndex::OnFileDoomed(CacheFileHandle
* aHandle
, nsresult aResult
) {
3720 MOZ_CRASH("CacheIndex::OnFileDoomed should not be called!");
3721 return NS_ERROR_UNEXPECTED
;
3724 nsresult
CacheIndex::OnEOFSet(CacheFileHandle
* aHandle
, nsresult aResult
) {
3725 MOZ_CRASH("CacheIndex::OnEOFSet should not be called!");
3726 return NS_ERROR_UNEXPECTED
;
3729 nsresult
CacheIndex::OnFileRenamed(CacheFileHandle
* aHandle
, nsresult aResult
) {
3730 LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08" PRIx32
"]",
3731 aHandle
, static_cast<uint32_t>(aResult
)));
3733 MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
3735 StaticMutexAutoLock
lock(sLock
);
3737 MOZ_RELEASE_ASSERT(IsIndexUsable());
3739 if (mState
== READY
&& mShuttingDown
) {
3745 // This is a result of renaming the new index written to tmpfile to index
3746 // file. This is the last step when writing the index and the whole
3747 // writing process is successful iff renaming was successful.
3749 if (mIndexHandle
!= aHandle
) {
3751 ("CacheIndex::OnFileRenamed() - ignoring notification since it "
3752 "belongs to previously canceled operation [state=%d]",
3757 FinishWrite(NS_SUCCEEDED(aResult
), lock
);
3760 // This is a result of renaming journal file to tmpfile. It is renamed
3761 // before we start reading index and journal file and it should normally
3762 // succeed. If it fails give up reading of index.
3764 if (mJournalHandle
!= aHandle
) {
3766 ("CacheIndex::OnFileRenamed() - ignoring notification since it "
3767 "belongs to previously canceled operation [state=%d]",
3772 if (NS_FAILED(aResult
)) {
3773 FinishRead(false, lock
);
3775 StartReadingIndex(lock
);
3779 // Reading/writing was canceled.
3781 ("CacheIndex::OnFileRenamed() - ignoring notification since the "
3782 "operation was previously canceled [state=%d]",
3791 size_t CacheIndex::SizeOfExcludingThisInternal(
3792 mozilla::MallocSizeOf mallocSizeOf
) const {
3793 sLock
.AssertCurrentThreadOwns();
3796 nsCOMPtr
<nsISizeOf
> sizeOf
;
3798 // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
3799 // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
3802 sizeOf
= do_QueryInterface(mCacheDirectory
);
3804 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3807 sizeOf
= do_QueryInterface(mUpdateTimer
);
3809 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
3812 n
+= mallocSizeOf(mRWBuf
);
3813 n
+= mallocSizeOf(mRWHash
);
3815 n
+= mIndex
.SizeOfExcludingThis(mallocSizeOf
);
3816 n
+= mPendingUpdates
.SizeOfExcludingThis(mallocSizeOf
);
3817 n
+= mTmpJournal
.SizeOfExcludingThis(mallocSizeOf
);
3819 // mFrecencyArray items are reported by mIndex/mPendingUpdates
3820 n
+= mFrecencyArray
.mRecs
.ShallowSizeOfExcludingThis(mallocSizeOf
);
3821 n
+= mDiskConsumptionObservers
.ShallowSizeOfExcludingThis(mallocSizeOf
);
3827 size_t CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) {
3828 StaticMutexAutoLock
lock(sLock
);
3830 if (!gInstance
) return 0;
3832 return gInstance
->SizeOfExcludingThisInternal(mallocSizeOf
);
3836 size_t CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) {
3837 StaticMutexAutoLock
lock(sLock
);
3839 return mallocSizeOf(gInstance
) +
3840 (gInstance
? gInstance
->SizeOfExcludingThisInternal(mallocSizeOf
) : 0);
3844 void CacheIndex::UpdateTotalBytesWritten(uint32_t aBytesWritten
) {
3845 StaticMutexAutoLock
lock(sLock
);
3847 RefPtr
<CacheIndex
> index
= gInstance
;
3852 index
->mTotalBytesWritten
+= aBytesWritten
;
3854 // Do telemetry report if enough data has been written and the index is
3855 // in READY state. The data is available also in WRITING state, but we would
3856 // need to deal with pending updates.
3857 if (index
->mTotalBytesWritten
>= kTelemetryReportBytesLimit
&&
3858 index
->mState
== READY
&& !index
->mIndexNeedsUpdate
&&
3859 !index
->mShuttingDown
) {
3860 index
->DoTelemetryReport();
3861 index
->mTotalBytesWritten
= 0;
3866 void CacheIndex::DoTelemetryReport() {
3867 static const nsLiteralCString
3868 contentTypeNames
[nsICacheEntry::CONTENT_TYPE_LAST
] = {
3869 "UNKNOWN"_ns
, "OTHER"_ns
, "JAVASCRIPT"_ns
, "IMAGE"_ns
,
3870 "MEDIA"_ns
, "STYLESHEET"_ns
, "WASM"_ns
};
3872 for (uint32_t i
= 0; i
< nsICacheEntry::CONTENT_TYPE_LAST
; ++i
) {
3873 if (mIndexStats
.Size() > 0) {
3874 Telemetry::Accumulate(
3875 Telemetry::NETWORK_CACHE_SIZE_SHARE
, contentTypeNames
[i
],
3876 round(static_cast<double>(mIndexStats
.SizeByType(i
)) * 100.0 /
3877 static_cast<double>(mIndexStats
.Size())));
3880 if (mIndexStats
.Count() > 0) {
3881 Telemetry::Accumulate(
3882 Telemetry::NETWORK_CACHE_ENTRY_COUNT_SHARE
, contentTypeNames
[i
],
3883 round(static_cast<double>(mIndexStats
.CountByType(i
)) * 100.0 /
3884 static_cast<double>(mIndexStats
.Count())));
3889 if (CacheObserver::SmartCacheSizeEnabled()) {
3890 probeKey
= "SMARTSIZE"_ns
;
3892 probeKey
= "USERDEFINEDSIZE"_ns
;
3894 Telemetry::Accumulate(Telemetry::NETWORK_CACHE_ENTRY_COUNT
, probeKey
,
3895 mIndexStats
.Count());
3896 Telemetry::Accumulate(Telemetry::NETWORK_CACHE_SIZE
, probeKey
,
3897 mIndexStats
.Size() >> 10);
3901 void CacheIndex::OnAsyncEviction(bool aEvicting
) {
3902 StaticMutexAutoLock
lock(sLock
);
3904 RefPtr
<CacheIndex
> index
= gInstance
;
3909 index
->mAsyncGetDiskConsumptionBlocked
= aEvicting
;
3911 index
->NotifyAsyncGetDiskConsumptionCallbacks();
3915 } // namespace mozilla::net