1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef CacheEntry__h__
6 #define CacheEntry__h__
8 #include "mozilla/LinkedList.h"
9 #include "nsICacheEntry.h"
10 #include "CacheFile.h"
12 #include "nsIRunnable.h"
13 #include "nsIOutputStream.h"
14 #include "nsICacheEntryOpenCallback.h"
15 #include "nsICacheEntryDoomCallback.h"
16 #include "nsITransportSecurityInfo.h"
19 #include "nsRefPtrHashtable.h"
20 #include "nsHashKeys.h"
22 #include "nsCOMArray.h"
23 #include "nsThreadUtils.h"
24 #include "mozilla/Attributes.h"
25 #include "mozilla/Mutex.h"
26 #include "mozilla/TimeStamp.h"
28 static inline uint32_t PRTimeToSeconds(PRTime t_usec
) {
29 return uint32_t(t_usec
/ PR_USEC_PER_SEC
);
32 #define NowInSeconds() PRTimeToSeconds(PR_Now())
34 class nsIOutputStream
;
41 class CacheStorageService
;
43 class CacheOutputCloseListener
;
44 class CacheEntryHandle
;
46 class CacheEntry final
: public nsIRunnable
,
47 public CacheFileListener
,
48 // Used by CacheStorageService::MemoryPool
49 public LinkedListElement
<RefPtr
<CacheEntry
>> {
51 NS_DECL_THREADSAFE_ISUPPORTS
54 static uint64_t GetNextId();
56 CacheEntry(const nsACString
& aStorageID
, const nsACString
& aURI
,
57 const nsACString
& aEnhanceID
, bool aUseDisk
, bool aSkipSizeCheck
,
60 void AsyncOpen(nsICacheEntryOpenCallback
* aCallback
, uint32_t aFlags
);
62 CacheEntryHandle
* NewHandle();
63 // For a new and recreated entry w/o a callback, we need to wrap it
64 // with a handle to detect writing consumer is gone.
65 CacheEntryHandle
* NewWriteHandle();
67 // Forwarded to from CacheEntryHandle : nsICacheEntry
68 nsresult
GetKey(nsACString
& aKey
);
69 nsresult
GetCacheEntryId(uint64_t* aCacheEntryId
);
70 nsresult
GetPersistent(bool* aPersistToDisk
);
71 nsresult
GetFetchCount(uint32_t* aFetchCount
);
72 nsresult
GetLastFetched(uint32_t* aLastFetched
);
73 nsresult
GetLastModified(uint32_t* aLastModified
);
74 nsresult
GetExpirationTime(uint32_t* aExpirationTime
);
75 nsresult
SetExpirationTime(uint32_t expirationTime
);
76 nsresult
GetOnStartTime(uint64_t* aTime
);
77 nsresult
GetOnStopTime(uint64_t* aTime
);
78 nsresult
SetNetworkTimes(uint64_t onStartTime
, uint64_t onStopTime
);
79 nsresult
SetContentType(uint8_t aContentType
);
80 nsresult
ForceValidFor(uint32_t aSecondsToTheFuture
);
81 nsresult
GetIsForcedValid(bool* aIsForcedValid
);
82 nsresult
MarkForcedValidUse();
83 nsresult
OpenInputStream(int64_t offset
, nsIInputStream
** _retval
);
84 nsresult
OpenOutputStream(int64_t offset
, int64_t predictedSize
,
85 nsIOutputStream
** _retval
);
86 nsresult
GetSecurityInfo(nsITransportSecurityInfo
** aSecurityInfo
);
87 nsresult
SetSecurityInfo(nsITransportSecurityInfo
* aSecurityInfo
);
88 nsresult
GetStorageDataSize(uint32_t* aStorageDataSize
);
89 nsresult
AsyncDoom(nsICacheEntryDoomCallback
* aCallback
);
90 nsresult
GetMetaDataElement(const char* key
, char** aRetval
);
91 nsresult
SetMetaDataElement(const char* key
, const char* value
);
92 nsresult
VisitMetaData(nsICacheEntryMetaDataVisitor
* visitor
);
93 nsresult
MetaDataReady(void);
94 nsresult
SetValid(void);
95 nsresult
GetDiskStorageSizeInKB(uint32_t* aDiskStorageSizeInKB
);
96 nsresult
Recreate(bool aMemoryOnly
, nsICacheEntry
** _retval
);
97 nsresult
GetDataSize(int64_t* aDataSize
);
98 nsresult
GetAltDataSize(int64_t* aDataSize
);
99 nsresult
GetAltDataType(nsACString
& aAltDataType
);
100 nsresult
OpenAlternativeOutputStream(const nsACString
& type
,
101 int64_t predictedSize
,
102 nsIAsyncOutputStream
** _retval
);
103 nsresult
OpenAlternativeInputStream(const nsACString
& type
,
104 nsIInputStream
** _retval
);
105 nsresult
GetLoadContextInfo(nsILoadContextInfo
** aInfo
);
108 uint32_t GetMetadataMemoryConsumption();
109 nsCString
const& GetStorageID() const { return mStorageID
; }
110 nsCString
const& GetEnhanceID() const { return mEnhanceID
; }
111 nsCString
const& GetURI() const { return mURI
; }
112 // Accessible at any time
113 bool IsUsingDisk() const { return mUseDisk
; }
114 bool IsReferenced() const MOZ_NO_THREAD_SAFETY_ANALYSIS
;
116 bool IsDoomed() const { return mIsDoomed
; }
117 bool IsPinned() const { return mPinned
; }
119 // Methods for entry management (eviction from memory),
120 // called only on the management thread.
122 // TODO make these inline
123 double GetFrecency() const;
124 uint32_t GetExpirationTime() const;
125 uint32_t UseCount() const { return mUseCount
; }
127 bool IsRegistered() const;
128 bool CanRegister() const;
129 void SetRegistered(bool aRegistered
);
131 TimeStamp
const& LoadStart() const { return mLoadStart
; }
134 PURGE_DATA_ONLY_DISK_BACKED
,
135 PURGE_WHOLE_ONLY_DISK_BACKED
,
139 bool DeferOrBypassRemovalOnPinStatus(bool aPinned
);
140 bool Purge(uint32_t aWhat
);
142 void DoomAlreadyRemoved();
144 nsresult
HashingKeyWithStorage(nsACString
& aResult
) const;
145 nsresult
HashingKey(nsACString
& aResult
) const;
147 static nsresult
HashingKey(const nsACString
& aStorageID
,
148 const nsACString
& aEnhanceID
, nsIURI
* aURI
,
149 nsACString
& aResult
);
151 static nsresult
HashingKey(const nsACString
& aStorageID
,
152 const nsACString
& aEnhanceID
,
153 const nsACString
& aURISpec
, nsACString
& aResult
);
155 // Accessed only on the service management thread
157 ::mozilla::Atomic
<uint32_t, ::mozilla::Relaxed
> mSortingExpirationTime
{
161 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
);
162 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
);
165 virtual ~CacheEntry();
168 NS_IMETHOD
OnFileReady(nsresult aResult
, bool aIsNew
) override
;
169 NS_IMETHOD
OnFileDoomed(nsresult aResult
) override
;
171 // Keep the service alive during life-time of an entry
172 RefPtr
<CacheStorageService
> mService
;
174 // We must monitor when a cache entry whose consumer is responsible
175 // for writing it the first time gets released. We must then invoke
176 // waiting callbacks to not break the chain.
179 Callback(CacheEntry
* aEntry
, nsICacheEntryOpenCallback
* aCallback
,
180 bool aReadOnly
, bool aCheckOnAnyThread
, bool aSecret
);
181 // Special constructor for Callback objects added to the chain
182 // just to ensure proper defer dooming (recreation) of this entry.
183 Callback(CacheEntry
* aEntry
, bool aDoomWhenFoundInPinStatus
);
184 Callback(Callback
const& aThat
);
187 // Called when this callback record changes it's owning entry,
188 // mainly during recreation.
189 void ExchangeEntry(CacheEntry
* aEntry
) MOZ_REQUIRES(aEntry
->mLock
);
191 // Returns true when an entry is about to be "defer" doomed and this is
192 // a "defer" callback. The caller must hold a lock (this entry is in the
193 // caller's mCallback array)
194 bool DeferDoom(bool* aDoom
) const;
196 // We are raising reference count here to take into account the pending
197 // callback (that virtually holds a ref to this entry before it gets
199 RefPtr
<CacheEntry
> mEntry
;
200 nsCOMPtr
<nsICacheEntryOpenCallback
> mCallback
;
201 nsCOMPtr
<nsIEventTarget
> mTarget
;
203 bool mRevalidating
: 1;
204 bool mCheckOnAnyThread
: 1;
205 bool mRecheckAfterWrite
: 1;
209 // These are set only for the defer-doomer Callback instance inserted
210 // to the callback chain. When any of these is set and also any of
211 // the corressponding flags on the entry is set, this callback will
212 // indicate (via DeferDoom()) the entry have to be recreated/doomed.
213 bool mDoomWhenFoundPinned
: 1;
214 bool mDoomWhenFoundNonPinned
: 1;
216 nsresult
OnCheckThread(bool* aOnCheckThread
) const;
217 nsresult
OnAvailThread(bool* aOnAvailThread
) const;
220 // Since OnCacheEntryAvailable must be invoked on the main thread
221 // we need a runnable for it...
222 class AvailableCallbackRunnable
: public Runnable
{
224 AvailableCallbackRunnable(CacheEntry
* aEntry
, Callback
const& aCallback
)
225 : Runnable("CacheEntry::AvailableCallbackRunnable"),
227 mCallback(aCallback
) {}
230 NS_IMETHOD
Run() override
{
231 mEntry
->InvokeAvailableCallback(mCallback
);
235 RefPtr
<CacheEntry
> mEntry
;
239 // Since OnCacheEntryDoomed must be invoked on the main thread
240 // we need a runnable for it...
241 class DoomCallbackRunnable
: public Runnable
{
243 DoomCallbackRunnable(CacheEntry
* aEntry
, nsresult aRv
)
244 : Runnable("net::CacheEntry::DoomCallbackRunnable"),
249 NS_IMETHOD
Run() override
{
250 nsCOMPtr
<nsICacheEntryDoomCallback
> callback
;
252 mozilla::MutexAutoLock
lock(mEntry
->mLock
);
253 mEntry
->mDoomCallback
.swap(callback
);
256 if (callback
) callback
->OnCacheEntryDoomed(mRv
);
260 RefPtr
<CacheEntry
> mEntry
;
264 // Starts the load or just invokes the callback, bypasses (when required)
265 // if busy. Returns true on job done, false on bypass.
266 bool Open(Callback
& aCallback
, bool aTruncate
, bool aPriority
,
268 // Loads from disk asynchronously
269 bool Load(bool aTruncate
, bool aPriority
);
271 void RememberCallback(Callback
& aCallback
) MOZ_REQUIRES(mLock
);
272 void InvokeCallbacksLock();
273 void InvokeCallbacks();
274 bool InvokeCallbacks(bool aReadOnly
);
275 bool InvokeCallback(Callback
& aCallback
);
276 void InvokeAvailableCallback(Callback
const& aCallback
);
277 void OnFetched(Callback
const& aCallback
);
279 nsresult
OpenOutputStreamInternal(int64_t offset
, nsIOutputStream
** _retval
);
280 nsresult
OpenInputStreamInternal(int64_t offset
, const char* aAltDataType
,
281 nsIInputStream
** _retval
);
283 void OnHandleClosed(CacheEntryHandle
const* aHandle
);
286 friend class CacheEntryHandle
;
287 // Increment/decrements the number of handles keeping this entry.
288 void AddHandleRef() MOZ_REQUIRES(mLock
) { ++mHandlesCount
; }
289 void ReleaseHandleRef() MOZ_REQUIRES(mLock
) { --mHandlesCount
; }
290 // Current number of handles keeping this entry.
291 uint32_t HandlesCount() const MOZ_REQUIRES(mLock
) { return mHandlesCount
; }
294 friend class CacheOutputCloseListener
;
295 void OnOutputClosed();
298 // Schedules a background operation on the management thread.
299 // When executed on the management thread directly, the operation(s)
300 // is (are) executed immediately.
301 void BackgroundOp(uint32_t aOperation
, bool aForceAsync
= false);
302 void StoreFrecency(double aFrecency
);
304 // Called only from DoomAlreadyRemoved()
305 void DoomFile() MOZ_REQUIRES(mLock
);
306 // When this entry is doomed the first time, this method removes
307 // any force-valid timing info for this entry.
308 void RemoveForcedValidity();
310 already_AddRefed
<CacheEntryHandle
> ReopenTruncated(
311 bool aMemoryOnly
, nsICacheEntryOpenCallback
* aCallback
);
312 void TransferCallbacks(CacheEntry
& aFromEntry
);
314 mozilla::Mutex mLock
{"CacheEntry"};
316 // Reflects the number of existing handles for this entry
317 ::mozilla::ThreadSafeAutoRefCnt mHandlesCount
MOZ_GUARDED_BY(mLock
);
319 nsTArray
<Callback
> mCallbacks
MOZ_GUARDED_BY(mLock
);
320 nsCOMPtr
<nsICacheEntryDoomCallback
> mDoomCallback
;
322 // Set in CacheEntry::Load(), only - shouldn't need to be under lock
323 // XXX FIX? is this correct?
324 RefPtr
<CacheFile
> mFile
;
326 // Using ReleaseAcquire since we only control access to mFile with this.
327 // When mFileStatus is read and found success it is ensured there is mFile and
328 // that it is after a successful call to Init().
329 Atomic
<nsresult
, ReleaseAcquire
> mFileStatus
{NS_ERROR_NOT_INITIALIZED
};
330 // Set in constructor
331 nsCString
const mURI
;
332 nsCString
const mEnhanceID
;
333 nsCString
const mStorageID
;
335 // mUseDisk, mSkipSizeCheck, mIsDoomed are plain "bool", not "bool:1",
336 // so as to avoid bitfield races with the byte containing
337 // mSecurityInfoLoaded et al. See bug 1278524.
339 // Whether it's allowed to persist the data to disk
341 // Whether it should skip max size check.
342 bool const mSkipSizeCheck
;
343 // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
344 Atomic
<bool, Relaxed
> mIsDoomed
{false};
345 // The indication of pinning this entry was open with
346 Atomic
<bool, Relaxed
> mPinned
;
348 // Following flags are all synchronized with the cache entry lock.
350 // Whether security info has already been looked up in metadata.
351 bool mSecurityInfoLoaded
: 1 MOZ_GUARDED_BY(mLock
);
352 // Prevents any callback invocation
353 bool mPreventCallbacks
: 1 MOZ_GUARDED_BY(mLock
);
354 // true: after load and an existing file, or after output stream has been
356 // note - when opening an input stream, and this flag is false, output
357 // stream is open along ; this makes input streams on new entries
358 // behave correctly when EOF is reached (WOULD_BLOCK is returned).
359 // false: after load and a new file, or dropped to back to false when a
360 // writer fails to open an output stream.
361 bool mHasData
: 1 MOZ_GUARDED_BY(mLock
);
362 // Whether the pinning state of the entry is known (equals to the actual state
363 // of the cache file)
364 bool mPinningKnown
: 1 MOZ_GUARDED_BY(mLock
);
366 static char const* StateString(uint32_t aState
);
368 enum EState
{ // transiting to:
369 NOTLOADED
= 0, // -> LOADING | EMPTY
370 LOADING
= 1, // -> EMPTY | READY
371 EMPTY
= 2, // -> WRITING
372 WRITING
= 3, // -> EMPTY | READY
373 READY
= 4, // -> REVALIDATING
374 REVALIDATING
= 5 // -> READY
377 // State of this entry.
378 EState mState
MOZ_GUARDED_BY(mLock
){NOTLOADED
};
381 NEVERREGISTERED
= 0, // The entry has never been registered
382 REGISTERED
= 1, // The entry is stored in the memory pool index
383 DEREGISTERED
= 2 // The entry has been removed from the pool
386 // Accessed only on the management thread. Records the state of registration
387 // this entry in the memory pool intermediate cache.
388 ERegistration mRegistration
{NEVERREGISTERED
};
390 // If a new (empty) entry is requested to open an input stream before
391 // output stream has been opened, we must open output stream internally
392 // on CacheFile and hold until writer releases the entry or opens the output
393 // stream for read (then we trade him mOutputStream).
394 nsCOMPtr
<nsIOutputStream
> mOutputStream
MOZ_GUARDED_BY(mLock
);
396 // Weak reference to the current writter. There can be more then one
397 // writer at a time and OnHandleClosed() must be processed only for the
399 CacheEntryHandle
* mWriter
MOZ_GUARDED_BY(mLock
){nullptr};
401 // Background thread scheduled operation. Set (under the lock) one
402 // of this flags to tell the background thread what to do.
405 static uint32_t const REGISTER
= 1 << 0;
406 static uint32_t const FRECENCYUPDATE
= 1 << 1;
407 static uint32_t const CALLBACKS
= 1 << 2;
408 static uint32_t const UNREGISTER
= 1 << 3;
412 uint32_t flags
= mFlags
;
416 bool Set(uint32_t aFlags
) {
417 if (mFlags
& aFlags
) return false;
424 } mBackgroundOperations
;
426 nsCOMPtr
<nsITransportSecurityInfo
> mSecurityInfo
;
427 mozilla::TimeStamp mLoadStart
;
428 uint32_t mUseCount
{0};
430 const uint64_t mCacheEntryId
;
433 class CacheEntryHandle final
: public nsICacheEntry
{
435 explicit CacheEntryHandle(CacheEntry
* aEntry
);
436 CacheEntry
* Entry() const { return mEntry
; }
438 NS_DECL_THREADSAFE_ISUPPORTS
440 // Default implementation is simply safely forwarded.
441 NS_IMETHOD
GetKey(nsACString
& aKey
) override
{ return mEntry
->GetKey(aKey
); }
442 NS_IMETHOD
GetCacheEntryId(uint64_t* aCacheEntryId
) override
{
443 return mEntry
->GetCacheEntryId(aCacheEntryId
);
445 NS_IMETHOD
GetPersistent(bool* aPersistent
) override
{
446 return mEntry
->GetPersistent(aPersistent
);
448 NS_IMETHOD
GetFetchCount(uint32_t* aFetchCount
) override
{
449 return mEntry
->GetFetchCount(aFetchCount
);
451 NS_IMETHOD
GetLastFetched(uint32_t* aLastFetched
) override
{
452 return mEntry
->GetLastFetched(aLastFetched
);
454 NS_IMETHOD
GetLastModified(uint32_t* aLastModified
) override
{
455 return mEntry
->GetLastModified(aLastModified
);
457 NS_IMETHOD
GetExpirationTime(uint32_t* aExpirationTime
) override
{
458 return mEntry
->GetExpirationTime(aExpirationTime
);
460 NS_IMETHOD
SetExpirationTime(uint32_t expirationTime
) override
{
461 return mEntry
->SetExpirationTime(expirationTime
);
463 NS_IMETHOD
GetOnStartTime(uint64_t* aOnStartTime
) override
{
464 return mEntry
->GetOnStartTime(aOnStartTime
);
466 NS_IMETHOD
GetOnStopTime(uint64_t* aOnStopTime
) override
{
467 return mEntry
->GetOnStopTime(aOnStopTime
);
469 NS_IMETHOD
SetNetworkTimes(uint64_t onStartTime
,
470 uint64_t onStopTime
) override
{
471 return mEntry
->SetNetworkTimes(onStartTime
, onStopTime
);
473 NS_IMETHOD
SetContentType(uint8_t contentType
) override
{
474 return mEntry
->SetContentType(contentType
);
476 NS_IMETHOD
ForceValidFor(uint32_t aSecondsToTheFuture
) override
{
477 return mEntry
->ForceValidFor(aSecondsToTheFuture
);
479 NS_IMETHOD
GetIsForcedValid(bool* aIsForcedValid
) override
{
480 return mEntry
->GetIsForcedValid(aIsForcedValid
);
482 NS_IMETHOD
MarkForcedValidUse() override
{
483 return mEntry
->MarkForcedValidUse();
485 NS_IMETHOD
OpenInputStream(int64_t offset
,
486 nsIInputStream
** _retval
) override
{
487 return mEntry
->OpenInputStream(offset
, _retval
);
489 NS_IMETHOD
OpenOutputStream(int64_t offset
, int64_t predictedSize
,
490 nsIOutputStream
** _retval
) override
{
491 return mEntry
->OpenOutputStream(offset
, predictedSize
, _retval
);
493 NS_IMETHOD
GetSecurityInfo(
494 nsITransportSecurityInfo
** aSecurityInfo
) override
{
495 return mEntry
->GetSecurityInfo(aSecurityInfo
);
497 NS_IMETHOD
SetSecurityInfo(nsITransportSecurityInfo
* aSecurityInfo
) override
{
498 return mEntry
->SetSecurityInfo(aSecurityInfo
);
500 NS_IMETHOD
GetStorageDataSize(uint32_t* aStorageDataSize
) override
{
501 return mEntry
->GetStorageDataSize(aStorageDataSize
);
503 NS_IMETHOD
AsyncDoom(nsICacheEntryDoomCallback
* listener
) override
{
504 return mEntry
->AsyncDoom(listener
);
506 NS_IMETHOD
GetMetaDataElement(const char* key
, char** _retval
) override
{
507 return mEntry
->GetMetaDataElement(key
, _retval
);
509 NS_IMETHOD
SetMetaDataElement(const char* key
, const char* value
) override
{
510 return mEntry
->SetMetaDataElement(key
, value
);
512 NS_IMETHOD
VisitMetaData(nsICacheEntryMetaDataVisitor
* visitor
) override
{
513 return mEntry
->VisitMetaData(visitor
);
515 NS_IMETHOD
MetaDataReady(void) override
{ return mEntry
->MetaDataReady(); }
516 NS_IMETHOD
SetValid(void) override
{ return mEntry
->SetValid(); }
517 NS_IMETHOD
GetDiskStorageSizeInKB(uint32_t* aDiskStorageSizeInKB
) override
{
518 return mEntry
->GetDiskStorageSizeInKB(aDiskStorageSizeInKB
);
520 NS_IMETHOD
Recreate(bool aMemoryOnly
, nsICacheEntry
** _retval
) override
{
521 return mEntry
->Recreate(aMemoryOnly
, _retval
);
523 NS_IMETHOD
GetDataSize(int64_t* aDataSize
) override
{
524 return mEntry
->GetDataSize(aDataSize
);
526 NS_IMETHOD
GetAltDataSize(int64_t* aAltDataSize
) override
{
527 return mEntry
->GetAltDataSize(aAltDataSize
);
529 NS_IMETHOD
GetAltDataType(nsACString
& aType
) override
{
530 return mEntry
->GetAltDataType(aType
);
532 NS_IMETHOD
OpenAlternativeOutputStream(
533 const nsACString
& type
, int64_t predictedSize
,
534 nsIAsyncOutputStream
** _retval
) override
{
535 return mEntry
->OpenAlternativeOutputStream(type
, predictedSize
, _retval
);
537 NS_IMETHOD
OpenAlternativeInputStream(const nsACString
& type
,
538 nsIInputStream
** _retval
) override
{
539 return mEntry
->OpenAlternativeInputStream(type
, _retval
);
541 NS_IMETHOD
GetLoadContextInfo(
542 nsILoadContextInfo
** aLoadContextInfo
) override
{
543 return mEntry
->GetLoadContextInfo(aLoadContextInfo
);
546 // Specific implementation:
547 NS_IMETHOD
Dismiss() override
;
550 virtual ~CacheEntryHandle();
551 RefPtr
<CacheEntry
> mEntry
;
553 // This is |false| until Dismiss() was called and prevents OnHandleClosed
554 // being called more than once.
555 Atomic
<bool, ReleaseAcquire
> mClosed
{false};
558 class CacheOutputCloseListener final
: public Runnable
{
560 void OnOutputClosed();
563 friend class CacheEntry
;
565 virtual ~CacheOutputCloseListener() = default;
568 explicit CacheOutputCloseListener(CacheEntry
* aEntry
);
571 RefPtr
<CacheEntry
> mEntry
;
575 } // namespace mozilla