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
,
82 , mCallback(aCallback
)
83 , mTargetThread(do_GetCurrentThread())
84 , mReadOnly(aReadOnly
)
85 , mCheckOnAnyThread(aCheckOnAnyThread
)
86 , mRecheckAfterWrite(false)
90 MOZ_COUNT_CTOR(CacheEntry::Callback
);
92 // The counter may go from zero to non-null only under the service lock
93 // but here we expect it to be already positive.
94 MOZ_ASSERT(mEntry
->HandlesCount());
95 mEntry
->AddHandleRef();
98 CacheEntry::Callback::Callback(CacheEntry::Callback
const &aThat
)
99 : mEntry(aThat
.mEntry
)
100 , mCallback(aThat
.mCallback
)
101 , mTargetThread(aThat
.mTargetThread
)
102 , mReadOnly(aThat
.mReadOnly
)
103 , mCheckOnAnyThread(aThat
.mCheckOnAnyThread
)
104 , mRecheckAfterWrite(aThat
.mRecheckAfterWrite
)
105 , mNotWanted(aThat
.mNotWanted
)
106 , mSecret(aThat
.mSecret
)
108 MOZ_COUNT_CTOR(CacheEntry::Callback
);
110 // The counter may go from zero to non-null only under the service lock
111 // but here we expect it to be already positive.
112 MOZ_ASSERT(mEntry
->HandlesCount());
113 mEntry
->AddHandleRef();
116 CacheEntry::Callback::~Callback()
118 ProxyRelease(mCallback
, mTargetThread
);
120 mEntry
->ReleaseHandleRef();
121 MOZ_COUNT_DTOR(CacheEntry::Callback
);
124 void CacheEntry::Callback::ExchangeEntry(CacheEntry
* aEntry
)
126 if (mEntry
== aEntry
)
129 // The counter may go from zero to non-null only under the service lock
130 // but here we expect it to be already positive.
131 MOZ_ASSERT(aEntry
->HandlesCount());
132 aEntry
->AddHandleRef();
133 mEntry
->ReleaseHandleRef();
137 nsresult
CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread
) const
139 if (!mCheckOnAnyThread
) {
140 // Check we are on the target
141 return mTargetThread
->IsOnCurrentThread(aOnCheckThread
);
144 // We can invoke check anywhere
145 *aOnCheckThread
= true;
149 nsresult
CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread
) const
151 return mTargetThread
->IsOnCurrentThread(aOnAvailThread
);
156 NS_IMPL_ISUPPORTS(CacheEntry
,
161 CacheEntry::CacheEntry(const nsACString
& aStorageID
,
163 const nsACString
& aEnhanceID
,
166 , mSortingExpirationTime(uint32_t(-1))
167 , mLock("CacheEntry")
168 , mFileStatus(NS_ERROR_NOT_INITIALIZED
)
170 , mEnhanceID(aEnhanceID
)
171 , mStorageID(aStorageID
)
174 , mSecurityInfoLoaded(false)
175 , mPreventCallbacks(false)
178 , mRegistration(NEVERREGISTERED
)
180 , mPredictedDataSize(0)
182 , mReleaseThread(NS_GetCurrentThread())
184 MOZ_COUNT_CTOR(CacheEntry
);
186 mService
= CacheStorageService::Self();
188 CacheStorageService::Self()->RecordMemoryOnlyEntry(
189 this, !aUseDisk
, true /* overwrite */);
192 CacheEntry::~CacheEntry()
194 ProxyRelease(mURI
, mReleaseThread
);
196 LOG(("CacheEntry::~CacheEntry [this=%p]", this));
197 MOZ_COUNT_DTOR(CacheEntry
);
202 char const * CacheEntry::StateString(uint32_t aState
)
205 case NOTLOADED
: return "NOTLOADED";
206 case LOADING
: return "LOADING";
207 case EMPTY
: return "EMPTY";
208 case WRITING
: return "WRITING";
209 case READY
: return "READY";
210 case REVALIDATING
: return "REVALIDATING";
218 nsresult
CacheEntry::HashingKeyWithStorage(nsACString
&aResult
) const
220 return HashingKey(mStorageID
, mEnhanceID
, mURI
, aResult
);
223 nsresult
CacheEntry::HashingKey(nsACString
&aResult
) const
225 return HashingKey(EmptyCString(), mEnhanceID
, mURI
, aResult
);
229 nsresult
CacheEntry::HashingKey(nsCSubstring
const& aStorageID
,
230 nsCSubstring
const& aEnhanceID
,
235 nsresult rv
= aURI
->GetAsciiSpec(spec
);
236 NS_ENSURE_SUCCESS(rv
, rv
);
238 return HashingKey(aStorageID
, aEnhanceID
, spec
, aResult
);
242 nsresult
CacheEntry::HashingKey(nsCSubstring
const& aStorageID
,
243 nsCSubstring
const& aEnhanceID
,
244 nsCSubstring
const& aURISpec
,
248 * This key is used to salt hash that is a base for disk file name.
249 * Changing it will cause we will not be able to find files on disk.
252 aResult
.Append(aStorageID
);
254 if (!aEnhanceID
.IsEmpty()) {
255 CacheFileUtils::AppendTagWithValue(aResult
, '~', aEnhanceID
);
258 // Appending directly
260 aResult
.Append(aURISpec
);
265 void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback
* aCallback
, uint32_t aFlags
)
267 LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
268 this, StateString(mState
), aFlags
, aCallback
));
270 bool readonly
= aFlags
& nsICacheStorage::OPEN_READONLY
;
271 bool bypassIfBusy
= aFlags
& nsICacheStorage::OPEN_BYPASS_IF_BUSY
;
272 bool truncate
= aFlags
& nsICacheStorage::OPEN_TRUNCATE
;
273 bool priority
= aFlags
& nsICacheStorage::OPEN_PRIORITY
;
274 bool multithread
= aFlags
& nsICacheStorage::CHECK_MULTITHREADED
;
275 bool secret
= aFlags
& nsICacheStorage::OPEN_SECRETLY
;
277 MOZ_ASSERT(!readonly
|| !truncate
, "Bad flags combination");
278 MOZ_ASSERT(!(truncate
&& mState
> LOADING
), "Must not call truncate on already loaded entry");
280 Callback
callback(this, aCallback
, readonly
, multithread
, secret
);
282 if (!Open(callback
, truncate
, priority
, bypassIfBusy
)) {
283 // We get here when the callback wants to bypass cache when it's busy.
284 LOG((" writing or revalidating, callback wants to bypass cache"));
285 callback
.mNotWanted
= true;
286 InvokeAvailableCallback(callback
);
290 bool CacheEntry::Open(Callback
& aCallback
, bool aTruncate
,
291 bool aPriority
, bool aBypassIfBusy
)
293 mozilla::MutexAutoLock
lock(mLock
);
295 // Check state under the lock
296 if (aBypassIfBusy
&& (mState
== WRITING
|| mState
== REVALIDATING
)) {
300 RememberCallback(aCallback
);
302 // Load() opens the lock
303 if (Load(aTruncate
, aPriority
)) {
304 // Loading is in progress...
313 bool CacheEntry::Load(bool aTruncate
, bool aPriority
)
315 LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate
));
317 mLock
.AssertCurrentThreadOwns();
319 if (mState
> LOADING
) {
320 LOG((" already loaded"));
324 if (mState
== LOADING
) {
325 LOG((" already loading"));
335 nsAutoCString fileKey
;
336 rv
= HashingKeyWithStorage(fileKey
);
338 // Check the index under two conditions for two states and take appropriate action:
339 // 1. When this is a disk entry and not told to truncate, check there is a disk file.
340 // If not, set the 'truncate' flag to true so that this entry will open instantly
342 // 2. When this is a memory-only entry, check there is a disk file.
343 // If there is or could be, doom that file.
344 if ((!aTruncate
|| !mUseDisk
) && NS_SUCCEEDED(rv
)) {
345 // Check the index right now to know we have or have not the entry
346 // as soon as possible.
347 CacheIndex::EntryStatus status
;
348 if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey
, &status
))) {
350 case CacheIndex::DOES_NOT_EXIST
:
351 LOG((" entry doesn't exist according information from the index, truncating"));
354 case CacheIndex::EXISTS
:
355 case CacheIndex::DO_NOT_KNOW
:
357 LOG((" entry open as memory-only, but there is (status=%d) a file, dooming it", status
));
358 CacheFileIOManager::DoomFileByKey(fileKey
, nullptr);
365 mFile
= new CacheFile();
367 BackgroundOp(Ops::REGISTER
);
369 bool directLoad
= aTruncate
|| !mUseDisk
;
372 // mLoadStart will be used to calculate telemetry of life-time of this entry.
373 // Low resulution is then enough.
374 mLoadStart
= TimeStamp::NowLoRes();
376 mLoadStart
= TimeStamp::Now();
380 mozilla::MutexAutoUnlock
unlock(mLock
);
382 LOG((" performing load, file=%p", mFile
.get()));
383 if (NS_SUCCEEDED(rv
)) {
384 rv
= mFile
->Init(fileKey
,
388 directLoad
? nullptr : this);
399 // Just fake the load has already been done as "new".
403 return mState
== LOADING
;
406 NS_IMETHODIMP
CacheEntry::OnFileReady(nsresult aResult
, bool aIsNew
)
408 LOG(("CacheEntry::OnFileReady [this=%p, rv=0x%08x, new=%d]",
409 this, aResult
, aIsNew
));
411 MOZ_ASSERT(!mLoadStart
.IsNull());
413 if (NS_SUCCEEDED(aResult
)) {
415 mozilla::Telemetry::AccumulateTimeDelta(
416 mozilla::Telemetry::NETWORK_CACHE_V2_MISS_TIME_MS
,
420 mozilla::Telemetry::AccumulateTimeDelta(
421 mozilla::Telemetry::NETWORK_CACHE_V2_HIT_TIME_MS
,
426 // OnFileReady, that is the only code that can transit from LOADING
427 // to any follow-on state, can only be invoked ones on an entry,
428 // thus no need to lock. Until this moment there is no consumer that
429 // could manipulate the entry state.
430 mozilla::MutexAutoLock
lock(mLock
);
432 MOZ_ASSERT(mState
== LOADING
);
434 mState
= (aIsNew
|| NS_FAILED(aResult
))
438 mFileStatus
= aResult
;
440 if (mState
== READY
) {
444 mFile
->GetFrecency(&frecency
);
445 // mFrecency is held in a double to increase computance precision.
446 // It is ok to persist frecency only as a uint32 with some math involved.
447 mFrecency
= INT2FRECENCY(frecency
);
454 NS_IMETHODIMP
CacheEntry::OnFileDoomed(nsresult aResult
)
457 nsRefPtr
<DoomCallbackRunnable
> event
=
458 new DoomCallbackRunnable(this, aResult
);
459 NS_DispatchToMainThread(event
);
465 already_AddRefed
<CacheEntryHandle
> CacheEntry::ReopenTruncated(bool aMemoryOnly
,
466 nsICacheEntryOpenCallback
* aCallback
)
468 LOG(("CacheEntry::ReopenTruncated [this=%p]", this));
470 mLock
.AssertCurrentThreadOwns();
472 // Hold callbacks invocation, AddStorageEntry would invoke from doom prematurly
473 mPreventCallbacks
= true;
475 nsRefPtr
<CacheEntryHandle
> handle
;
476 nsRefPtr
<CacheEntry
> newEntry
;
478 mozilla::MutexAutoUnlock
unlock(mLock
);
480 // The following call dooms this entry (calls DoomAlreadyRemoved on us)
481 nsresult rv
= CacheStorageService::Self()->AddStorageEntry(
482 GetStorageID(), GetURI(), GetEnhanceID(),
483 mUseDisk
&& !aMemoryOnly
,
484 true, // always create
485 true, // truncate existing (this one)
486 getter_AddRefs(handle
));
488 if (NS_SUCCEEDED(rv
)) {
489 newEntry
= handle
->Entry();
490 LOG((" exchanged entry %p by entry %p, rv=0x%08x", this, newEntry
.get(), rv
));
491 newEntry
->AsyncOpen(aCallback
, nsICacheStorage::OPEN_TRUNCATE
);
493 LOG((" exchanged of entry %p failed, rv=0x%08x", this, rv
));
498 mPreventCallbacks
= false;
503 newEntry
->TransferCallbacks(*this);
506 // Must return a new write handle, since the consumer is expected to
507 // write to this newly recreated entry. The |handle| is only a common
508 // reference counter and doesn't revert entry state back when write
509 // fails and also doesn't update the entry frecency. Not updating
510 // frecency causes entries to not be purged from our memory pools.
511 nsRefPtr
<CacheEntryHandle
> writeHandle
=
512 newEntry
->NewWriteHandle();
513 return writeHandle
.forget();
516 void CacheEntry::TransferCallbacks(CacheEntry
& aFromEntry
)
518 mozilla::MutexAutoLock
lock(mLock
);
520 LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
523 if (!mCallbacks
.Length())
524 mCallbacks
.SwapElements(aFromEntry
.mCallbacks
);
526 mCallbacks
.AppendElements(aFromEntry
.mCallbacks
);
528 uint32_t callbacksLength
= mCallbacks
.Length();
529 if (callbacksLength
) {
530 // Carry the entry reference (unfortunatelly, needs to be done manually...)
531 for (uint32_t i
= 0; i
< callbacksLength
; ++i
)
532 mCallbacks
[i
].ExchangeEntry(this);
534 BackgroundOp(Ops::CALLBACKS
, true);
538 void CacheEntry::RememberCallback(Callback
& aCallback
)
540 mLock
.AssertCurrentThreadOwns();
542 LOG(("CacheEntry::RememberCallback [this=%p, cb=%p, state=%s]",
543 this, aCallback
.mCallback
.get(), StateString(mState
)));
545 mCallbacks
.AppendElement(aCallback
);
548 void CacheEntry::InvokeCallbacksLock()
550 mozilla::MutexAutoLock
lock(mLock
);
554 void CacheEntry::InvokeCallbacks()
556 mLock
.AssertCurrentThreadOwns();
558 LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
560 // Invoke first all r/w callbacks, then all r/o callbacks.
561 if (InvokeCallbacks(false))
562 InvokeCallbacks(true);
564 LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
567 bool CacheEntry::InvokeCallbacks(bool aReadOnly
)
569 mLock
.AssertCurrentThreadOwns();
572 while (i
< mCallbacks
.Length()) {
573 if (mPreventCallbacks
) {
574 LOG((" callbacks prevented!"));
578 if (!mIsDoomed
&& (mState
== WRITING
|| mState
== REVALIDATING
)) {
579 LOG((" entry is being written/revalidated"));
583 if (mCallbacks
[i
].mReadOnly
!= aReadOnly
) {
584 // Callback is not r/w or r/o, go to another one in line
590 nsresult rv
= mCallbacks
[i
].OnCheckThread(&onCheckThread
);
592 if (NS_SUCCEEDED(rv
) && !onCheckThread
) {
593 // Redispatch to the target thread
594 nsRefPtr
<nsRunnableMethod
<CacheEntry
> > event
=
595 NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock
);
597 rv
= mCallbacks
[i
].mTargetThread
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
598 if (NS_SUCCEEDED(rv
)) {
599 LOG((" re-dispatching to target thread"));
604 Callback callback
= mCallbacks
[i
];
605 mCallbacks
.RemoveElementAt(i
);
607 if (NS_SUCCEEDED(rv
) && !InvokeCallback(callback
)) {
608 // Callback didn't fire, put it back and go to another one in line.
609 // Only reason InvokeCallback returns false is that onCacheEntryCheck
610 // returns RECHECK_AFTER_WRITE_FINISHED. If we would stop the loop, other
611 // readers or potential writers would be unnecessarily kept from being
613 mCallbacks
.InsertElementAt(i
, callback
);
621 bool CacheEntry::InvokeCallback(Callback
& aCallback
)
623 LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
624 this, StateString(mState
), aCallback
.mCallback
.get()));
626 mLock
.AssertCurrentThreadOwns();
628 // When this entry is doomed we want to notify the callback any time
630 // When we are here, the entry must be loaded from disk
631 MOZ_ASSERT(mState
> LOADING
);
633 if (mState
== WRITING
|| mState
== REVALIDATING
) {
634 // Prevent invoking other callbacks since one of them is now writing
635 // or revalidating this entry. No consumers should get this entry
636 // until metadata are filled with values downloaded from the server
637 // or the entry revalidated and output stream has been opened.
638 LOG((" entry is being written/revalidated, callback bypassed"));
642 // mRecheckAfterWrite flag already set means the callback has already passed
643 // the onCacheEntryCheck call. Until the current write is not finished this
644 // callback will be bypassed.
645 if (!aCallback
.mRecheckAfterWrite
) {
647 if (!aCallback
.mReadOnly
) {
648 if (mState
== EMPTY
) {
649 // Advance to writing state, we expect to invoke the callback and let
650 // it fill content of this entry. Must set and check the state here
651 // to prevent more then one
653 LOG((" advancing to WRITING state"));
656 if (!aCallback
.mCallback
) {
657 // We can be given no callback only in case of recreate, it is ok
658 // to advance to WRITING state since the caller of recreate is expected
659 // to write this entry now.
664 if (mState
== READY
) {
665 // Metadata present, validate the entry
666 uint32_t checkResult
;
668 // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
669 mozilla::MutexAutoUnlock
unlock(mLock
);
671 nsresult rv
= aCallback
.mCallback
->OnCacheEntryCheck(
672 this, nullptr, &checkResult
);
673 LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv
, checkResult
));
676 checkResult
= ENTRY_NOT_WANTED
;
679 switch (checkResult
) {
681 // Nothing more to do here, the consumer is responsible to handle
682 // the result of OnCacheEntryCheck it self.
683 // Proceed to callback...
686 case RECHECK_AFTER_WRITE_FINISHED
:
687 LOG((" consumer will check on the entry again after write is done"));
688 // The consumer wants the entry to complete first.
689 aCallback
.mRecheckAfterWrite
= true;
692 case ENTRY_NEEDS_REVALIDATION
:
693 LOG((" will be holding callbacks until entry is revalidated"));
694 // State is READY now and from that state entry cannot transit to any other
695 // state then REVALIDATING for which cocurrency is not an issue. Potentially
696 // no need to lock here.
697 mState
= REVALIDATING
;
700 case ENTRY_NOT_WANTED
:
701 LOG((" consumer not interested in the entry"));
702 // Do not give this entry to the consumer, it is not interested in us.
703 aCallback
.mNotWanted
= true;
710 if (aCallback
.mCallback
) {
711 if (!mIsDoomed
&& aCallback
.mRecheckAfterWrite
) {
712 // If we don't have data and the callback wants a complete entry,
714 bool bypass
= !mHasData
;
715 if (!bypass
&& NS_SUCCEEDED(mFileStatus
)) {
717 bypass
= !mFile
->DataSize(&_unused
);
721 LOG((" bypassing, entry data still being written"));
725 // Entry is complete now, do the check+avail call again
726 aCallback
.mRecheckAfterWrite
= false;
727 return InvokeCallback(aCallback
);
730 mozilla::MutexAutoUnlock
unlock(mLock
);
731 InvokeAvailableCallback(aCallback
);
737 void CacheEntry::InvokeAvailableCallback(Callback
const & aCallback
)
739 LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
740 this, StateString(mState
), aCallback
.mCallback
.get(), aCallback
.mReadOnly
, aCallback
.mNotWanted
));
744 uint32_t const state
= mState
;
746 // When we are here, the entry must be loaded from disk
747 MOZ_ASSERT(state
> LOADING
|| mIsDoomed
);
750 rv
= aCallback
.OnAvailThread(&onAvailThread
);
752 LOG((" target thread dead?"));
756 if (!onAvailThread
) {
757 // Dispatch to the right thread
758 nsRefPtr
<AvailableCallbackRunnable
> event
=
759 new AvailableCallbackRunnable(this, aCallback
);
761 rv
= aCallback
.mTargetThread
->Dispatch(event
, nsIEventTarget::DISPATCH_NORMAL
);
762 LOG((" redispatched, (rv = 0x%08x)", rv
));
766 if (NS_SUCCEEDED(mFileStatus
) && !aCallback
.mSecret
) {
767 // Let the last-fetched and fetch-count properties be updated.
771 if (mIsDoomed
|| aCallback
.mNotWanted
) {
772 LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
773 aCallback
.mCallback
->OnCacheEntryAvailable(
774 nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND
);
778 if (state
== READY
) {
779 LOG((" ready/has-meta, notifying OCEA with entry and NS_OK"));
781 if (!aCallback
.mSecret
)
783 mozilla::MutexAutoLock
lock(mLock
);
784 BackgroundOp(Ops::FRECENCYUPDATE
);
787 nsRefPtr
<CacheEntryHandle
> handle
= NewHandle();
788 aCallback
.mCallback
->OnCacheEntryAvailable(
789 handle
, false, nullptr, NS_OK
);
793 if (aCallback
.mReadOnly
) {
794 LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
795 aCallback
.mCallback
->OnCacheEntryAvailable(
796 nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND
);
800 // This is a new or potentially non-valid entry and needs to be fetched first.
801 // The CacheEntryHandle blocks other consumers until the channel
802 // either releases the entry or marks metadata as filled or whole entry valid,
803 // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.
805 // Consumer will be responsible to fill or validate the entry metadata and data.
807 nsRefPtr
<CacheEntryHandle
> handle
= NewWriteHandle();
808 rv
= aCallback
.mCallback
->OnCacheEntryAvailable(
809 handle
, state
== WRITING
, nullptr, NS_OK
);
812 LOG((" writing/revalidating failed (0x%08x)", rv
));
814 // Consumer given a new entry failed to take care of the entry.
815 OnHandleClosed(handle
);
819 LOG((" writing/revalidating"));
822 CacheEntryHandle
* CacheEntry::NewHandle()
824 return new CacheEntryHandle(this);
827 CacheEntryHandle
* CacheEntry::NewWriteHandle()
829 mozilla::MutexAutoLock
lock(mLock
);
831 // Ignore the OPEN_SECRETLY flag on purpose here, which should actually be
832 // used only along with OPEN_READONLY, but there is no need to enforce that.
833 BackgroundOp(Ops::FRECENCYUPDATE
);
835 return (mWriter
= NewHandle());
838 void CacheEntry::OnHandleClosed(CacheEntryHandle
const* aHandle
)
840 LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState
), aHandle
));
842 nsCOMPtr
<nsIOutputStream
> outputStream
;
845 mozilla::MutexAutoLock
lock(mLock
);
847 if (mWriter
!= aHandle
) {
848 LOG((" not the writer"));
853 // No one took our internal output stream, so there are no data
854 // and output stream has to be open symultaneously with input stream
855 // on this entry again.
859 outputStream
.swap(mOutputStream
);
862 if (mState
== WRITING
) {
863 LOG((" reverting to state EMPTY - write failed"));
866 else if (mState
== REVALIDATING
) {
867 LOG((" reverting to state READY - reval failed"));
871 if (mState
== READY
&& !mHasData
) {
872 // We may get to this state when following steps happen:
873 // 1. a new entry is given to a consumer
874 // 2. the consumer calls MetaDataReady(), we transit to READY
875 // 3. abandons the entry w/o opening the output stream, mHasData left false
877 // In this case any following consumer will get a ready entry (with metadata)
878 // but in state like the entry data write was still happening (was in progress)
879 // and will indefinitely wait for the entry data or even the entry itself when
880 // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck.
881 LOG((" we are in READY state, pretend we have data regardless it"
882 " has actully been never touched"));
890 LOG((" abandoning phantom output stream"));
891 outputStream
->Close();
895 void CacheEntry::OnOutputClosed()
897 // Called when the file's output stream is closed. Invoke any callbacks
898 // waiting for complete entry.
900 mozilla::MutexAutoLock
lock(mLock
);
904 bool CacheEntry::IsReferenced() const
906 CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
908 // Increasing this counter from 0 to non-null and this check both happen only
909 // under the service lock.
910 return mHandlesCount
> 0;
913 bool CacheEntry::IsFileDoomed()
915 if (NS_SUCCEEDED(mFileStatus
)) {
916 return mFile
->IsDoomed();
922 uint32_t CacheEntry::GetMetadataMemoryConsumption()
924 NS_ENSURE_SUCCESS(mFileStatus
, 0);
927 if (NS_FAILED(mFile
->ElementsSize(&size
)))
935 NS_IMETHODIMP
CacheEntry::GetPersistent(bool *aPersistToDisk
)
937 // No need to sync when only reading.
938 // When consumer needs to be consistent with state of the memory storage entries
939 // table, then let it use GetUseDisk getter that must be called under the service lock.
940 *aPersistToDisk
= mUseDisk
;
944 NS_IMETHODIMP
CacheEntry::GetKey(nsACString
& aKey
)
946 return mURI
->GetAsciiSpec(aKey
);
949 NS_IMETHODIMP
CacheEntry::GetFetchCount(int32_t *aFetchCount
)
951 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
953 return mFile
->GetFetchCount(reinterpret_cast<uint32_t*>(aFetchCount
));
956 NS_IMETHODIMP
CacheEntry::GetLastFetched(uint32_t *aLastFetched
)
958 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
960 return mFile
->GetLastFetched(aLastFetched
);
963 NS_IMETHODIMP
CacheEntry::GetLastModified(uint32_t *aLastModified
)
965 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
967 return mFile
->GetLastModified(aLastModified
);
970 NS_IMETHODIMP
CacheEntry::GetExpirationTime(uint32_t *aExpirationTime
)
972 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
974 return mFile
->GetExpirationTime(aExpirationTime
);
977 NS_IMETHODIMP
CacheEntry::GetIsForcedValid(bool *aIsForcedValid
)
979 NS_ENSURE_ARG(aIsForcedValid
);
983 nsresult rv
= HashingKeyWithStorage(key
);
988 *aIsForcedValid
= CacheStorageService::Self()->IsForcedValidEntry(key
);
989 LOG(("CacheEntry::GetIsForcedValid [this=%p, IsForcedValid=%d]", this, *aIsForcedValid
));
994 NS_IMETHODIMP
CacheEntry::ForceValidFor(uint32_t aSecondsToTheFuture
)
996 LOG(("CacheEntry::ForceValidFor [this=%p, aSecondsToTheFuture=%d]", this, aSecondsToTheFuture
));
999 nsresult rv
= HashingKeyWithStorage(key
);
1000 if (NS_FAILED(rv
)) {
1004 CacheStorageService::Self()->ForceEntryValidFor(key
, aSecondsToTheFuture
);
1009 NS_IMETHODIMP
CacheEntry::SetExpirationTime(uint32_t aExpirationTime
)
1011 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1013 nsresult rv
= mFile
->SetExpirationTime(aExpirationTime
);
1014 NS_ENSURE_SUCCESS(rv
, rv
);
1016 // Aligned assignment, thus atomic.
1017 mSortingExpirationTime
= aExpirationTime
;
1021 NS_IMETHODIMP
CacheEntry::OpenInputStream(int64_t offset
, nsIInputStream
* *_retval
)
1023 LOG(("CacheEntry::OpenInputStream [this=%p]", this));
1025 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1029 nsCOMPtr
<nsIInputStream
> stream
;
1030 rv
= mFile
->OpenInputStream(getter_AddRefs(stream
));
1031 NS_ENSURE_SUCCESS(rv
, rv
);
1033 nsCOMPtr
<nsISeekableStream
> seekable
=
1034 do_QueryInterface(stream
, &rv
);
1035 NS_ENSURE_SUCCESS(rv
, rv
);
1037 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1038 NS_ENSURE_SUCCESS(rv
, rv
);
1040 mozilla::MutexAutoLock
lock(mLock
);
1043 // So far output stream on this new entry not opened, do it now.
1044 LOG((" creating phantom output stream"));
1045 rv
= OpenOutputStreamInternal(0, getter_AddRefs(mOutputStream
));
1046 NS_ENSURE_SUCCESS(rv
, rv
);
1049 stream
.forget(_retval
);
1053 NS_IMETHODIMP
CacheEntry::OpenOutputStream(int64_t offset
, nsIOutputStream
* *_retval
)
1055 LOG(("CacheEntry::OpenOutputStream [this=%p]", this));
1059 mozilla::MutexAutoLock
lock(mLock
);
1061 MOZ_ASSERT(mState
> EMPTY
);
1063 if (mOutputStream
&& !mIsDoomed
) {
1064 LOG((" giving phantom output stream"));
1065 mOutputStream
.forget(_retval
);
1068 rv
= OpenOutputStreamInternal(offset
, _retval
);
1069 if (NS_FAILED(rv
)) return rv
;
1072 // Entry considered ready when writer opens output stream.
1076 // Invoke any pending readers now.
1082 nsresult
CacheEntry::OpenOutputStreamInternal(int64_t offset
, nsIOutputStream
* *_retval
)
1084 LOG(("CacheEntry::OpenOutputStreamInternal [this=%p]", this));
1086 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1088 mLock
.AssertCurrentThreadOwns();
1091 LOG((" doomed..."));
1092 return NS_ERROR_NOT_AVAILABLE
;
1095 MOZ_ASSERT(mState
> LOADING
);
1099 // No need to sync on mUseDisk here, we don't need to be consistent
1100 // with content of the memory storage entries hash table.
1102 rv
= mFile
->SetMemoryOnly();
1103 NS_ENSURE_SUCCESS(rv
, rv
);
1106 nsRefPtr
<CacheOutputCloseListener
> listener
=
1107 new CacheOutputCloseListener(this);
1109 nsCOMPtr
<nsIOutputStream
> stream
;
1110 rv
= mFile
->OpenOutputStream(listener
, getter_AddRefs(stream
));
1111 NS_ENSURE_SUCCESS(rv
, rv
);
1113 nsCOMPtr
<nsISeekableStream
> seekable
=
1114 do_QueryInterface(stream
, &rv
);
1115 NS_ENSURE_SUCCESS(rv
, rv
);
1117 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
1118 NS_ENSURE_SUCCESS(rv
, rv
);
1120 // Prevent opening output stream again.
1123 stream
.swap(*_retval
);
1127 NS_IMETHODIMP
CacheEntry::GetPredictedDataSize(int64_t *aPredictedDataSize
)
1129 *aPredictedDataSize
= mPredictedDataSize
;
1132 NS_IMETHODIMP
CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize
)
1134 mPredictedDataSize
= aPredictedDataSize
;
1136 if (CacheObserver::EntryIsTooBig(mPredictedDataSize
, mUseDisk
)) {
1137 LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this));
1140 return NS_ERROR_FILE_TOO_BIG
;
1146 NS_IMETHODIMP
CacheEntry::GetSecurityInfo(nsISupports
* *aSecurityInfo
)
1149 mozilla::MutexAutoLock
lock(mLock
);
1150 if (mSecurityInfoLoaded
) {
1151 NS_IF_ADDREF(*aSecurityInfo
= mSecurityInfo
);
1156 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1158 nsXPIDLCString info
;
1159 nsCOMPtr
<nsISupports
> secInfo
;
1162 rv
= mFile
->GetElement("security-info", getter_Copies(info
));
1163 NS_ENSURE_SUCCESS(rv
, rv
);
1166 rv
= NS_DeserializeObject(info
, getter_AddRefs(secInfo
));
1167 NS_ENSURE_SUCCESS(rv
, rv
);
1171 mozilla::MutexAutoLock
lock(mLock
);
1173 mSecurityInfo
.swap(secInfo
);
1174 mSecurityInfoLoaded
= true;
1176 NS_IF_ADDREF(*aSecurityInfo
= mSecurityInfo
);
1181 NS_IMETHODIMP
CacheEntry::SetSecurityInfo(nsISupports
*aSecurityInfo
)
1185 NS_ENSURE_SUCCESS(mFileStatus
, mFileStatus
);
1188 mozilla::MutexAutoLock
lock(mLock
);
1190 mSecurityInfo
= aSecurityInfo
;
1191 mSecurityInfoLoaded
= true;
1194 nsCOMPtr
<nsISerializable
> serializable
=
1195 do_QueryInterface(aSecurityInfo
);
1196 if (aSecurityInfo
&& !serializable
)
1197 return NS_ERROR_UNEXPECTED
;
1201 rv
= NS_SerializeToString(serializable
, info
);
1202 NS_ENSURE_SUCCESS(rv
, rv
);
1205 rv
= mFile
->SetElement("security-info", info
.Length() ? info
.get() : nullptr);
1206 NS_ENSURE_SUCCESS(rv
, rv
);
1211 NS_IMETHODIMP
CacheEntry::GetStorageDataSize(uint32_t *aStorageDataSize
)
1213 NS_ENSURE_ARG(aStorageDataSize
);
1216 nsresult rv
= GetDataSize(&dataSize
);
1220 *aStorageDataSize
= (uint32_t)std::min(int64_t(uint32_t(-1)), dataSize
);
1225 NS_IMETHODIMP
CacheEntry::AsyncDoom(nsICacheEntryDoomCallback
*aCallback
)
1227 LOG(("CacheEntry::AsyncDoom [this=%p]", this));
1230 mozilla::MutexAutoLock
lock(mLock
);
1232 if (mIsDoomed
|| mDoomCallback
)
1233 return NS_ERROR_IN_PROGRESS
; // to aggregate have DOOMING state
1236 mDoomCallback
= aCallback
;
1239 // This immediately removes the entry from the master hashtable and also
1240 // immediately dooms the file. This way we make sure that any consumer
1241 // after this point asking for the same entry won't get
1243 // b) a new entry with the same file
1249 NS_IMETHODIMP
CacheEntry::GetMetaDataElement(const char * aKey
, char * *aRetval
)
1251 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1253 return mFile
->GetElement(aKey
, aRetval
);
1256 NS_IMETHODIMP
CacheEntry::SetMetaDataElement(const char * aKey
, const char * aValue
)
1258 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1260 return mFile
->SetElement(aKey
, aValue
);
1263 NS_IMETHODIMP
CacheEntry::VisitMetaData(nsICacheEntryMetaDataVisitor
*aVisitor
)
1265 NS_ENSURE_SUCCESS(mFileStatus
, NS_ERROR_NOT_AVAILABLE
);
1267 return mFile
->VisitMetaData(aVisitor
);
1270 NS_IMETHODIMP
CacheEntry::MetaDataReady()
1272 mozilla::MutexAutoLock
lock(mLock
);
1274 LOG(("CacheEntry::MetaDataReady [this=%p, state=%s]", this, StateString(mState
)));
1276 MOZ_ASSERT(mState
> EMPTY
);
1278 if (mState
== WRITING
)
1286 NS_IMETHODIMP
CacheEntry::SetValid()
1288 LOG(("CacheEntry::SetValid [this=%p, state=%s]", this, StateString(mState
)));
1290 nsCOMPtr
<nsIOutputStream
> outputStream
;
1293 mozilla::MutexAutoLock
lock(mLock
);
1295 MOZ_ASSERT(mState
> EMPTY
);
1302 outputStream
.swap(mOutputStream
);
1306 LOG((" abandoning phantom output stream"));
1307 outputStream
->Close();
1313 NS_IMETHODIMP
CacheEntry::Recreate(bool aMemoryOnly
,
1314 nsICacheEntry
**_retval
)
1316 LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState
)));
1318 mozilla::MutexAutoLock
lock(mLock
);
1320 nsRefPtr
<CacheEntryHandle
> handle
= ReopenTruncated(aMemoryOnly
, nullptr);
1322 handle
.forget(_retval
);
1326 BackgroundOp(Ops::CALLBACKS
, true);
1330 NS_IMETHODIMP
CacheEntry::GetDataSize(int64_t *aDataSize
)
1332 LOG(("CacheEntry::GetDataSize [this=%p]", this));
1336 mozilla::MutexAutoLock
lock(mLock
);
1339 LOG((" write in progress (no data)"));
1340 return NS_ERROR_IN_PROGRESS
;
1344 NS_ENSURE_SUCCESS(mFileStatus
, mFileStatus
);
1346 // mayhemer: TODO Problem with compression?
1347 if (!mFile
->DataSize(aDataSize
)) {
1348 LOG((" write in progress (stream active)"));
1349 return NS_ERROR_IN_PROGRESS
;
1352 LOG((" size=%lld", *aDataSize
));
1356 NS_IMETHODIMP
CacheEntry::MarkValid()
1358 // NOT IMPLEMENTED ACTUALLY
1362 NS_IMETHODIMP
CacheEntry::MaybeMarkValid()
1364 // NOT IMPLEMENTED ACTUALLY
1368 NS_IMETHODIMP
CacheEntry::HasWriteAccess(bool aWriteAllowed
, bool *aWriteAccess
)
1370 *aWriteAccess
= aWriteAllowed
;
1374 NS_IMETHODIMP
CacheEntry::Close()
1376 // NOT IMPLEMENTED ACTUALLY
1382 NS_IMETHODIMP
CacheEntry::Run()
1384 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1386 mozilla::MutexAutoLock
lock(mLock
);
1388 BackgroundOp(mBackgroundOperations
.Grab());
1392 // Management methods
1394 double CacheEntry::GetFrecency() const
1396 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1400 uint32_t CacheEntry::GetExpirationTime() const
1402 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1403 return mSortingExpirationTime
;
1406 bool CacheEntry::IsRegistered() const
1408 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1409 return mRegistration
== REGISTERED
;
1412 bool CacheEntry::CanRegister() const
1414 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1415 return mRegistration
== NEVERREGISTERED
;
1418 void CacheEntry::SetRegistered(bool aRegistered
)
1420 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1423 MOZ_ASSERT(mRegistration
== NEVERREGISTERED
);
1424 mRegistration
= REGISTERED
;
1427 MOZ_ASSERT(mRegistration
== REGISTERED
);
1428 mRegistration
= DEREGISTERED
;
1432 bool CacheEntry::Purge(uint32_t aWhat
)
1434 LOG(("CacheEntry::Purge [this=%p, what=%d]", this, aWhat
));
1436 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1439 case PURGE_DATA_ONLY_DISK_BACKED
:
1440 case PURGE_WHOLE_ONLY_DISK_BACKED
:
1441 // This is an in-memory only entry, don't purge it
1443 LOG((" not using disk"));
1448 if (mState
== WRITING
|| mState
== LOADING
|| mFrecency
== 0) {
1449 // In-progress (write or load) entries should (at least for consistency and from
1450 // the logical point of view) stay in memory.
1451 // Zero-frecency entries are those which have never been given to any consumer, those
1452 // are actually very fresh and should not go just because frecency had not been set
1454 LOG((" state=%s, frecency=%1.10f", StateString(mState
), mFrecency
));
1458 if (NS_SUCCEEDED(mFileStatus
) && mFile
->IsWriteInProgress()) {
1459 // The file is used when there are open streams or chunks/metadata still waiting for
1460 // write. In this case, this entry cannot be purged, otherwise reopenned entry
1461 // would may not even find the data on disk - CacheFile is not shared and cannot be
1462 // left orphan when its job is not done, hence keep the whole entry.
1463 LOG((" file still under use"));
1468 case PURGE_WHOLE_ONLY_DISK_BACKED
:
1471 if (!CacheStorageService::Self()->RemoveEntry(this, true)) {
1472 LOG((" not purging, still referenced"));
1476 CacheStorageService::Self()->UnregisterEntry(this);
1478 // Entry removed it self from control arrays, return true
1482 case PURGE_DATA_ONLY_DISK_BACKED
:
1484 NS_ENSURE_SUCCESS(mFileStatus
, false);
1486 mFile
->ThrowMemoryCachedData();
1488 // Entry has been left in control arrays, return false (not purged)
1497 void CacheEntry::PurgeAndDoom()
1499 LOG(("CacheEntry::PurgeAndDoom [this=%p]", this));
1501 CacheStorageService::Self()->RemoveEntry(this);
1502 DoomAlreadyRemoved();
1505 void CacheEntry::DoomAlreadyRemoved()
1507 LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));
1509 mozilla::MutexAutoLock
lock(mLock
);
1513 // This schedules dooming of the file, dooming is ensured to happen
1514 // sooner than demand to open the same file made after this point
1515 // so that we don't get this file for any newer opened entry(s).
1518 // Must force post here since may be indirectly called from
1519 // InvokeCallbacks of this entry and we don't want reentrancy here.
1520 BackgroundOp(Ops::CALLBACKS
, true);
1521 // Process immediately when on the management thread.
1522 BackgroundOp(Ops::UNREGISTER
);
1525 void CacheEntry::DoomFile()
1527 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
1529 if (NS_SUCCEEDED(mFileStatus
)) {
1530 // Always calls the callback asynchronously.
1531 rv
= mFile
->Doom(mDoomCallback
? this : nullptr);
1532 if (NS_SUCCEEDED(rv
)) {
1533 LOG((" file doomed"));
1537 if (NS_ERROR_FILE_NOT_FOUND
== rv
) {
1538 // File is set to be just memory-only, notify the callbacks
1539 // and pretend dooming has succeeded. From point of view of
1540 // the entry it actually did - the data is gone and cannot be
1546 // Always posts to the main thread.
1550 void CacheEntry::BackgroundOp(uint32_t aOperations
, bool aForceAsync
)
1552 mLock
.AssertCurrentThreadOwns();
1554 if (!CacheStorageService::IsOnManagementThread() || aForceAsync
) {
1555 if (mBackgroundOperations
.Set(aOperations
))
1556 CacheStorageService::Self()->Dispatch(this);
1558 LOG(("CacheEntry::BackgroundOp this=%p dipatch of %x", this, aOperations
));
1563 mozilla::MutexAutoUnlock
unlock(mLock
);
1565 MOZ_ASSERT(CacheStorageService::IsOnManagementThread());
1567 if (aOperations
& Ops::FRECENCYUPDATE
) {
1571 #define M_LN2 0.69314718055994530942
1574 // Half-life is dynamic, in seconds.
1575 static double half_life
= CacheObserver::HalfLifeSeconds();
1576 // Must convert from seconds to milliseconds since PR_Now() gives usecs.
1577 static double const decay
= (M_LN2
/ half_life
) / static_cast<double>(PR_USEC_PER_SEC
);
1579 double now_decay
= static_cast<double>(PR_Now()) * decay
;
1581 if (mFrecency
== 0) {
1582 mFrecency
= now_decay
;
1585 // TODO: when C++11 enabled, use std::log1p(n) which is equal to log(n + 1) but
1587 mFrecency
= log(exp(mFrecency
- now_decay
) + 1) + now_decay
;
1589 LOG(("CacheEntry FRECENCYUPDATE [this=%p, frecency=%1.10f]", this, mFrecency
));
1591 // Because CacheFile::Set*() are not thread-safe to use (uses WeakReference that
1592 // is not thread-safe) we must post to the main thread...
1593 nsRefPtr
<nsRunnableMethod
<CacheEntry
> > event
=
1594 NS_NewRunnableMethod(this, &CacheEntry::StoreFrecency
);
1595 NS_DispatchToMainThread(event
);
1598 if (aOperations
& Ops::REGISTER
) {
1599 LOG(("CacheEntry REGISTER [this=%p]", this));
1601 CacheStorageService::Self()->RegisterEntry(this);
1604 if (aOperations
& Ops::UNREGISTER
) {
1605 LOG(("CacheEntry UNREGISTER [this=%p]", this));
1607 CacheStorageService::Self()->UnregisterEntry(this);
1611 if (aOperations
& Ops::CALLBACKS
) {
1612 LOG(("CacheEntry CALLBACKS (invoke) [this=%p]", this));
1618 void CacheEntry::StoreFrecency()
1620 // No need for thread safety over mFrecency, it will be rewriten
1621 // correctly on following invocation if broken by concurrency.
1622 MOZ_ASSERT(NS_IsMainThread());
1624 if (NS_SUCCEEDED(mFileStatus
)) {
1625 mFile
->SetFrecency(FRECENCY2INT(mFrecency
));
1629 // CacheOutputCloseListener
1631 CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry
* aEntry
)
1634 MOZ_COUNT_CTOR(CacheOutputCloseListener
);
1637 CacheOutputCloseListener::~CacheOutputCloseListener()
1639 MOZ_COUNT_DTOR(CacheOutputCloseListener
);
1642 void CacheOutputCloseListener::OnOutputClosed()
1644 // We need this class and to redispatch since this callback is invoked
1645 // under the file's lock and to do the job we need to enter the entry's
1646 // lock too. That would lead to potential deadlocks.
1647 NS_DispatchToCurrentThread(this);
1650 NS_IMETHODIMP
CacheOutputCloseListener::Run()
1652 mEntry
->OnOutputClosed();
1658 size_t CacheEntry::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1661 nsCOMPtr
<nsISizeOf
> sizeOf
;
1663 n
+= mCallbacks
.SizeOfExcludingThis(mallocSizeOf
);
1665 n
+= mFile
->SizeOfIncludingThis(mallocSizeOf
);
1668 sizeOf
= do_QueryInterface(mURI
);
1670 n
+= sizeOf
->SizeOfIncludingThis(mallocSizeOf
);
1673 n
+= mEnhanceID
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1674 n
+= mStorageID
.SizeOfExcludingThisIfUnshared(mallocSizeOf
);
1676 // mDoomCallback is an arbitrary class that is probably reported elsewhere.
1677 // mOutputStream is reported in mFile.
1678 // mWriter is one of many handles we create, but (intentionally) not keep
1679 // any reference to, so those unfortunatelly cannot be reported. Handles are
1681 // mSecurityInfo doesn't impl nsISizeOf.
1686 size_t CacheEntry::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
1688 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf
);