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/. */
6 #include "CacheEntry.h"
7 #include "CacheStorageService.h"
8 #include "CacheObserver.h"
9 #include "CacheFileUtils.h"
10 #include "CacheIndex.h"
12 #include "nsIInputStream.h"
13 #include "nsIOutputStream.h"
14 #include "nsISeekableStream.h"
16 #include "nsICacheEntryOpenCallback.h"
17 #include "nsICacheStorage.h"
18 #include "nsISerializable.h"
19 #include "nsIStreamTransportService.h"
20 #include "nsISizeOf.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsServiceManagerUtils.h"
25 #include "nsProxyRelease.h"
26 #include "nsSerializationHelper.h"
27 #include "nsThreadUtils.h"
28 #include "mozilla/Telemetry.h"
35 static uint32_t const ENTRY_WANTED
=
36 nsICacheEntryOpenCallback::ENTRY_WANTED
;
37 static uint32_t const RECHECK_AFTER_WRITE_FINISHED
=
38 nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED
;
39 static uint32_t const ENTRY_NEEDS_REVALIDATION
=
40 nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION
;
41 static uint32_t const ENTRY_NOT_WANTED
=
42 nsICacheEntryOpenCallback::ENTRY_NOT_WANTED
;
44 NS_IMPL_ISUPPORTS(CacheEntryHandle
, nsICacheEntry
)
48 CacheEntryHandle::CacheEntryHandle(CacheEntry
* aEntry
)
51 MOZ_COUNT_CTOR(CacheEntryHandle
);
54 if (!mEntry
->HandlesCount()) {
55 // CacheEntry.mHandlesCount must go from zero to one only under
56 // the service lock. Can access CacheStorageService::Self() w/o a check
57 // since CacheEntry hrefs it.
58 CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
62 mEntry
->AddHandleRef();
64 LOG(("New CacheEntryHandle %p for entry %p", this, aEntry
));
67 CacheEntryHandle::~CacheEntryHandle()
69 mEntry
->ReleaseHandleRef();
70 mEntry
->OnHandleClosed(this);
72 MOZ_COUNT_DTOR(CacheEntryHandle
);
75 // CacheEntry::Callback
77 CacheEntry::Callback::Callback(CacheEntry
* aEntry
,
78 nsICacheEntryOpenCallback
*aCallback
,
79 bool aReadOnly
, bool aCheckOnAnyThread
)
81 , mCallback(aCallback
)
82 , mTargetThread(do_GetCurrentThread())
83 , mReadOnly(aReadOnly
)
84 , mCheckOnAnyThread(aCheckOnAnyThread
)
85 , mRecheckAfterWrite(false)
88 MOZ_COUNT_CTOR(CacheEntry::Callback
);
90 // The counter may go from zero to non-null only under the service lock
91 // but here we expect it to be already positive.
92 MOZ_ASSERT(mEntry
->HandlesCount());
93 mEntry
->AddHandleRef();
96 CacheEntry::Callback::Callback(CacheEntry::Callback
const &aThat
)
97 : mEntry(aThat
.mEntry
)
98 , mCallback(aThat
.mCallback
)
99 , mTargetThread(aThat
.mTargetThread
)
100 , mReadOnly(aThat
.mReadOnly
)
101 , mCheckOnAnyThread(aThat
.mCheckOnAnyThread
)
102 , mRecheckAfterWrite(aThat
.mRecheckAfterWrite
)
103 , mNotWanted(aThat
.mNotWanted
)
105 MOZ_COUNT_CTOR(CacheEntry::Callback
);
107 // The counter may go from zero to non-null only under the service lock
108 // but here we expect it to be already positive.
109 MOZ_ASSERT(mEntry
->HandlesCount());
110 mEntry
->AddHandleRef();
113 CacheEntry::Callback::~Callback()
115 ProxyRelease(mCallback
, mTargetThread
);
117 mEntry
->ReleaseHandleRef();
118 MOZ_COUNT_DTOR(CacheEntry::Callback
);
121 void CacheEntry::Callback::ExchangeEntry(CacheEntry
* aEntry
)
123 if (mEntry
== aEntry
)
126 // The counter may go from zero to non-null only under the service lock
127 // but here we expect it to be already positive.
128 MOZ_ASSERT(aEntry
->HandlesCount());
129 aEntry
->AddHandleRef();
130 mEntry
->ReleaseHandleRef();
134 nsresult
CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread
) const
136 if (!mCheckOnAnyThread
) {
137 // Check we are on the target
138 return mTargetThread
->IsOnCurrentThread(aOnCheckThread
);
141 // We can invoke check anywhere
142 *aOnCheckThread
= true;
146 nsresult
CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread
) const
148 return mTargetThread
->IsOnCurrentThread(aOnAvailThread
);
153 NS_IMPL_ISUPPORTS(CacheEntry
,
158 CacheEntry::CacheEntry(const nsACString
& aStorageID
,
160 const nsACString
& aEnhanceID
,
163 , mSortingExpirationTime(uint32_t(-1))
164 , mLock("CacheEntry")
165 , mFileStatus(NS_ERROR_NOT_INITIALIZED
)
167 , mEnhanceID(aEnhanceID
)
168 , mStorageID(aStorageID
)
171 , mSecurityInfoLoaded(false)
172 , mPreventCallbacks(false)
175 , mRegistration(NEVERREGISTERED
)
177 , mPredictedDataSize(0)
179 , mReleaseThread(NS_GetCurrentThread())
181 MOZ_COUNT_CTOR(CacheEntry
);
183 mService
= CacheStorageService::Self();
185 CacheStorageService::Self()->RecordMemoryOnlyEntry(
186 this, !aUseDisk
, true /* overwrite */);
189 CacheEntry::~CacheEntry()
191 ProxyRelease(mURI
, mReleaseThread
);
193 LOG(("CacheEntry::~CacheEntry [this=%p]", this));
194 MOZ_COUNT_DTOR(CacheEntry
);
199 char const * CacheEntry::StateString(uint32_t aState
)
202 case NOTLOADED
: return "NOTLOADED";
203 case LOADING
: return "LOADING";
204 case EMPTY
: return "EMPTY";
205 case WRITING
: return "WRITING";
206 case READY
: return "READY";
207 case REVALIDATING
: return "REVALIDATING";
215 nsresult
CacheEntry::HashingKeyWithStorage(nsACString
&aResult
) const
217 return HashingKey(mStorageID
, mEnhanceID
, mURI
, aResult
);
220 nsresult
CacheEntry::HashingKey(nsACString
&aResult
) const
222 return HashingKey(EmptyCString(), mEnhanceID
, mURI
, aResult
);
226 nsresult
CacheEntry::HashingKey(nsCSubstring
const& aStorageID
,
227 nsCSubstring
const& aEnhanceID
,
232 nsresult rv
= aURI
->GetAsciiSpec(spec
);
233 NS_ENSURE_SUCCESS(rv
, rv
);
235 return HashingKey(aStorageID
, aEnhanceID
, spec
, aResult
);
239 nsresult
CacheEntry::HashingKey(nsCSubstring
const& aStorageID
,
240 nsCSubstring
const& aEnhanceID
,
241 nsCSubstring
const& aURISpec
,
245 * This key is used to salt hash that is a base for disk file name.
246 * Changing it will cause we will not be able to find files on disk.
249 aResult
.Append(aStorageID
);
251 if (!aEnhanceID
.IsEmpty()) {
252 CacheFileUtils::AppendTagWithValue(aResult
, '~', aEnhanceID
);
255 // Appending directly
257 aResult
.Append(aURISpec
);
262 void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback
* aCallback
, uint32_t aFlags
)
264 LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
265 this, StateString(mState
), aFlags
, aCallback
));
267 bool readonly
= aFlags
& nsICacheStorage::OPEN_READONLY
;
268 bool bypassIfBusy
= aFlags
& nsICacheStorage::OPEN_BYPASS_IF_BUSY
;
269 bool truncate
= aFlags
& nsICacheStorage::OPEN_TRUNCATE
;
270 bool priority
= aFlags
& nsICacheStorage::OPEN_PRIORITY
;
271 bool multithread
= aFlags
& nsICacheStorage::CHECK_MULTITHREADED
;
273 MOZ_ASSERT(!readonly
|| !truncate
, "Bad flags combination");
274 MOZ_ASSERT(!(truncate
&& mState
> LOADING
), "Must not call truncate on already loaded entry");
276 Callback
callback(this, aCallback
, readonly
, multithread
);
278 if (!Open(callback
, truncate
, priority
, bypassIfBusy
)) {
279 // We get here when the callback wants to bypass cache when it's busy.
280 LOG((" writing or revalidating, callback wants to bypass cache"));
281 callback
.mNotWanted
= true;
282 InvokeAvailableCallback(callback
);
286 bool CacheEntry::Open(Callback
& aCallback
, bool aTruncate
,
287 bool aPriority
, bool aBypassIfBusy
)
289 mozilla::MutexAutoLock
lock(mLock
);
291 // Check state under the lock
292 if (aBypassIfBusy
&& (mState
== WRITING
|| mState
== REVALIDATING
)) {
296 RememberCallback(aCallback
);
298 // Load() opens the lock
299 if (Load(aTruncate
, aPriority
)) {
300 // Loading is in progress...
309 bool CacheEntry::Load(bool aTruncate
, bool aPriority
)
311 LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate
));
313 mLock
.AssertCurrentThreadOwns();
315 if (mState
> LOADING
) {
316 LOG((" already loaded"));
320 if (mState
== LOADING
) {
321 LOG((" already loading"));
331 nsAutoCString fileKey
;
332 rv
= HashingKeyWithStorage(fileKey
);
334 // Check the index under two conditions for two states and take appropriate action:
335 // 1. When this is a disk entry and not told to truncate, check there is a disk file.
336 // If not, set the 'truncate' flag to true so that this entry will open instantly
338 // 2. When this is a memory-only entry, check there is a disk file.
339 // If there is or could be, doom that file.
340 if ((!aTruncate
|| !mUseDisk
) && NS_SUCCEEDED(rv
)) {
341 // Check the index right now to know we have or have not the entry
342 // as soon as possible.
343 CacheIndex::EntryStatus status
;
344 if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey
, &status
))) {
346 case CacheIndex::DOES_NOT_EXIST
:
347 LOG((" entry doesn't exist according information from the index, truncating"));
350 case CacheIndex::EXISTS
:
351 case CacheIndex::DO_NOT_KNOW
:
353 LOG((" entry open as memory-only, but there is (status=%d) a file, dooming it", status
));
354 CacheFileIOManager::DoomFileByKey(fileKey
, nullptr);
361 mFile
= new CacheFile();
363 BackgroundOp(Ops::REGISTER
);
365 bool directLoad
= aTruncate
|| !mUseDisk
;
368 // mLoadStart will be used to calculate telemetry of life-time of this entry.
369 // Low resulution is then enough.
370 mLoadStart
= TimeStamp::NowLoRes();
372 mLoadStart
= TimeStamp::Now();
376 mozilla::MutexAutoUnlock
unlock(mLock
);
378 LOG((" performing load, file=%p", mFile
.get()));
379 if (NS_SUCCEEDED(rv
)) {
380 rv
= mFile
->Init(fileKey
,
384 directLoad
? nullptr : this);
395 // Just fake the load has already been done as "new".
399 return mState
== LOADING
;
402 NS_IMETHODIMP
CacheEntry::OnFileReady(nsresult aResult
, bool aIsNew
)
404 LOG(("CacheEntry::OnFileReady [this=%p, rv=0x%08x, new=%d]",
405 this, aResult
, aIsNew
));
407 MOZ_ASSERT(!mLoadStart
.IsNull());
409 if (NS_SUCCEEDED(aResult
)) {
411 mozilla::Telemetry::AccumulateTimeDelta(
412 mozilla::Telemetry::NETWORK_CACHE_V2_MISS_TIME_MS
,
416 mozilla::Telemetry::AccumulateTimeDelta(
417 mozilla::Telemetry::NETWORK_CACHE_V2_HIT_TIME_MS
,
422 // OnFileReady, that is the only code that can transit from LOADING
423 // to any follow-on state, can only be invoked ones on an entry,
424 // thus no need to lock. Until this moment there is no consumer that
425 // could manipulate the entry state.
426 mozilla::MutexAutoLock
lock(mLock
);
428 MOZ_ASSERT(mState
== LOADING
);
430 mState
= (aIsNew
|| NS_FAILED(aResult
))
434 mFileStatus
= aResult
;
436 if (mState
== READY
) {
440 mFile
->GetFrecency(&frecency
);
441 // mFrecency is held in a double to increase computance precision.
442 // It is ok to persist frecency only as a uint32 with some math involved.
443 mFrecency
= INT2FRECENCY(frecency
);
450 NS_IMETHODIMP
CacheEntry::OnFileDoomed(nsresult aResult
)
453 nsRefPtr
<DoomCallbackRunnable
> event
=
454 new DoomCallbackRunnable(this, aResult
);
455 NS_DispatchToMainThread(event
);
461 already_AddRefed
<CacheEntryHandle
> CacheEntry::ReopenTruncated(bool aMemoryOnly
,
462 nsICacheEntryOpenCallback
* aCallback
)
464 LOG(("CacheEntry::ReopenTruncated [this=%p]", this));
466 mLock
.AssertCurrentThreadOwns();
468 // Hold callbacks invocation, AddStorageEntry would invoke from doom prematurly
469 mPreventCallbacks
= true;
471 nsRefPtr
<CacheEntryHandle
> handle
;
472 nsRefPtr
<CacheEntry
> newEntry
;
474 mozilla::MutexAutoUnlock
unlock(mLock
);
476 // The following call dooms this entry (calls DoomAlreadyRemoved on us)
477 nsresult rv
= CacheStorageService::Self()->AddStorageEntry(
478 GetStorageID(), GetURI(), GetEnhanceID(),
479 mUseDisk
&& !aMemoryOnly
,
480 true, // always create
481 true, // truncate existing (this one)
482 getter_AddRefs(handle
));
484 if (NS_SUCCEEDED(rv
)) {
485 newEntry
= handle
->Entry();
486 LOG((" exchanged entry %p by entry %p, rv=0x%08x", this, newEntry
.get(), rv
));
487 newEntry
->AsyncOpen(aCallback
, nsICacheStorage::OPEN_TRUNCATE
);
489 LOG((" exchanged of entry %p failed, rv=0x%08x", this, rv
));
494 mPreventCallbacks
= false;
499 newEntry
->TransferCallbacks(*this);
502 // Must return a new write handle, since the consumer is expected to
503 // write to this newly recreated entry. The |handle| is only a common
504 // reference counter and doesn't revert entry state back when write
505 // fails and also doesn't update the entry frecency. Not updating
506 // frecency causes entries to not be purged from our memory pools.
507 nsRefPtr
<CacheEntryHandle
> writeHandle
=
508 newEntry
->NewWriteHandle();
509 return writeHandle
.forget();
512 void CacheEntry::TransferCallbacks(CacheEntry
& aFromEntry
)
514 mozilla::MutexAutoLock
lock(mLock
);
516 LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
519 if (!mCallbacks
.Length())
520 mCallbacks
.SwapElements(aFromEntry
.mCallbacks
);
522 mCallbacks
.AppendElements(aFromEntry
.mCallbacks
);
524 uint32_t callbacksLength
= mCallbacks
.Length();
525 if (callbacksLength
) {
526 // Carry the entry reference (unfortunatelly, needs to be done manually...)
527 for (uint32_t i
= 0; i
< callbacksLength
; ++i
)
528 mCallbacks
[i
].ExchangeEntry(this);
530 BackgroundOp(Ops::CALLBACKS
, true);
534 void CacheEntry::RememberCallback(Callback
& aCallback
)
536 mLock
.AssertCurrentThreadOwns();
538 LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
539 this, aCallback
.mCallback
.get(), StateString(mState
)));
541 mCallbacks
.AppendElement(aCallback
);
544 void CacheEntry::InvokeCallbacksLock()
546 mozilla::MutexAutoLock
lock(mLock
);
550 void CacheEntry::InvokeCallbacks()
552 mLock
.AssertCurrentThreadOwns();
554 LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
556 // Invoke first all r/w callbacks, then all r/o callbacks.
557 if (InvokeCallbacks(false))
558 InvokeCallbacks(true);
560 LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
563 bool CacheEntry::InvokeCallbacks(bool aReadOnly
)
565 mLock
.AssertCurrentThreadOwns();
568 while (i
< mCallbacks
.Length()) {
569 if (mPreventCallbacks
) {
570 LOG((" callbacks prevented!"));
574 if (!mIsDoomed
&& (mState
== WRITING
|| mState
== REVALIDATING
)) {
575 LOG((" entry is being written/revalidated"));
579 if (mCallbacks
[i
].mReadOnly
!= aReadOnly
) {
580 // Callback is not r/w or r/o, go to another one in line
586 nsresult rv
= mCallbacks
[i
].OnCheckThread(&onCheckThread
);
588 if (NS_SUCCEEDED(rv
) && !onCheckThread
) {
589 // Redispatch to the target thread
590 nsRefPtr
<nsRunnableMethod
<CacheEntry
> > event
=
591 NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock
);
593 rv
= mCallbacks
[i
].mTargetThread
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
594 if (NS_SUCCEEDED(rv
)) {
595 LOG((" re-dispatching to target thread"));
600 Callback callback
= mCallbacks
[i
];
601 mCallbacks
.RemoveElementAt(i
);
603 if (NS_SUCCEEDED(rv
) && !InvokeCallback(callback
)) {
604 // Callback didn't fire, put it back and go to another one in line.
605 // Only reason InvokeCallback returns false is that onCacheEntryCheck
606 // returns RECHECK_AFTER_WRITE_FINISHED. If we would stop the loop, other
607 // readers or potential writers would be unnecessarily kept from being
609 mCallbacks
.InsertElementAt(i
, callback
);
617 bool CacheEntry::InvokeCallback(Callback
& aCallback
)
619 LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
620 this, StateString(mState
), aCallback
.mCallback
.get()));
622 mLock
.AssertCurrentThreadOwns();
624 // When this entry is doomed we want to notify the callback any time
626 // When we are here, the entry must be loaded from disk
627 MOZ_ASSERT(mState
> LOADING
);
629 if (mState
== WRITING
|| mState
== REVALIDATING
) {
630 // Prevent invoking other callbacks since one of them is now writing
631 // or revalidating this entry. No consumers should get this entry
632 // until metadata are filled with values downloaded from the server
633 // or the entry revalidated and output stream has been opened.
634 LOG((" entry is being written/revalidated, callback bypassed"));
638 // mRecheckAfterWrite flag already set means the callback has already passed
639 // the onCacheEntryCheck call. Until the current write is not finished this
640 // callback will be bypassed.
641 if (!aCallback
.mRecheckAfterWrite
) {
643 if (!aCallback
.mReadOnly
) {
644 if (mState
== EMPTY
) {
645 // Advance to writing state, we expect to invoke the callback and let
646 // it fill content of this entry. Must set and check the state here
647 // to prevent more then one
649 LOG((" advancing to WRITING state"));
652 if (!aCallback
.mCallback
) {
653 // We can be given no callback only in case of recreate, it is ok
654 // to advance to WRITING state since the caller of recreate is expected
655 // to write this entry now.
660 if (mState
== READY
) {
661 // Metadata present, validate the entry
662 uint32_t checkResult
;
664 // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
665 mozilla::MutexAutoUnlock
unlock(mLock
);
667 nsresult rv
= aCallback
.mCallback
->OnCacheEntryCheck(
668 this, nullptr, &checkResult
);
669 LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv
, checkResult
));
672 checkResult
= ENTRY_NOT_WANTED
;
675 switch (checkResult
) {
677 // Nothing more to do here, the consumer is responsible to handle
678 // the result of OnCacheEntryCheck it self.
679 // Proceed to callback...
682 case RECHECK_AFTER_WRITE_FINISHED
:
683 LOG((" consumer will check on the entry again after write is done"));
684 // The consumer wants the entry to complete first.
685 aCallback
.mRecheckAfterWrite
= true;
688 case ENTRY_NEEDS_REVALIDATION
:
689 LOG((" will be holding callbacks until entry is revalidated"));
690 // State is READY now and from that state entry cannot transit to any other
691 // state then REVALIDATING for which cocurrency is not an issue. Potentially
692 // no need to lock here.
693 mState
= REVALIDATING
;
696 case ENTRY_NOT_WANTED
:
697 LOG((" consumer not interested in the entry"));
698 // Do not give this entry to the consumer, it is not interested in us.
699 aCallback
.mNotWanted
= true;
706 if (aCallback
.mCallback
) {
707 if (!mIsDoomed
&& aCallback
.mRecheckAfterWrite
) {
708 // If we don't have data and the callback wants a complete entry,
710 bool bypass
= !mHasData
;
711 if (!bypass
&& NS_SUCCEEDED(mFileStatus
)) {
713 bypass
= !mFile
->DataSize(&_unused
);
717 LOG((" bypassing, entry data still being written"));
721 // Entry is complete now, do the check+avail call again
722 aCallback
.mRecheckAfterWrite
= false;
723 return InvokeCallback(aCallback
);
726 mozilla::MutexAutoUnlock
unlock(mLock
);
727 InvokeAvailableCallback(aCallback
);
733 void CacheEntry::InvokeAvailableCallback(Callback
const & aCallback
)
735 LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
736 this, StateString(mState
), aCallback
.mCallback
.get(), aCallback
.mReadOnly
, aCallback
.mNotWanted
));
740 uint32_t const state
= mState
;
742 // When we are here, the entry must be loaded from disk
743 MOZ_ASSERT(state
> LOADING
|| mIsDoomed
);
746 rv
= aCallback
.OnAvailThread(&onAvailThread
);
748 LOG((" target thread dead?"));
752 if (!onAvailThread
) {
753 // Dispatch to the right thread
754 nsRefPtr
<AvailableCallbackRunnable
> event
=
755 new AvailableCallbackRunnable(this, aCallback
);
757 rv
= aCallback
.mTargetThread
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
758 LOG((" redispatched, (rv = 0x%08x)", rv
));
762 if (NS_SUCCEEDED(mFileStatus
)) {
763 // Let the last-fetched and fetch-count properties be updated.
767 if (mIsDoomed
|| aCallback
.mNotWanted
) {
768 LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
769 aCallback
.mCallback
->OnCacheEntryAvailable(
770 nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND
);
774 if (state
== READY
) {
775 LOG((" ready/has-meta, notifying OCEA with entry and NS_OK"));
777 mozilla::MutexAutoLock
lock(mLock
);
778 BackgroundOp(Ops::FRECENCYUPDATE
);
781 nsRefPtr
<CacheEntryHandle
> handle
= NewHandle();
782 aCallback
.mCallback
->OnCacheEntryAvailable(
783 handle
, false, nullptr, NS_OK
);
787 if (aCallback
.mReadOnly
) {
788 LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
789 aCallback
.mCallback
->OnCacheEntryAvailable(
790 nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND
);
794 // This is a new or potentially non-valid entry and needs to be fetched first.
795 // The CacheEntryHandle blocks other consumers until the channel
796 // either releases the entry or marks metadata as filled or whole entry valid,
797 // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.
799 // Consumer will be responsible to fill or validate the entry metadata and data.
801 nsRefPtr
<CacheEntryHandle
> handle
= NewWriteHandle();
802 rv
= aCallback
.mCallback
->OnCacheEntryAvailable(
803 handle
, state
== WRITING
, nullptr, NS_OK
);
806 LOG((" writing/revalidating failed (0x%08x)", rv
));
808 // Consumer given a new entry failed to take care of the entry.
809 OnHandleClosed(handle
);
813 LOG((" writing/revalidating"));
816 CacheEntryHandle
* CacheEntry::NewHandle()
818 return new CacheEntryHandle(this);
821 CacheEntryHandle
* CacheEntry::NewWriteHandle()
823 mozilla::MutexAutoLock
lock(mLock
);
825 BackgroundOp(Ops::FRECENCYUPDATE
);
826 return (mWriter
= NewHandle());
829 void CacheEntry::OnHandleClosed(CacheEntryHandle
const* aHandle
)
831 LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState
), aHandle
));
833 nsCOMPtr
<nsIOutputStream
> outputStream
;
836 mozilla::MutexAutoLock
lock(mLock
);
838 if (mWriter
!= aHandle
) {
839 LOG((" not the writer"));
844 // No one took our internal output stream, so there are no data
845 // and output stream has to be open symultaneously with input stream
846 // on this entry again.
850 outputStream
.swap(mOutputStream
);
853 if (mState
== WRITING
) {
854 LOG((" reverting to state EMPTY - write failed"));
857 else if (mState
== REVALIDATING
) {
858 LOG((" reverting to state READY - reval failed"));
862 if (mState
== READY
&& !mHasData
) {
863 // We may get to this state when following steps happen:
864 // 1. a new entry is given to a consumer
865 // 2. the consumer calls MetaDataReady(), we transit to READY
866 // 3. abandons the entry w/o opening the output stream, mHasData left false
868 // In this case any following consumer will get a ready entry (with metadata)
869 // but in state like the entry data write was still happening (was in progress)
870 // and will indefinitely wait for the entry data or even the entry itself when
871 // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck.
872 LOG((" we are in READY state, pretend we have data regardless it"
873 " has actully been never touched"));
881 LOG((" abandoning phantom output stream"));
882 outputStream
->Close();
886 void CacheEntry::OnOutputClosed()
888 // Called when the file's output stream is closed. Invoke any callbacks
889 // waiting for complete entry.
891 mozilla::MutexAutoLock
lock(mLock
);
895 bool CacheEntry::IsReferenced() const
897 CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
899 // Increasing this counter from 0 to non-null and this check both happen only
900 // under the service lock.
901 return mHandlesCount
> 0;
904 bool CacheEntry::IsFileDoomed()
906 if (NS_SUCCEEDED(mFileStatus
)) {
907 return mFile
->IsDoomed();
913 uint32_t CacheEntry::GetMetadataMemoryConsumption()
915 NS_ENSURE_SUCCESS(mFileStatus
, 0);
918 if (NS_FAILED(mFile
->ElementsSize(&size
)))
926 NS_IMETHODIMP
CacheEntry::GetPersistent(bool *aPersistToDisk
)
928 // No need to sync when only reading.
929 // When consumer needs to be consistent with state of the memory storage entries
930 // table, then let it use GetUseDisk getter that must be called under the service lock.
931 *aPersistToDisk
= mUseDisk
;
935 NS_IMETHODIMP
CacheEntry::GetKey(nsACString
& aKey
)
937 return mURI
->GetAsciiSpec(aKey
);
940 NS_IMETHODIMP
CacheEntry::GetFetchCount(int32_t *aFetchCount
)
942 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
944 return mFile
->GetFetchCount(reinterpret_cast<uint32_t*>(aFetchCount
));
947 NS_IMETHODIMP
CacheEntry::GetLastFetched(uint32_t *aLastFetched
)
949 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
951 return mFile
->GetLastFetched(aLastFetched
);
954 NS_IMETHODIMP
CacheEntry::GetLastModified(uint32_t *aLastModified
)
956 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
958 return mFile
->GetLastModified(aLastModified
);
961 NS_IMETHODIMP
CacheEntry::GetExpirationTime(uint32_t *aExpirationTime
)
963 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
965 return mFile
->GetExpirationTime(aExpirationTime
);
968 NS_IMETHODIMP
CacheEntry::GetIsForcedValid(bool *aIsForcedValid
)
970 NS_ENSURE_ARG(aIsForcedValid
);
974 nsresult rv
= HashingKeyWithStorage(key
);
979 *aIsForcedValid
= CacheStorageService::Self()->IsForcedValidEntry(key
);
980 LOG(("CacheEntry::GetIsForcedValid [this=%p, IsForcedValid=%d]", this, *aIsForcedValid
));
985 NS_IMETHODIMP
CacheEntry::ForceValidFor(uint32_t aSecondsToTheFuture
)
987 LOG(("CacheEntry::ForceValidFor [this=%p, aSecondsToTheFuture=%d]", this, aSecondsToTheFuture
));
990 nsresult rv
= HashingKeyWithStorage(key
);
995 CacheStorageService::Self()->ForceEntryValidFor(key
, aSecondsToTheFuture
);
1000 NS_IMETHODIMP
CacheEntry::SetExpirationTime(uint32_t aExpirationTime
)
1002 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1004 nsresult rv
= mFile
->SetExpirationTime(aExpirationTime
);
1005 NS_ENSURE_SUCCESS(rv
, rv
);
1007 // Aligned assignment, thus atomic.
1008 mSortingExpirationTime
= aExpirationTime
;
1012 NS_IMETHODIMP
CacheEntry::OpenInputStream(int64_t offset
, nsIInputStream
* *_retval
)
1014 LOG(("CacheEntry::OpenInputStream [this=%p]", this));
1016 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1020 nsCOMPtr
<nsIInputStream
> stream
;
1021 rv
= mFile
->OpenInputStream(getter_AddRefs(stream
));
1022 NS_ENSURE_SUCCESS(rv
, rv
);
1024 nsCOMPtr
<nsISeekableStream
> seekable
=
1025 do_QueryInterface(stream
, &rv
);
1026 NS_ENSURE_SUCCESS(rv
, rv
);
1028 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1029 NS_ENSURE_SUCCESS(rv
, rv
);
1031 mozilla::MutexAutoLock
lock(mLock
);
1034 // So far output stream on this new entry not opened, do it now.
1035 LOG((" creating phantom output stream"));
1036 rv
= OpenOutputStreamInternal(0, getter_AddRefs(mOutputStream
));
1037 NS_ENSURE_SUCCESS(rv
, rv
);
1040 stream
.forget(_retval
);
1044 NS_IMETHODIMP
CacheEntry::OpenOutputStream(int64_t offset
, nsIOutputStream
* *_retval
)
1046 LOG(("CacheEntry::OpenOutputStream [this=%p]", this));
1050 mozilla::MutexAutoLock
lock(mLock
);
1052 MOZ_ASSERT(mState
> EMPTY
);
1054 if (mOutputStream
&& !mIsDoomed
) {
1055 LOG((" giving phantom output stream"));
1056 mOutputStream
.forget(_retval
);
1059 rv
= OpenOutputStreamInternal(offset
, _retval
);
1060 if (NS_FAILED(rv
)) return rv
;
1063 // Entry considered ready when writer opens output stream.
1067 // Invoke any pending readers now.
1073 nsresult
CacheEntry::OpenOutputStreamInternal(int64_t offset
, nsIOutputStream
* *_retval
)
1075 LOG(("CacheEntry::OpenOutputStreamInternal [this=%p]", this));
1077 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1079 mLock
.AssertCurrentThreadOwns();
1082 LOG((" doomed..."));
1083 return NS_ERROR_NOT_AVAILABLE
;
1086 MOZ_ASSERT(mState
> LOADING
);
1090 // No need to sync on mUseDisk here, we don't need to be consistent
1091 // with content of the memory storage entries hash table.
1093 rv
= mFile
->SetMemoryOnly();
1094 NS_ENSURE_SUCCESS(rv
, rv
);
1097 nsRefPtr
<CacheOutputCloseListener
> listener
=
1098 new CacheOutputCloseListener(this);
1100 nsCOMPtr
<nsIOutputStream
> stream
;
1101 rv
= mFile
->OpenOutputStream(listener
, getter_AddRefs(stream
));
1102 NS_ENSURE_SUCCESS(rv
, rv
);
1104 nsCOMPtr
<nsISeekableStream
> seekable
=
1105 do_QueryInterface(stream
, &rv
);
1106 NS_ENSURE_SUCCESS(rv
, rv
);
1108 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1109 NS_ENSURE_SUCCESS(rv
, rv
);
1111 // Prevent opening output stream again.
1114 stream
.swap(*_retval
);
1118 NS_IMETHODIMP
CacheEntry::GetPredictedDataSize(int64_t *aPredictedDataSize
)
1120 *aPredictedDataSize
= mPredictedDataSize
;
1123 NS_IMETHODIMP
CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize
)
1125 mPredictedDataSize
= aPredictedDataSize
;
1127 if (CacheObserver::EntryIsTooBig(mPredictedDataSize
, mUseDisk
)) {
1128 LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
1131 return NS_ERROR_FILE_TOO_BIG
;
1137 NS_IMETHODIMP
CacheEntry::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
1140 mozilla::MutexAutoLock
lock(mLock
);
1141 if (mSecurityInfoLoaded
) {
1142 NS_IF_ADDREF(*aSecurityInfo
= mSecurityInfo
);
1147 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1149 nsXPIDLCString info
;
1150 nsCOMPtr
<nsISupports
> secInfo
;
1153 rv
= mFile
->GetElement("security-info", getter_Copies(info
));
1154 NS_ENSURE_SUCCESS(rv
, rv
);
1157 rv
= NS_DeserializeObject(info
, getter_AddRefs(secInfo
));
1158 NS_ENSURE_SUCCESS(rv
, rv
);
1162 mozilla::MutexAutoLock
lock(mLock
);
1164 mSecurityInfo
.swap(secInfo
);
1165 mSecurityInfoLoaded
= true;
1167 NS_IF_ADDREF(*aSecurityInfo
= mSecurityInfo
);
1172 NS_IMETHODIMP
CacheEntry::SetSecurityInfo(nsISupports
*aSecurityInfo
)
1176 NS_ENSURE_SUCCESS(mFileStatus
, mFileStatus
);
1179 mozilla::MutexAutoLock
lock(mLock
);
1181 mSecurityInfo
= aSecurityInfo
;
1182 mSecurityInfoLoaded
= true;
1185 nsCOMPtr
<nsISerializable
> serializable
=
1186 do_QueryInterface(aSecurityInfo
);
1187 if (aSecurityInfo
&& !serializable
)
1188 return NS_ERROR_UNEXPECTED
;
1192 rv
= NS_SerializeToString(serializable
, info
);
1193 NS_ENSURE_SUCCESS(rv
, rv
);
1196 rv
= mFile
->SetElement("security-info", info
.Length() ? info
.get() : nullptr);
1197 NS_ENSURE_SUCCESS(rv
, rv
);
1202 NS_IMETHODIMP
CacheEntry::GetStorageDataSize(uint32_t *aStorageDataSize
)
1204 NS_ENSURE_ARG(aStorageDataSize
);
1207 nsresult rv
= GetDataSize(&dataSize
);
1211 *aStorageDataSize
= (uint32_t)std::min(int64_t(uint32_t(-1)), dataSize
);
1216 NS_IMETHODIMP
CacheEntry::AsyncDoom(nsICacheEntryDoomCallback
*aCallback
)
1218 LOG(("CacheEntry::AsyncDoom [this=%p]", this));
1221 mozilla::MutexAutoLock
lock(mLock
);
1223 if (mIsDoomed
|| mDoomCallback
)
1224 return NS_ERROR_IN_PROGRESS
; // to aggregate have DOOMING state
1227 mDoomCallback
= aCallback
;
1230 // This immediately removes the entry from the master hashtable and also
1231 // immediately dooms the file. This way we make sure that any consumer
1232 // after this point asking for the same entry won't get
1234 // b) a new entry with the same file
1240 NS_IMETHODIMP
CacheEntry::GetMetaDataElement(const char * aKey
, char * *aRetval
)
1242 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1244 return mFile
->GetElement(aKey
, aRetval
);
1247 NS_IMETHODIMP
CacheEntry::SetMetaDataElement(const char * aKey
, const char * aValue
)
1249 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1251 return mFile
->SetElement(aKey
, aValue
);
1254 NS_IMETHODIMP
CacheEntry::VisitMetaData(nsICacheEntryMetaDataVisitor
*aVisitor
)
1256 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1258 return mFile
->VisitMetaData(aVisitor
);
1261 NS_IMETHODIMP
CacheEntry::MetaDataReady()
1263 mozilla::MutexAutoLock
lock(mLock
);
1265 LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState
)));
1267 MOZ_ASSERT(mState
> EMPTY
);
1269 if (mState
== WRITING
)
1277 NS_IMETHODIMP
CacheEntry::SetValid()
1279 LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState
)));
1281 nsCOMPtr
<nsIOutputStream
> outputStream
;
1284 mozilla::MutexAutoLock
lock(mLock
);
1286 MOZ_ASSERT(mState
> EMPTY
);
1293 outputStream
.swap(mOutputStream
);
1297 LOG((" abandoning phantom output stream"));
1298 outputStream
->Close();
1304 NS_IMETHODIMP
CacheEntry::Recreate(bool aMemoryOnly
,
1305 nsICacheEntry
**_retval
)
1307 LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState
)));
1309 mozilla::MutexAutoLock
lock(mLock
);
1311 nsRefPtr
<CacheEntryHandle
> handle
= ReopenTruncated(aMemoryOnly
, nullptr);
1313 handle
.forget(_retval
);
1317 BackgroundOp(Ops::CALLBACKS
, true);
1321 NS_IMETHODIMP
CacheEntry::GetDataSize(int64_t *aDataSize
)
1323 LOG(("CacheEntry::GetDataSize [this=%p]", this));
1327 mozilla::MutexAutoLock
lock(mLock
);
1330 LOG((" write in progress (no data)"));
1331 return NS_ERROR_IN_PROGRESS
;
1335 NS_ENSURE_SUCCESS(mFileStatus
, mFileStatus
);
1337 // mayhemer: TODO Problem with compression?
1338 if (!mFile
->DataSize(aDataSize
)) {
1339 LOG((" write in progress (stream active)"));
1340 return NS_ERROR_IN_PROGRESS
;
1343 LOG((" size=%lld", *aDataSize
));
1347 NS_IMETHODIMP
CacheEntry::MarkValid()
1349 // NOT IMPLEMENTED ACTUALLY
1353 NS_IMETHODIMP
CacheEntry::MaybeMarkValid()
1355 // NOT IMPLEMENTED ACTUALLY
1359 NS_IMETHODIMP
CacheEntry::HasWriteAccess(bool aWriteAllowed
, bool *aWriteAccess
)
1361 *aWriteAccess
= aWriteAllowed
;
1365 NS_IMETHODIMP
CacheEntry::Close()
1367 // NOT IMPLEMENTED ACTUALLY
1373 NS_IMETHODIMP
CacheEntry::Run()
1375 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1377 mozilla::MutexAutoLock
lock(mLock
);
1379 BackgroundOp(mBackgroundOperations
.Grab());
1383 // Management methods
1385 double CacheEntry::GetFrecency() const
1387 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1391 uint32_t CacheEntry::GetExpirationTime() const
1393 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1394 return mSortingExpirationTime
;
1397 bool CacheEntry::IsRegistered() const
1399 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1400 return mRegistration
== REGISTERED
;
1403 bool CacheEntry::CanRegister() const
1405 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1406 return mRegistration
== NEVERREGISTERED
;
1409 void CacheEntry::SetRegistered(bool aRegistered
)
1411 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1414 MOZ_ASSERT(mRegistration
== NEVERREGISTERED
);
1415 mRegistration
= REGISTERED
;
1418 MOZ_ASSERT(mRegistration
== REGISTERED
);
1419 mRegistration
= DEREGISTERED
;
1423 bool CacheEntry::Purge(uint32_t aWhat
)
1425 LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat
));
1427 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1430 case PURGE_DATA_ONLY_DISK_BACKED
:
1431 case PURGE_WHOLE_ONLY_DISK_BACKED
:
1432 // This is an in-memory only entry, don't purge it
1434 LOG((" not using disk"));
1439 if (mState
== WRITING
|| mState
== LOADING
|| mFrecency
== 0) {
1440 // In-progress (write or load) entries should (at least for consistency and from
1441 // the logical point of view) stay in memory.
1442 // Zero-frecency entries are those which have never been given to any consumer, those
1443 // are actually very fresh and should not go just because frecency had not been set
1445 LOG((" state=%s, frecency=%1.10f", StateString(mState
), mFrecency
));
1449 if (NS_SUCCEEDED(mFileStatus
) && mFile
->IsWriteInProgress()) {
1450 // The file is used when there are open streams or chunks/metadata still waiting for
1451 // write. In this case, this entry cannot be purged, otherwise reopenned entry
1452 // would may not even find the data on disk - CacheFile is not shared and cannot be
1453 // left orphan when its job is not done, hence keep the whole entry.
1454 LOG((" file still under use"));
1459 case PURGE_WHOLE_ONLY_DISK_BACKED
:
1462 if (!CacheStorageService::Self()->RemoveEntry(this, true)) {
1463 LOG((" not purging, still referenced"));
1467 CacheStorageService::Self()->UnregisterEntry(this);
1469 // Entry removed it self from control arrays, return true
1473 case PURGE_DATA_ONLY_DISK_BACKED
:
1475 NS_ENSURE_SUCCESS(mFileStatus
, false);
1477 mFile
->ThrowMemoryCachedData();
1479 // Entry has been left in control arrays, return false (not purged)
1488 void CacheEntry::PurgeAndDoom()
1490 LOG(("CacheEntry::PurgeAndDoom [this=%p]", this));
1492 CacheStorageService::Self()->RemoveEntry(this);
1493 DoomAlreadyRemoved();
1496 void CacheEntry::DoomAlreadyRemoved()
1498 LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
1500 mozilla::MutexAutoLock
lock(mLock
);
1504 // This schedules dooming of the file, dooming is ensured to happen
1505 // sooner than demand to open the same file made after this point
1506 // so that we don't get this file for any newer opened entry(s).
1509 // Must force post here since may be indirectly called from
1510 // InvokeCallbacks of this entry and we don't want reentrancy here.
1511 BackgroundOp(Ops::CALLBACKS
, true);
1512 // Process immediately when on the management thread.
1513 BackgroundOp(Ops::UNREGISTER
);
1516 void CacheEntry::DoomFile()
1518 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
1520 if (NS_SUCCEEDED(mFileStatus
)) {
1521 // Always calls the callback asynchronously.
1522 rv
= mFile
->Doom(mDoomCallback
? this : nullptr);
1523 if (NS_SUCCEEDED(rv
)) {
1524 LOG((" file doomed"));
1528 if (NS_ERROR_FILE_NOT_FOUND
== rv
) {
1529 // File is set to be just memory-only, notify the callbacks
1530 // and pretend dooming has succeeded. From point of view of
1531 // the entry it actually did - the data is gone and cannot be
1537 // Always posts to the main thread.
1541 void CacheEntry::BackgroundOp(uint32_t aOperations
, bool aForceAsync
)
1543 mLock
.AssertCurrentThreadOwns();
1545 if (!CacheStorageService::IsOnManagementThread() || aForceAsync
) {
1546 if (mBackgroundOperations
.Set(aOperations
))
1547 CacheStorageService::Self()->Dispatch(this);
1549 LOG(("CacheEntry::BackgroundOp this=%p dipatch of %x", this, aOperations
));
1554 mozilla::MutexAutoUnlock
unlock(mLock
);
1556 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1558 if (aOperations
& Ops::FRECENCYUPDATE
) {
1562 #define M_LN2 0.69314718055994530942
1565 // Half-life is dynamic, in seconds.
1566 static double half_life
= CacheObserver::HalfLifeSeconds();
1567 // Must convert from seconds to milliseconds since PR_Now() gives usecs.
1568 static double const decay
= (M_LN2
/ half_life
) / static_cast<double>(PR_USEC_PER_SEC
);
1570 double now_decay
= static_cast<double>(PR_Now()) * decay
;
1572 if (mFrecency
== 0) {
1573 mFrecency
= now_decay
;
1576 // TODO: when C++11 enabled, use std::log1p(n) which is equal to log(n + 1) but
1578 mFrecency
= log(exp(mFrecency
- now_decay
) + 1) + now_decay
;
1580 LOG(("CacheEntry FRECENCYUPDATE [this=%p, frecency=%1.10f]", this, mFrecency
));
1582 // Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that
1583 // is not thread-safe) we must post to the main thread...
1584 nsRefPtr
<nsRunnableMethod
<CacheEntry
> > event
=
1585 NS_NewRunnableMethod(this, &CacheEntry::StoreFrecency
);
1586 NS_DispatchToMainThread(event
);
1589 if (aOperations
& Ops::REGISTER
) {
1590 LOG(("CacheEntry REGISTER [this=%p]", this));
1592 CacheStorageService::Self()->RegisterEntry(this);
1595 if (aOperations
& Ops::UNREGISTER
) {
1596 LOG(("CacheEntry UNREGISTER [this=%p]", this));
1598 CacheStorageService::Self()->UnregisterEntry(this);
1602 if (aOperations
& Ops::CALLBACKS
) {
1603 LOG(("CacheEntry CALLBACKS (invoke) [this=%p]", this));
1609 void CacheEntry::StoreFrecency()
1611 // No need for thread safety over mFrecency, it will be rewriten
1612 // correctly on following invocation if broken by concurrency.
1613 MOZ_ASSERT(NS_IsMainThread());
1615 if (NS_SUCCEEDED(mFileStatus
)) {
1616 mFile
->SetFrecency(FRECENCY2INT(mFrecency
));
1620 // CacheOutputCloseListener
1622 CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry
* aEntry
)
1625 MOZ_COUNT_CTOR(CacheOutputCloseListener
);
1628 CacheOutputCloseListener::~CacheOutputCloseListener()
1630 MOZ_COUNT_DTOR(CacheOutputCloseListener
);
1633 void CacheOutputCloseListener::OnOutputClosed()
1635 // We need this class and to redispatch since this callback is invoked
1636 // under the file's lock and to do the job we need to enter the entry's
1637 // lock too. That would lead to potential deadlocks.
1638 NS_DispatchToCurrentThread(this);
1641 NS_IMETHODIMP
CacheOutputCloseListener::Run()
1643 mEntry
->OnOutputClosed();
1649 size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1652 nsCOMPtr
<nsISizeOf
> sizeOf
;
1654 n
+= mCallbacks
.SizeOfExcludingThis(mallocSizeOf
);
1656 n
+= mFile
->SizeOfIncludingThis(mallocSizeOf
);
1659 sizeOf
= do_QueryInterface(mURI
);
1661 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
1664 n
+= mEnhanceID
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1665 n
+= mStorageID
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1667 // mDoomCallback is an arbitrary class that is probably reported elsewhere.
1668 // mOutputStream is reported in mFile.
1669 // mWriter is one of many handles we create, but (intentionally) not keep
1670 // any reference to, so those unfortunatelly cannot be reported. Handles are
1672 // mSecurityInfo doesn't impl nsISizeOf.
1677 size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1679 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);