Line data Source code
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/. */
4 :
5 : #ifndef CacheEntry__h__
6 : #define CacheEntry__h__
7 :
8 : #include "nsICacheEntry.h"
9 : #include "CacheFile.h"
10 :
11 : #include "nsIRunnable.h"
12 : #include "nsIOutputStream.h"
13 : #include "nsICacheEntryOpenCallback.h"
14 : #include "nsICacheEntryDoomCallback.h"
15 :
16 : #include "nsCOMPtr.h"
17 : #include "nsRefPtrHashtable.h"
18 : #include "nsDataHashtable.h"
19 : #include "nsHashKeys.h"
20 : #include "nsString.h"
21 : #include "nsCOMArray.h"
22 : #include "nsThreadUtils.h"
23 : #include "mozilla/Attributes.h"
24 : #include "mozilla/Mutex.h"
25 : #include "mozilla/TimeStamp.h"
26 :
27 : static inline uint32_t
28 0 : PRTimeToSeconds(PRTime t_usec)
29 : {
30 0 : PRTime usec_per_sec = PR_USEC_PER_SEC;
31 0 : return uint32_t(t_usec /= usec_per_sec);
32 : }
33 :
34 : #define NowInSeconds() PRTimeToSeconds(PR_Now())
35 :
36 : class nsIOutputStream;
37 : class nsIURI;
38 : class nsIThread;
39 :
40 : namespace mozilla {
41 : namespace net {
42 :
43 : class CacheStorageService;
44 : class CacheStorage;
45 : class CacheOutputCloseListener;
46 : class CacheEntryHandle;
47 :
48 : class CacheEntry final : public nsICacheEntry
49 : , public nsIRunnable
50 : , public CacheFileListener
51 : {
52 : public:
53 : NS_DECL_THREADSAFE_ISUPPORTS
54 : NS_DECL_NSICACHEENTRY
55 : NS_DECL_NSIRUNNABLE
56 :
57 : CacheEntry(const nsACString& aStorageID, const nsACString& aURI, const nsACString& aEnhanceID,
58 : bool aUseDisk, bool aSkipSizeCheck, bool aPin);
59 :
60 : void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags);
61 :
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();
66 :
67 : public:
68 : uint32_t GetMetadataMemoryConsumption();
69 10 : nsCString const &GetStorageID() const { return mStorageID; }
70 0 : nsCString const &GetEnhanceID() const { return mEnhanceID; }
71 0 : nsCString const &GetURI() const { return mURI; }
72 : // Accessible at any time
73 5 : bool IsUsingDisk() const { return mUseDisk; }
74 : bool IsReferenced() const;
75 : bool IsFileDoomed();
76 28 : bool IsDoomed() const { return mIsDoomed; }
77 0 : bool IsPinned() const { return mPinned; }
78 :
79 : // Methods for entry management (eviction from memory),
80 : // called only on the management thread.
81 :
82 : // TODO make these inline
83 : double GetFrecency() const;
84 : uint32_t GetExpirationTime() const;
85 0 : uint32_t UseCount() const { return mUseCount; }
86 :
87 : bool IsRegistered() const;
88 : bool CanRegister() const;
89 : void SetRegistered(bool aRegistered);
90 :
91 0 : TimeStamp const& LoadStart() const { return mLoadStart; }
92 :
93 : enum EPurge {
94 : PURGE_DATA_ONLY_DISK_BACKED,
95 : PURGE_WHOLE_ONLY_DISK_BACKED,
96 : PURGE_WHOLE,
97 : };
98 :
99 : bool DeferOrBypassRemovalOnPinStatus(bool aPinned);
100 : bool Purge(uint32_t aWhat);
101 : void PurgeAndDoom();
102 : void DoomAlreadyRemoved();
103 :
104 : nsresult HashingKeyWithStorage(nsACString &aResult) const;
105 : nsresult HashingKey(nsACString &aResult) const;
106 :
107 : static nsresult HashingKey(const nsACString& aStorageID,
108 : const nsACString& aEnhanceID,
109 : nsIURI* aURI,
110 : nsACString &aResult);
111 :
112 : static nsresult HashingKey(const nsACString& aStorageID,
113 : const nsACString& aEnhanceID,
114 : const nsACString& aURISpec,
115 : nsACString &aResult);
116 :
117 : // Accessed only on the service management thread
118 : double mFrecency;
119 : ::mozilla::Atomic<uint32_t, ::mozilla::Relaxed> mSortingExpirationTime;
120 :
121 : // Memory reporting
122 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
123 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
124 :
125 : private:
126 : virtual ~CacheEntry();
127 :
128 : // CacheFileListener
129 : NS_IMETHOD OnFileReady(nsresult aResult, bool aIsNew) override;
130 : NS_IMETHOD OnFileDoomed(nsresult aResult) override;
131 :
132 : // Keep the service alive during life-time of an entry
133 : RefPtr<CacheStorageService> mService;
134 :
135 : // We must monitor when a cache entry whose consumer is responsible
136 : // for writing it the first time gets released. We must then invoke
137 : // waiting callbacks to not break the chain.
138 : class Callback
139 : {
140 : public:
141 : Callback(CacheEntry* aEntry,
142 : nsICacheEntryOpenCallback *aCallback,
143 : bool aReadOnly, bool aCheckOnAnyThread, bool aSecret);
144 : // Special constructor for Callback objects added to the chain
145 : // just to ensure proper defer dooming (recreation) of this entry.
146 : Callback(CacheEntry* aEntry, bool aDoomWhenFoundInPinStatus);
147 : Callback(Callback const &aThat);
148 : ~Callback();
149 :
150 : // Called when this callback record changes it's owning entry,
151 : // mainly during recreation.
152 : void ExchangeEntry(CacheEntry* aEntry);
153 :
154 : // Returns true when an entry is about to be "defer" doomed and this is
155 : // a "defer" callback.
156 : bool DeferDoom(bool *aDoom) const;
157 :
158 : // We are raising reference count here to take into account the pending
159 : // callback (that virtually holds a ref to this entry before it gets
160 : // it's pointer).
161 : RefPtr<CacheEntry> mEntry;
162 : nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
163 : nsCOMPtr<nsIEventTarget> mTarget;
164 : bool mReadOnly : 1;
165 : bool mRevalidating : 1;
166 : bool mCheckOnAnyThread : 1;
167 : bool mRecheckAfterWrite : 1;
168 : bool mNotWanted : 1;
169 : bool mSecret : 1;
170 :
171 : // These are set only for the defer-doomer Callback instance inserted
172 : // to the callback chain. When any of these is set and also any of
173 : // the corressponding flags on the entry is set, this callback will
174 : // indicate (via DeferDoom()) the entry have to be recreated/doomed.
175 : bool mDoomWhenFoundPinned : 1;
176 : bool mDoomWhenFoundNonPinned : 1;
177 :
178 : nsresult OnCheckThread(bool *aOnCheckThread) const;
179 : nsresult OnAvailThread(bool *aOnAvailThread) const;
180 : };
181 :
182 : // Since OnCacheEntryAvailable must be invoked on the main thread
183 : // we need a runnable for it...
184 18 : class AvailableCallbackRunnable : public Runnable
185 : {
186 : public:
187 6 : AvailableCallbackRunnable(CacheEntry* aEntry,
188 : Callback const &aCallback)
189 6 : : Runnable("CacheEntry::AvailableCallbackRunnable")
190 : , mEntry(aEntry)
191 6 : , mCallback(aCallback)
192 6 : {}
193 :
194 : private:
195 6 : NS_IMETHOD Run() override
196 : {
197 6 : mEntry->InvokeAvailableCallback(mCallback);
198 6 : return NS_OK;
199 : }
200 :
201 : RefPtr<CacheEntry> mEntry;
202 : Callback mCallback;
203 : };
204 :
205 : // Since OnCacheEntryDoomed must be invoked on the main thread
206 : // we need a runnable for it...
207 0 : class DoomCallbackRunnable : public Runnable
208 : {
209 : public:
210 0 : DoomCallbackRunnable(CacheEntry* aEntry, nsresult aRv)
211 0 : : Runnable("net::CacheEntry::DoomCallbackRunnable")
212 : , mEntry(aEntry)
213 0 : , mRv(aRv)
214 : {
215 0 : }
216 :
217 : private:
218 0 : NS_IMETHOD Run() override
219 : {
220 0 : nsCOMPtr<nsICacheEntryDoomCallback> callback;
221 : {
222 0 : mozilla::MutexAutoLock lock(mEntry->mLock);
223 0 : mEntry->mDoomCallback.swap(callback);
224 : }
225 :
226 0 : if (callback)
227 0 : callback->OnCacheEntryDoomed(mRv);
228 0 : return NS_OK;
229 : }
230 :
231 : RefPtr<CacheEntry> mEntry;
232 : nsresult mRv;
233 : };
234 :
235 : // Starts the load or just invokes the callback, bypasses (when required)
236 : // if busy. Returns true on job done, false on bypass.
237 : bool Open(Callback & aCallback, bool aTruncate, bool aPriority, bool aBypassIfBusy);
238 : // Loads from disk asynchronously
239 : bool Load(bool aTruncate, bool aPriority);
240 :
241 : void RememberCallback(Callback & aCallback);
242 : void InvokeCallbacksLock();
243 : void InvokeCallbacks();
244 : bool InvokeCallbacks(bool aReadOnly);
245 : bool InvokeCallback(Callback & aCallback);
246 : void InvokeAvailableCallback(Callback const & aCallback);
247 : void OnFetched(Callback const & aCallback);
248 :
249 : nsresult OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval);
250 : nsresult OpenInputStreamInternal(int64_t offset, const char *aAltDataType, nsIInputStream * *_retval);
251 :
252 : void OnHandleClosed(CacheEntryHandle const* aHandle);
253 :
254 : private:
255 : friend class CacheEntryHandle;
256 : // Increment/decrements the number of handles keeping this entry.
257 77 : void AddHandleRef() { ++mHandlesCount; }
258 77 : void ReleaseHandleRef() { --mHandlesCount; }
259 : // Current number of handles keeping this entry.
260 77 : uint32_t HandlesCount() const { return mHandlesCount; }
261 :
262 : private:
263 : friend class CacheOutputCloseListener;
264 : void OnOutputClosed();
265 :
266 : private:
267 : // Schedules a background operation on the management thread.
268 : // When executed on the management thread directly, the operation(s)
269 : // is (are) executed immediately.
270 : void BackgroundOp(uint32_t aOperation, bool aForceAsync = false);
271 : void StoreFrecency(double aFrecency);
272 :
273 : // Called only from DoomAlreadyRemoved()
274 : void DoomFile();
275 : // When this entry is doomed the first time, this method removes
276 : // any force-valid timing info for this entry.
277 : void RemoveForcedValidity();
278 :
279 : already_AddRefed<CacheEntryHandle> ReopenTruncated(bool aMemoryOnly,
280 : nsICacheEntryOpenCallback* aCallback);
281 : void TransferCallbacks(CacheEntry & aFromEntry);
282 :
283 : mozilla::Mutex mLock;
284 :
285 : // Reflects the number of existing handles for this entry
286 : ::mozilla::ThreadSafeAutoRefCnt mHandlesCount;
287 :
288 : nsTArray<Callback> mCallbacks;
289 : nsCOMPtr<nsICacheEntryDoomCallback> mDoomCallback;
290 :
291 : RefPtr<CacheFile> mFile;
292 :
293 : // Using ReleaseAcquire since we only control access to mFile with this.
294 : // When mFileStatus is read and found success it is ensured there is mFile and
295 : // that it is after a successful call to Init().
296 : ::mozilla::Atomic<nsresult, ::mozilla::ReleaseAcquire> mFileStatus;
297 : nsCString mURI;
298 : nsCString mEnhanceID;
299 : nsCString mStorageID;
300 :
301 : // mUseDisk, mSkipSizeCheck, mIsDoomed are plain "bool", not "bool:1",
302 : // so as to avoid bitfield races with the byte containing
303 : // mSecurityInfoLoaded et al. See bug 1278524.
304 : //
305 : // Whether it's allowed to persist the data to disk
306 : bool const mUseDisk;
307 : // Whether it should skip max size check.
308 : bool const mSkipSizeCheck;
309 : // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
310 : bool mIsDoomed;
311 :
312 : // Following flags are all synchronized with the cache entry lock.
313 :
314 : // Whether security info has already been looked up in metadata.
315 : bool mSecurityInfoLoaded : 1;
316 : // Prevents any callback invocation
317 : bool mPreventCallbacks : 1;
318 : // true: after load and an existing file, or after output stream has been opened.
319 : // note - when opening an input stream, and this flag is false, output stream
320 : // is open along ; this makes input streams on new entries behave correctly
321 : // when EOF is reached (WOULD_BLOCK is returned).
322 : // false: after load and a new file, or dropped to back to false when a writer
323 : // fails to open an output stream.
324 : bool mHasData : 1;
325 : // The indication of pinning this entry was open with
326 : bool mPinned : 1;
327 : // Whether the pinning state of the entry is known (equals to the actual state
328 : // of the cache file)
329 : bool mPinningKnown : 1;
330 :
331 : static char const * StateString(uint32_t aState);
332 :
333 : enum EState { // transiting to:
334 : NOTLOADED = 0, // -> LOADING | EMPTY
335 : LOADING = 1, // -> EMPTY | READY
336 : EMPTY = 2, // -> WRITING
337 : WRITING = 3, // -> EMPTY | READY
338 : READY = 4, // -> REVALIDATING
339 : REVALIDATING = 5 // -> READY
340 : };
341 :
342 : // State of this entry.
343 : EState mState;
344 :
345 : enum ERegistration {
346 : NEVERREGISTERED = 0, // The entry has never been registered
347 : REGISTERED = 1, // The entry is stored in the memory pool index
348 : DEREGISTERED = 2 // The entry has been removed from the pool
349 : };
350 :
351 : // Accessed only on the management thread. Records the state of registration
352 : // this entry in the memory pool intermediate cache.
353 : ERegistration mRegistration;
354 :
355 : // If a new (empty) entry is requested to open an input stream before
356 : // output stream has been opened, we must open output stream internally
357 : // on CacheFile and hold until writer releases the entry or opens the output
358 : // stream for read (then we trade him mOutputStream).
359 : nsCOMPtr<nsIOutputStream> mOutputStream;
360 :
361 : // Weak reference to the current writter. There can be more then one
362 : // writer at a time and OnHandleClosed() must be processed only for the
363 : // current one.
364 : CacheEntryHandle* mWriter;
365 :
366 : // Background thread scheduled operation. Set (under the lock) one
367 : // of this flags to tell the background thread what to do.
368 : class Ops {
369 : public:
370 : static uint32_t const REGISTER = 1 << 0;
371 : static uint32_t const FRECENCYUPDATE = 1 << 1;
372 : static uint32_t const CALLBACKS = 1 << 2;
373 : static uint32_t const UNREGISTER = 1 << 3;
374 :
375 5 : Ops() : mFlags(0) { }
376 13 : uint32_t Grab() { uint32_t flags = mFlags; mFlags = 0; return flags; }
377 13 : bool Set(uint32_t aFlags) { if (mFlags & aFlags) return false; mFlags |= aFlags; return true; }
378 : private:
379 : uint32_t mFlags;
380 : } mBackgroundOperations;
381 :
382 : nsCOMPtr<nsISupports> mSecurityInfo;
383 : int64_t mPredictedDataSize;
384 : mozilla::TimeStamp mLoadStart;
385 : uint32_t mUseCount;
386 : };
387 :
388 :
389 : class CacheEntryHandle : public nsICacheEntry
390 : {
391 : public:
392 : explicit CacheEntryHandle(CacheEntry* aEntry);
393 13 : CacheEntry* Entry() const { return mEntry; }
394 :
395 : NS_DECL_THREADSAFE_ISUPPORTS
396 76 : NS_FORWARD_NSICACHEENTRY(mEntry->)
397 : private:
398 : virtual ~CacheEntryHandle();
399 : RefPtr<CacheEntry> mEntry;
400 : };
401 :
402 :
403 : class CacheOutputCloseListener final : public Runnable
404 : {
405 : public:
406 : void OnOutputClosed();
407 :
408 : private:
409 : friend class CacheEntry;
410 :
411 : virtual ~CacheOutputCloseListener();
412 :
413 : NS_DECL_NSIRUNNABLE
414 : explicit CacheOutputCloseListener(CacheEntry* aEntry);
415 :
416 : private:
417 : RefPtr<CacheEntry> mEntry;
418 : };
419 :
420 : } // namespace net
421 : } // namespace mozilla
422 :
423 : #endif
|