Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #ifndef CacheEntry__h__ |
michael@0 | 6 | #define CacheEntry__h__ |
michael@0 | 7 | |
michael@0 | 8 | #include "nsICacheEntry.h" |
michael@0 | 9 | #include "CacheFile.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "nsIRunnable.h" |
michael@0 | 12 | #include "nsIOutputStream.h" |
michael@0 | 13 | #include "nsICacheEntryOpenCallback.h" |
michael@0 | 14 | #include "nsICacheEntryDoomCallback.h" |
michael@0 | 15 | |
michael@0 | 16 | #include "nsCOMPtr.h" |
michael@0 | 17 | #include "nsRefPtrHashtable.h" |
michael@0 | 18 | #include "nsDataHashtable.h" |
michael@0 | 19 | #include "nsHashKeys.h" |
michael@0 | 20 | #include "nsString.h" |
michael@0 | 21 | #include "nsCOMArray.h" |
michael@0 | 22 | #include "nsThreadUtils.h" |
michael@0 | 23 | #include "mozilla/Mutex.h" |
michael@0 | 24 | #include "mozilla/TimeStamp.h" |
michael@0 | 25 | |
michael@0 | 26 | static inline uint32_t |
michael@0 | 27 | PRTimeToSeconds(PRTime t_usec) |
michael@0 | 28 | { |
michael@0 | 29 | PRTime usec_per_sec = PR_USEC_PER_SEC; |
michael@0 | 30 | return uint32_t(t_usec /= usec_per_sec); |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | #define NowInSeconds() PRTimeToSeconds(PR_Now()) |
michael@0 | 34 | |
michael@0 | 35 | class nsIStorageStream; |
michael@0 | 36 | class nsIOutputStream; |
michael@0 | 37 | class nsIURI; |
michael@0 | 38 | class nsIThread; |
michael@0 | 39 | |
michael@0 | 40 | namespace mozilla { |
michael@0 | 41 | namespace net { |
michael@0 | 42 | |
michael@0 | 43 | class CacheStorageService; |
michael@0 | 44 | class CacheStorage; |
michael@0 | 45 | class CacheFileOutputStream; |
michael@0 | 46 | class CacheOutputCloseListener; |
michael@0 | 47 | class CacheEntryHandle; |
michael@0 | 48 | |
michael@0 | 49 | class CacheEntry : public nsICacheEntry |
michael@0 | 50 | , public nsIRunnable |
michael@0 | 51 | , public CacheFileListener |
michael@0 | 52 | { |
michael@0 | 53 | public: |
michael@0 | 54 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 55 | NS_DECL_NSICACHEENTRY |
michael@0 | 56 | NS_DECL_NSIRUNNABLE |
michael@0 | 57 | |
michael@0 | 58 | CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID, |
michael@0 | 59 | bool aUseDisk); |
michael@0 | 60 | |
michael@0 | 61 | void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags); |
michael@0 | 62 | |
michael@0 | 63 | CacheEntryHandle* NewHandle(); |
michael@0 | 64 | |
michael@0 | 65 | public: |
michael@0 | 66 | uint32_t GetMetadataMemoryConsumption(); |
michael@0 | 67 | nsCString const &GetStorageID() const { return mStorageID; } |
michael@0 | 68 | nsCString const &GetEnhanceID() const { return mEnhanceID; } |
michael@0 | 69 | nsIURI* GetURI() const { return mURI; } |
michael@0 | 70 | // Accessible only under the CacheStorageService lock (asserts it) |
michael@0 | 71 | bool IsUsingDiskLocked() const; |
michael@0 | 72 | // Accessible at any time |
michael@0 | 73 | bool IsUsingDisk() const { return mUseDisk; } |
michael@0 | 74 | bool SetUsingDisk(bool aUsingDisk); |
michael@0 | 75 | bool IsReferenced() const; |
michael@0 | 76 | bool IsFileDoomed(); |
michael@0 | 77 | |
michael@0 | 78 | // Methods for entry management (eviction from memory), |
michael@0 | 79 | // called only on the management thread. |
michael@0 | 80 | |
michael@0 | 81 | // TODO make these inline |
michael@0 | 82 | double GetFrecency() const; |
michael@0 | 83 | uint32_t GetExpirationTime() const; |
michael@0 | 84 | |
michael@0 | 85 | bool IsRegistered() const; |
michael@0 | 86 | bool CanRegister() const; |
michael@0 | 87 | void SetRegistered(bool aRegistered); |
michael@0 | 88 | |
michael@0 | 89 | enum EPurge { |
michael@0 | 90 | PURGE_DATA_ONLY_DISK_BACKED, |
michael@0 | 91 | PURGE_WHOLE_ONLY_DISK_BACKED, |
michael@0 | 92 | PURGE_WHOLE, |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | bool Purge(uint32_t aWhat); |
michael@0 | 96 | void PurgeAndDoom(); |
michael@0 | 97 | void DoomAlreadyRemoved(); |
michael@0 | 98 | |
michael@0 | 99 | nsresult HashingKeyWithStorage(nsACString &aResult); |
michael@0 | 100 | nsresult HashingKey(nsACString &aResult); |
michael@0 | 101 | |
michael@0 | 102 | static nsresult HashingKey(nsCSubstring const& aStorageID, |
michael@0 | 103 | nsCSubstring const& aEnhanceID, |
michael@0 | 104 | nsIURI* aURI, |
michael@0 | 105 | nsACString &aResult); |
michael@0 | 106 | |
michael@0 | 107 | static nsresult HashingKey(nsCSubstring const& aStorageID, |
michael@0 | 108 | nsCSubstring const& aEnhanceID, |
michael@0 | 109 | nsCSubstring const& aURISpec, |
michael@0 | 110 | nsACString &aResult); |
michael@0 | 111 | |
michael@0 | 112 | // Accessed only on the service management thread |
michael@0 | 113 | double mFrecency; |
michael@0 | 114 | uint32_t mSortingExpirationTime; |
michael@0 | 115 | |
michael@0 | 116 | // Memory reporting |
michael@0 | 117 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
michael@0 | 118 | size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
michael@0 | 119 | |
michael@0 | 120 | private: |
michael@0 | 121 | virtual ~CacheEntry(); |
michael@0 | 122 | |
michael@0 | 123 | // CacheFileListener |
michael@0 | 124 | NS_IMETHOD OnFileReady(nsresult aResult, bool aIsNew); |
michael@0 | 125 | NS_IMETHOD OnFileDoomed(nsresult aResult); |
michael@0 | 126 | |
michael@0 | 127 | // Keep the service alive during life-time of an entry |
michael@0 | 128 | nsRefPtr<CacheStorageService> mService; |
michael@0 | 129 | |
michael@0 | 130 | // We must monitor when a cache entry whose consumer is responsible |
michael@0 | 131 | // for writing it the first time gets released. We must then invoke |
michael@0 | 132 | // waiting callbacks to not break the chain. |
michael@0 | 133 | class Callback |
michael@0 | 134 | { |
michael@0 | 135 | public: |
michael@0 | 136 | Callback(CacheEntry* aEntry, |
michael@0 | 137 | nsICacheEntryOpenCallback *aCallback, |
michael@0 | 138 | bool aReadOnly, bool aCheckOnAnyThread); |
michael@0 | 139 | Callback(Callback const &aThat); |
michael@0 | 140 | ~Callback(); |
michael@0 | 141 | |
michael@0 | 142 | // Called when this callback record changes it's owning entry, |
michael@0 | 143 | // mainly during recreation. |
michael@0 | 144 | void ExchangeEntry(CacheEntry* aEntry); |
michael@0 | 145 | |
michael@0 | 146 | // We are raising reference count here to take into account the pending |
michael@0 | 147 | // callback (that virtually holds a ref to this entry before it gets |
michael@0 | 148 | // it's pointer). |
michael@0 | 149 | nsRefPtr<CacheEntry> mEntry; |
michael@0 | 150 | nsCOMPtr<nsICacheEntryOpenCallback> mCallback; |
michael@0 | 151 | nsCOMPtr<nsIThread> mTargetThread; |
michael@0 | 152 | bool mReadOnly : 1; |
michael@0 | 153 | bool mCheckOnAnyThread : 1; |
michael@0 | 154 | bool mRecheckAfterWrite : 1; |
michael@0 | 155 | bool mNotWanted : 1; |
michael@0 | 156 | |
michael@0 | 157 | nsresult OnCheckThread(bool *aOnCheckThread) const; |
michael@0 | 158 | nsresult OnAvailThread(bool *aOnAvailThread) const; |
michael@0 | 159 | }; |
michael@0 | 160 | |
michael@0 | 161 | // Since OnCacheEntryAvailable must be invoked on the main thread |
michael@0 | 162 | // we need a runnable for it... |
michael@0 | 163 | class AvailableCallbackRunnable : public nsRunnable |
michael@0 | 164 | { |
michael@0 | 165 | public: |
michael@0 | 166 | AvailableCallbackRunnable(CacheEntry* aEntry, |
michael@0 | 167 | Callback const &aCallback) |
michael@0 | 168 | : mEntry(aEntry) |
michael@0 | 169 | , mCallback(aCallback) |
michael@0 | 170 | {} |
michael@0 | 171 | |
michael@0 | 172 | private: |
michael@0 | 173 | NS_IMETHOD Run() |
michael@0 | 174 | { |
michael@0 | 175 | mEntry->InvokeAvailableCallback(mCallback); |
michael@0 | 176 | return NS_OK; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | nsRefPtr<CacheEntry> mEntry; |
michael@0 | 180 | Callback mCallback; |
michael@0 | 181 | }; |
michael@0 | 182 | |
michael@0 | 183 | // Since OnCacheEntryDoomed must be invoked on the main thread |
michael@0 | 184 | // we need a runnable for it... |
michael@0 | 185 | class DoomCallbackRunnable : public nsRunnable |
michael@0 | 186 | { |
michael@0 | 187 | public: |
michael@0 | 188 | DoomCallbackRunnable(CacheEntry* aEntry, nsresult aRv) |
michael@0 | 189 | : mEntry(aEntry), mRv(aRv) {} |
michael@0 | 190 | |
michael@0 | 191 | private: |
michael@0 | 192 | NS_IMETHOD Run() |
michael@0 | 193 | { |
michael@0 | 194 | nsCOMPtr<nsICacheEntryDoomCallback> callback; |
michael@0 | 195 | { |
michael@0 | 196 | mozilla::MutexAutoLock lock(mEntry->mLock); |
michael@0 | 197 | mEntry->mDoomCallback.swap(callback); |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | if (callback) |
michael@0 | 201 | callback->OnCacheEntryDoomed(mRv); |
michael@0 | 202 | return NS_OK; |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | nsRefPtr<CacheEntry> mEntry; |
michael@0 | 206 | nsresult mRv; |
michael@0 | 207 | }; |
michael@0 | 208 | |
michael@0 | 209 | // Loads from disk asynchronously |
michael@0 | 210 | bool Load(bool aTruncate, bool aPriority); |
michael@0 | 211 | void OnLoaded(); |
michael@0 | 212 | |
michael@0 | 213 | void RememberCallback(Callback const & aCallback); |
michael@0 | 214 | void InvokeCallbacksLock(); |
michael@0 | 215 | void InvokeCallbacks(); |
michael@0 | 216 | bool InvokeCallbacks(bool aReadOnly); |
michael@0 | 217 | bool InvokeCallback(Callback & aCallback); |
michael@0 | 218 | void InvokeAvailableCallback(Callback const & aCallback); |
michael@0 | 219 | |
michael@0 | 220 | nsresult OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval); |
michael@0 | 221 | |
michael@0 | 222 | // When this entry is new and recreated w/o a callback, we need to wrap it |
michael@0 | 223 | // with a handle to detect writing consumer is gone. |
michael@0 | 224 | CacheEntryHandle* NewWriteHandle(); |
michael@0 | 225 | void OnHandleClosed(CacheEntryHandle const* aHandle); |
michael@0 | 226 | |
michael@0 | 227 | private: |
michael@0 | 228 | friend class CacheEntryHandle; |
michael@0 | 229 | // Increment/decrements the number of handles keeping this entry. |
michael@0 | 230 | void AddHandleRef() { ++mHandlesCount; } |
michael@0 | 231 | void ReleaseHandleRef() { --mHandlesCount; } |
michael@0 | 232 | // Current number of handles keeping this entry. |
michael@0 | 233 | uint32_t HandlesCount() const { return mHandlesCount; } |
michael@0 | 234 | |
michael@0 | 235 | private: |
michael@0 | 236 | friend class CacheOutputCloseListener; |
michael@0 | 237 | void OnOutputClosed(); |
michael@0 | 238 | |
michael@0 | 239 | private: |
michael@0 | 240 | // Schedules a background operation on the management thread. |
michael@0 | 241 | // When executed on the management thread directly, the operation(s) |
michael@0 | 242 | // is (are) executed immediately. |
michael@0 | 243 | void BackgroundOp(uint32_t aOperation, bool aForceAsync = false); |
michael@0 | 244 | void StoreFrecency(); |
michael@0 | 245 | |
michael@0 | 246 | // Called only from DoomAlreadyRemoved() |
michael@0 | 247 | void DoomFile(); |
michael@0 | 248 | |
michael@0 | 249 | already_AddRefed<CacheEntryHandle> ReopenTruncated(bool aMemoryOnly, |
michael@0 | 250 | nsICacheEntryOpenCallback* aCallback); |
michael@0 | 251 | void TransferCallbacks(CacheEntry & aFromEntry); |
michael@0 | 252 | |
michael@0 | 253 | mozilla::Mutex mLock; |
michael@0 | 254 | |
michael@0 | 255 | // Reflects the number of existing handles for this entry |
michael@0 | 256 | ::mozilla::ThreadSafeAutoRefCnt mHandlesCount; |
michael@0 | 257 | |
michael@0 | 258 | nsTArray<Callback> mCallbacks; |
michael@0 | 259 | nsCOMPtr<nsICacheEntryDoomCallback> mDoomCallback; |
michael@0 | 260 | |
michael@0 | 261 | nsRefPtr<CacheFile> mFile; |
michael@0 | 262 | nsresult mFileStatus; |
michael@0 | 263 | nsCOMPtr<nsIURI> mURI; |
michael@0 | 264 | nsCString mEnhanceID; |
michael@0 | 265 | nsCString mStorageID; |
michael@0 | 266 | |
michael@0 | 267 | // Whether it's allowed to persist the data to disk |
michael@0 | 268 | // Synchronized by the service management lock. |
michael@0 | 269 | // Hence, leave it as a standalone boolean. |
michael@0 | 270 | bool mUseDisk; |
michael@0 | 271 | |
michael@0 | 272 | // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved(). |
michael@0 | 273 | // Left as a standalone flag to not bother with locking (there is no need). |
michael@0 | 274 | bool mIsDoomed; |
michael@0 | 275 | |
michael@0 | 276 | // Following flags are all synchronized with the cache entry lock. |
michael@0 | 277 | |
michael@0 | 278 | // Whether security info has already been looked up in metadata. |
michael@0 | 279 | bool mSecurityInfoLoaded : 1; |
michael@0 | 280 | // Prevents any callback invocation |
michael@0 | 281 | bool mPreventCallbacks : 1; |
michael@0 | 282 | // true: after load and an existing file, or after output stream has been opened. |
michael@0 | 283 | // note - when opening an input stream, and this flag is false, output stream |
michael@0 | 284 | // is open along ; this makes input streams on new entries behave correctly |
michael@0 | 285 | // when EOF is reached (WOULD_BLOCK is returned). |
michael@0 | 286 | // false: after load and a new file, or dropped to back to false when a writer |
michael@0 | 287 | // fails to open an output stream. |
michael@0 | 288 | bool mHasData : 1; |
michael@0 | 289 | |
michael@0 | 290 | #ifdef PR_LOG |
michael@0 | 291 | static char const * StateString(uint32_t aState); |
michael@0 | 292 | #endif |
michael@0 | 293 | |
michael@0 | 294 | enum EState { // transiting to: |
michael@0 | 295 | NOTLOADED = 0, // -> LOADING | EMPTY |
michael@0 | 296 | LOADING = 1, // -> EMPTY | READY |
michael@0 | 297 | EMPTY = 2, // -> WRITING |
michael@0 | 298 | WRITING = 3, // -> EMPTY | READY |
michael@0 | 299 | READY = 4, // -> REVALIDATING |
michael@0 | 300 | REVALIDATING = 5 // -> READY |
michael@0 | 301 | }; |
michael@0 | 302 | |
michael@0 | 303 | // State of this entry. |
michael@0 | 304 | EState mState; |
michael@0 | 305 | |
michael@0 | 306 | enum ERegistration { |
michael@0 | 307 | NEVERREGISTERED = 0, // The entry has never been registered |
michael@0 | 308 | REGISTERED = 1, // The entry is stored in the memory pool index |
michael@0 | 309 | DEREGISTERED = 2 // The entry has been removed from the pool |
michael@0 | 310 | }; |
michael@0 | 311 | |
michael@0 | 312 | // Accessed only on the management thread. Records the state of registration |
michael@0 | 313 | // this entry in the memory pool intermediate cache. |
michael@0 | 314 | ERegistration mRegistration; |
michael@0 | 315 | |
michael@0 | 316 | // If a new (empty) entry is requested to open an input stream before |
michael@0 | 317 | // output stream has been opened, we must open output stream internally |
michael@0 | 318 | // on CacheFile and hold until writer releases the entry or opens the output |
michael@0 | 319 | // stream for read (then we trade him mOutputStream). |
michael@0 | 320 | nsCOMPtr<nsIOutputStream> mOutputStream; |
michael@0 | 321 | |
michael@0 | 322 | // Weak reference to the current writter. There can be more then one |
michael@0 | 323 | // writer at a time and OnHandleClosed() must be processed only for the |
michael@0 | 324 | // current one. |
michael@0 | 325 | CacheEntryHandle* mWriter; |
michael@0 | 326 | |
michael@0 | 327 | // Background thread scheduled operation. Set (under the lock) one |
michael@0 | 328 | // of this flags to tell the background thread what to do. |
michael@0 | 329 | class Ops { |
michael@0 | 330 | public: |
michael@0 | 331 | static uint32_t const REGISTER = 1 << 0; |
michael@0 | 332 | static uint32_t const FRECENCYUPDATE = 1 << 1; |
michael@0 | 333 | static uint32_t const CALLBACKS = 1 << 2; |
michael@0 | 334 | static uint32_t const UNREGISTER = 1 << 3; |
michael@0 | 335 | |
michael@0 | 336 | Ops() : mFlags(0) { } |
michael@0 | 337 | uint32_t Grab() { uint32_t flags = mFlags; mFlags = 0; return flags; } |
michael@0 | 338 | bool Set(uint32_t aFlags) { if (mFlags & aFlags) return false; mFlags |= aFlags; return true; } |
michael@0 | 339 | private: |
michael@0 | 340 | uint32_t mFlags; |
michael@0 | 341 | } mBackgroundOperations; |
michael@0 | 342 | |
michael@0 | 343 | nsCOMPtr<nsISupports> mSecurityInfo; |
michael@0 | 344 | int64_t mPredictedDataSize; |
michael@0 | 345 | mozilla::TimeStamp mLoadStart; |
michael@0 | 346 | nsCOMPtr<nsIThread> mReleaseThread; |
michael@0 | 347 | }; |
michael@0 | 348 | |
michael@0 | 349 | |
michael@0 | 350 | class CacheEntryHandle : public nsICacheEntry |
michael@0 | 351 | { |
michael@0 | 352 | public: |
michael@0 | 353 | CacheEntryHandle(CacheEntry* aEntry); |
michael@0 | 354 | virtual ~CacheEntryHandle(); |
michael@0 | 355 | CacheEntry* Entry() const { return mEntry; } |
michael@0 | 356 | |
michael@0 | 357 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 358 | NS_FORWARD_NSICACHEENTRY(mEntry->) |
michael@0 | 359 | private: |
michael@0 | 360 | nsRefPtr<CacheEntry> mEntry; |
michael@0 | 361 | }; |
michael@0 | 362 | |
michael@0 | 363 | |
michael@0 | 364 | class CacheOutputCloseListener : public nsRunnable |
michael@0 | 365 | { |
michael@0 | 366 | public: |
michael@0 | 367 | void OnOutputClosed(); |
michael@0 | 368 | virtual ~CacheOutputCloseListener(); |
michael@0 | 369 | |
michael@0 | 370 | private: |
michael@0 | 371 | friend class CacheEntry; |
michael@0 | 372 | |
michael@0 | 373 | NS_DECL_NSIRUNNABLE |
michael@0 | 374 | CacheOutputCloseListener(CacheEntry* aEntry); |
michael@0 | 375 | |
michael@0 | 376 | private: |
michael@0 | 377 | nsRefPtr<CacheEntry> mEntry; |
michael@0 | 378 | }; |
michael@0 | 379 | |
michael@0 | 380 | } // net |
michael@0 | 381 | } // mozilla |
michael@0 | 382 | |
michael@0 | 383 | #endif |