CacheStorageService.h (15803B)
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 CacheStorageService__h__ 6 #define CacheStorageService__h__ 7 8 #include "mozilla/LinkedList.h" 9 #include "nsICacheStorageService.h" 10 #include "nsIMemoryReporter.h" 11 #include "nsINamed.h" 12 #include "nsITimer.h" 13 #include "nsICacheTesting.h" 14 15 #include "nsClassHashtable.h" 16 #include "nsTHashMap.h" 17 #include "nsString.h" 18 #include "nsThreadUtils.h" 19 #include "nsProxyRelease.h" 20 #include "mozilla/Monitor.h" 21 #include "mozilla/Mutex.h" 22 #include "mozilla/StaticMutex.h" 23 #include "mozilla/AtomicBitfields.h" 24 #include "mozilla/Atomics.h" 25 #include "mozilla/TimeStamp.h" 26 #include "nsTArray.h" 27 28 class nsIURI; 29 class nsICacheEntryDoomCallback; 30 class nsICacheStorageVisitor; 31 class nsIRunnable; 32 class nsIThread; 33 class nsIEventTarget; 34 35 namespace mozilla { 36 37 class OriginAttributes; 38 39 namespace net { 40 41 class CacheStorageService; 42 class CacheStorage; 43 class CacheEntry; 44 class CacheEntryHandle; 45 class CacheEntryTable; 46 47 class CacheMemoryConsumer { 48 private: 49 friend class CacheStorageService; 50 // clang-format off 51 MOZ_ATOMIC_BITFIELDS(mAtomicBitfields, 32, ( 52 (uint32_t, ReportedMemoryConsumption, 30), 53 (uint32_t, Flags, 2) 54 )) 55 // clang-format on 56 57 private: 58 CacheMemoryConsumer() = delete; 59 60 protected: 61 enum { 62 // No special treatment, reports always to the disk-entries pool. 63 NORMAL = 0, 64 // This consumer is belonging to a memory-only cache entry, used to decide 65 // which of the two disk and memory pools count this consumption at. 66 MEMORY_ONLY = 1 << 0, 67 // Prevent reports of this consumer at all, used for disk data chunks since 68 // we throw them away as soon as the entry is not used by any consumer and 69 // don't want to make them wipe the whole pool out during their short life. 70 DONT_REPORT = 1 << 1 71 }; 72 73 explicit CacheMemoryConsumer(uint32_t aFlags); 74 ~CacheMemoryConsumer() { DoMemoryReport(0); } 75 void DoMemoryReport(uint32_t aCurrentSize); 76 }; 77 78 using GlobalEntryTables = nsClassHashtable<nsCStringHashKey, CacheEntryTable>; 79 class WalkMemoryCacheRunnable; 80 81 namespace CacheStorageServiceInternal { 82 class WalkMemoryCacheRunnable; 83 class WalkDiskCacheRunnable; 84 } // namespace CacheStorageServiceInternal 85 86 class CacheStorageService final : public nsICacheStorageService, 87 public nsIMemoryReporter, 88 public nsITimerCallback, 89 public nsICacheTesting, 90 public nsINamed { 91 friend class CacheStorageServiceInternal::WalkMemoryCacheRunnable; 92 friend class CacheStorageServiceInternal::WalkDiskCacheRunnable; 93 94 public: 95 NS_DECL_THREADSAFE_ISUPPORTS 96 NS_DECL_NSICACHESTORAGESERVICE 97 NS_DECL_NSIMEMORYREPORTER 98 NS_DECL_NSITIMERCALLBACK 99 NS_DECL_NSICACHETESTING 100 NS_DECL_NSINAMED 101 102 CacheStorageService(); 103 104 void Shutdown(); 105 void DropPrivateBrowsingEntries(); 106 107 static CacheStorageService* Self() { return sSelf; } 108 static nsISupports* SelfISupports() { 109 return static_cast<nsICacheStorageService*>(Self()); 110 } 111 nsresult Dispatch(nsIRunnable* aEvent); 112 static bool IsRunning() { return sSelf && !sSelf->mShutdown; } 113 static bool IsOnManagementThread(); 114 already_AddRefed<nsIEventTarget> Thread() const; 115 StaticMutex& Lock() { return sLock; } 116 117 // Tracks entries that may be forced valid in a pruned hashtable. 118 struct ForcedValidData { 119 // The timestamp is computed when the entry gets inserted into the map. 120 // It should never be null for an entry in the map. 121 TimeStamp validUntil; 122 // viewed gets set to true by a call to MarkForcedValidEntryUse() 123 bool viewed = false; 124 }; 125 nsTHashMap<nsCStringHashKey, ForcedValidData> mForcedValidEntries; 126 void ForcedValidEntriesPrune(TimeStamp& now); 127 128 // Helper thread-safe interface to pass entry info, only difference from 129 // nsICacheStorageVisitor is that instead of nsIURI only the uri spec is 130 // passed. 131 class EntryInfoCallback { 132 public: 133 virtual void OnEntryInfo(const nsACString& aURISpec, 134 const nsACString& aIdEnhance, int64_t aDataSize, 135 int64_t aAltDataSize, uint32_t aFetchCount, 136 uint32_t aLastModifiedTime, 137 uint32_t aExpirationTime, bool aPinned, 138 nsILoadContextInfo* aInfo) = 0; 139 }; 140 141 // Invokes OnEntryInfo for the given aEntry, synchronously. 142 static void GetCacheEntryInfo(CacheEntry* aEntry, 143 EntryInfoCallback* aCallback); 144 145 nsresult GetCacheIndexEntryAttrs(CacheStorage const* aStorage, 146 const nsACString& aURI, 147 const nsACString& aIdExtension, 148 bool* aHasAltData, uint32_t* aFileSizeKb); 149 150 static uint32_t CacheQueueSize(bool highPriority); 151 152 // Memory reporting 153 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 154 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 155 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) 156 157 private: 158 virtual ~CacheStorageService(); 159 void ShutdownBackground(); 160 161 /** 162 * Keeps tables of entries. There is one entries table for each distinct load 163 * context type. The distinction is based on following load context info 164 * states: <isPrivate|isAnon|inIsolatedMozBrowser> which builds a mapping 165 * key. 166 * 167 * Thread-safe to access, protected by the service mutex. 168 */ 169 static GlobalEntryTables* sGlobalEntryTables MOZ_GUARDED_BY(sLock); 170 171 private: 172 // The following methods may only be called on the management 173 // thread. 174 friend class CacheEntry; 175 176 /** 177 * Registers the entry into the associated MemoryPool. 178 * Holds a strong reference until it is unregistered. 179 */ 180 void RegisterEntry(CacheEntry* aEntry); 181 182 /** 183 * Deregisters the entry from the associated MemoryPool. 184 */ 185 void UnregisterEntry(CacheEntry* aEntry); 186 187 /** 188 * Removes the entry from the related entry hash table, if still present. 189 */ 190 bool RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced = false); 191 192 /** 193 * Tells the storage service whether this entry is only to be stored in 194 * memory. 195 */ 196 void RecordMemoryOnlyEntry(CacheEntry* aEntry, bool aOnlyInMemory, 197 bool aOverwrite); 198 199 /** 200 * Sets a cache entry valid (overrides the default loading behavior by loading 201 * directly from cache) for the given number of seconds 202 * See nsICacheEntry.idl for more details 203 */ 204 void ForceEntryValidFor(nsACString const& aContextKey, 205 nsACString const& aEntryKey, 206 uint32_t aSecondsToTheFuture); 207 208 /** 209 * Remove the validity info 210 */ 211 void RemoveEntryForceValid(nsACString const& aContextKey, 212 nsACString const& aEntryKey); 213 214 /** 215 * Retrieves the status of the cache entry to see if it has been forced valid 216 * (so it will loaded directly from cache without further validation) 217 */ 218 bool IsForcedValidEntry(nsACString const& aContextKey, 219 nsACString const& aEntryKey); 220 221 // Marks the entry as used, so we may properly report when it gets evicted 222 // if the prefetched resource was used or not. 223 void MarkForcedValidEntryUse(nsACString const& aContextKey, 224 nsACString const& aEntryKey); 225 226 private: 227 friend class CacheIndex; 228 229 /** 230 * CacheIndex uses this to prevent a cache entry from being prememptively 231 * thrown away when forced valid 232 * See nsICacheEntry.idl for more details 233 */ 234 bool IsForcedValidEntry(nsACString const& aContextEntryKey); 235 236 private: 237 // These are helpers for telemetry monitoring of the memory pools. 238 void TelemetryPrune(TimeStamp& now); 239 void TelemetryRecordEntryCreation(CacheEntry const* entry); 240 void TelemetryRecordEntryRemoval(CacheEntry* entry); 241 242 private: 243 // Following methods are thread safe to call. 244 friend class CacheStorage; 245 246 /** 247 * Get, or create when not existing and demanded, an entry for the storage 248 * and uri+id extension. 249 */ 250 nsresult AddStorageEntry(CacheStorage const* aStorage, const nsACString& aURI, 251 const nsACString& aIdExtension, uint32_t aFlags, 252 CacheEntryHandle** aResult); 253 254 /** 255 * Check existance of an entry. This may throw NS_ERROR_NOT_AVAILABLE 256 * when the information cannot be obtained synchronously w/o blocking. 257 */ 258 nsresult CheckStorageEntry(CacheStorage const* aStorage, 259 const nsACString& aURI, 260 const nsACString& aIdExtension, bool* aResult); 261 262 /** 263 * Removes the entry from the related entry hash table, if still present 264 * and returns it. 265 */ 266 nsresult DoomStorageEntry(CacheStorage const* aStorage, 267 const nsACString& aURI, 268 const nsACString& aIdExtension, 269 nsICacheEntryDoomCallback* aCallback); 270 271 /** 272 * Removes and returns entry table for the storage. 273 */ 274 nsresult DoomStorageEntries(CacheStorage const* aStorage, 275 nsICacheEntryDoomCallback* aCallback); 276 277 /** 278 * Walk all entiries beloging to the storage. 279 */ 280 nsresult WalkStorageEntries(CacheStorage const* aStorage, bool aVisitEntries, 281 nsICacheStorageVisitor* aVisitor); 282 283 private: 284 friend class CacheFileIOManager; 285 286 /** 287 * CacheFileIOManager uses this method to notify CacheStorageService that 288 * an active entry was removed. This method is called even if the entry 289 * removal was originated by CacheStorageService. This also removes the entry 290 * from the DictionaryCache. 291 */ 292 void CacheFileDoomed(const nsACString& aKey, 293 nsILoadContextInfo* aLoadContextInfo, 294 const nsACString& aIdExtension, 295 const nsACString& aURISpec); 296 297 /** 298 * Tries to find an existing entry in the hashtables and synchronously call 299 * OnCacheEntryInfo of the aVisitor callback when found. 300 * @retuns 301 * true, when the entry has been found that also implies the callbacks has 302 * beem invoked 303 * false, when an entry has not been found 304 */ 305 bool GetCacheEntryInfo(nsILoadContextInfo* aLoadContextInfo, 306 const nsACString& aIdExtension, 307 const nsACString& aURISpec, 308 EntryInfoCallback* aCallback); 309 310 private: 311 friend class CacheMemoryConsumer; 312 313 /** 314 * When memory consumption of this entry radically changes, this method 315 * is called to reflect the size of allocated memory. This call may purge 316 * unspecified number of entries from memory (but not from disk). 317 */ 318 void OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer, 319 uint32_t aCurrentMemoryConsumption); 320 321 /** 322 * If not already pending, it schedules mPurgeTimer that fires after 1 second 323 * and dispatches PurgeOverMemoryLimit(). 324 */ 325 void SchedulePurgeOverMemoryLimit(); 326 327 /** 328 * Called on the management thread, removes all expired and then least used 329 * entries from the memory, first from the disk pool and then from the memory 330 * pool. 331 */ 332 void PurgeExpiredOrOverMemoryLimit(); 333 334 private: 335 nsresult DoomStorageEntries(const nsACString& aContextKey, 336 nsILoadContextInfo* aContext, bool aDiskStorage, 337 bool aPin, nsICacheEntryDoomCallback* aCallback); 338 nsresult AddStorageEntry(const nsACString& aContextKey, 339 const nsACString& aURI, 340 const nsACString& aIdExtension, bool aWriteToDisk, 341 bool aSkipSizeCheck, bool aPin, uint32_t aFlags, 342 CacheEntryHandle** aResult); 343 344 nsresult ClearOriginInternal( 345 const nsAString& aOrigin, 346 const mozilla::OriginAttributes& aOriginAttributes, bool aAnonymous); 347 348 static CacheStorageService* sSelf; 349 350 static StaticMutex sLock; 351 mozilla::Mutex mForcedValidEntriesLock{ 352 "CacheStorageService.mForcedValidEntriesLock"}; 353 354 Atomic<bool, Relaxed> mShutdown{false}; 355 356 // Accessible only on the service thread 357 class MemoryPool { 358 public: 359 enum EType { 360 DISK, 361 MEMORY, 362 } mType; 363 364 explicit MemoryPool(EType aType); 365 ~MemoryPool(); 366 367 // We want to have constant O(1) for removal from this list. 368 LinkedList<RefPtr<CacheEntry>> mManagedEntries; 369 Atomic<uint32_t, Relaxed> mMemorySize{0}; 370 371 bool OnMemoryConsumptionChange(uint32_t aSavedMemorySize, 372 uint32_t aCurrentMemoryConsumption); 373 /** 374 * Purges entries from memory based on the frecency ordered array. 375 */ 376 void PurgeExpiredOrOverMemoryLimit(); 377 size_t PurgeExpired(size_t minprogress); 378 Result<size_t, nsresult> PurgeByFrecency(size_t minprogress); 379 size_t PurgeAll(uint32_t aWhat, size_t minprogress); 380 381 private: 382 uint32_t Limit() const; 383 MemoryPool() = delete; 384 }; 385 386 MemoryPool mDiskPool{MemoryPool::DISK}; 387 MemoryPool mMemoryPool{MemoryPool::MEMORY}; 388 TimeStamp mLastPurgeTime; 389 MemoryPool& Pool(bool aUsingDisk) { 390 return aUsingDisk ? mDiskPool : mMemoryPool; 391 } 392 MemoryPool const& Pool(bool aUsingDisk) const { 393 return aUsingDisk ? mDiskPool : mMemoryPool; 394 } 395 396 nsCOMPtr<nsITimer> mPurgeTimer; 397 #ifdef MOZ_TSAN 398 // In OnMemoryConsumptionChange() we check whether the timer exists, but we 399 // cannot grab the lock there (see comment 6 in bug 1614637) and TSan reports 400 // a data race. This data race is harmless, so we use this atomic flag only in 401 // TSan build to suppress it. 402 Atomic<bool, Relaxed> mPurgeTimerActive{false}; 403 #endif 404 405 class PurgeFromMemoryRunnable : public Runnable { 406 public: 407 PurgeFromMemoryRunnable(CacheStorageService* aService, uint32_t aWhat) 408 : Runnable("net::CacheStorageService::PurgeFromMemoryRunnable"), 409 mService(aService), 410 mWhat(aWhat) {} 411 412 private: 413 virtual ~PurgeFromMemoryRunnable() = default; 414 415 NS_IMETHOD Run() override; 416 417 RefPtr<CacheStorageService> mService; 418 uint32_t mWhat; 419 }; 420 421 // Used just for telemetry purposes, accessed only on the management thread. 422 // Note: not included in the memory reporter, this is not expected to be huge 423 // and also would be complicated to report since reporting happens on the main 424 // thread but this table is manipulated on the management thread. 425 nsTHashMap<nsCStringHashKey, mozilla::TimeStamp> mPurgeTimeStamps; 426 427 // nsICacheTesting 428 class IOThreadSuspender : public Runnable { 429 public: 430 IOThreadSuspender() 431 : Runnable("net::CacheStorageService::IOThreadSuspender"), 432 mMon("IOThreadSuspender") {} 433 void Notify(); 434 435 private: 436 virtual ~IOThreadSuspender() = default; 437 NS_IMETHOD Run() override; 438 439 Monitor mMon MOZ_UNANNOTATED; 440 bool mSignaled{false}; 441 }; 442 443 RefPtr<IOThreadSuspender> mActiveIOSuspender; 444 }; 445 446 template <class T> 447 void ProxyRelease(const char* aName, nsCOMPtr<T>& object, 448 nsIEventTarget* target) { 449 NS_ProxyRelease(aName, target, object.forget()); 450 } 451 452 template <class T> 453 void ProxyReleaseMainThread(const char* aName, nsCOMPtr<T>& object) { 454 ProxyRelease(aName, object, GetMainThreadSerialEventTarget()); 455 } 456 457 } // namespace net 458 } // namespace mozilla 459 460 #define NS_CACHE_STORAGE_SERVICE_CID \ 461 {0xea70b098, 0x5014, 0x4e21, {0xae, 0xe1, 0x75, 0xe6, 0xb2, 0xc4, 0xb8, 0xe0}} 462 463 #define NS_CACHE_STORAGE_SERVICE_CONTRACTID \ 464 "@mozilla.org/netwerk/cache-storage-service;1" 465 466 #define NS_CACHE_STORAGE_SERVICE_CONTRACTID2 \ 467 "@mozilla.org/network/cache-storage-service;1" 468 469 #endif