michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef CacheStorageService__h__ michael@0: #define CacheStorageService__h__ michael@0: michael@0: #include "nsICacheStorageService.h" michael@0: #include "nsIMemoryReporter.h" michael@0: michael@0: #include "nsITimer.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "nsTArray.h" michael@0: michael@0: class nsIURI; michael@0: class nsICacheEntryOpenCallback; michael@0: class nsICacheEntryDoomCallback; michael@0: class nsICacheStorageVisitor; michael@0: class nsIRunnable; michael@0: class nsIThread; michael@0: class nsIEventTarget; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: class CacheStorageService; michael@0: class CacheStorage; michael@0: class CacheEntry; michael@0: class CacheEntryHandle; michael@0: class CacheEntryTable; michael@0: michael@0: class CacheMemoryConsumer michael@0: { michael@0: private: michael@0: friend class CacheStorageService; michael@0: uint32_t mReportedMemoryConsumption : 30; michael@0: uint32_t mFlags : 2; michael@0: michael@0: private: michael@0: CacheMemoryConsumer() MOZ_DELETE; michael@0: michael@0: protected: michael@0: enum { michael@0: // No special treatment, reports always to the disk-entries pool. michael@0: NORMAL = 0, michael@0: // This consumer is belonging to a memory-only cache entry, used to decide michael@0: // which of the two disk and memory pools count this consumption at. michael@0: MEMORY_ONLY = 1 << 0, michael@0: // Prevent reports of this consumer at all, used for disk data chunks since michael@0: // we throw them away as soon as the entry is not used by any consumer and michael@0: // don't want to make them wipe the whole pool out during their short life. michael@0: DONT_REPORT = 1 << 1 michael@0: }; michael@0: michael@0: CacheMemoryConsumer(uint32_t aFlags); michael@0: ~CacheMemoryConsumer() { DoMemoryReport(0); } michael@0: void DoMemoryReport(uint32_t aCurrentSize); michael@0: }; michael@0: michael@0: class CacheStorageService : public nsICacheStorageService michael@0: , public nsIMemoryReporter michael@0: , public nsITimerCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICACHESTORAGESERVICE michael@0: NS_DECL_NSIMEMORYREPORTER michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: CacheStorageService(); michael@0: michael@0: void Shutdown(); michael@0: void DropPrivateBrowsingEntries(); michael@0: michael@0: // Wipes out the new or the old cache directory completely. michael@0: static void WipeCacheDirectory(uint32_t aVersion); michael@0: michael@0: static CacheStorageService* Self() { return sSelf; } michael@0: static nsISupports* SelfISupports() { return static_cast(Self()); } michael@0: nsresult Dispatch(nsIRunnable* aEvent); michael@0: static bool IsRunning() { return sSelf && !sSelf->mShutdown; } michael@0: static bool IsOnManagementThread(); michael@0: already_AddRefed Thread() const; michael@0: mozilla::Mutex& Lock() { return mLock; } michael@0: michael@0: // Memory reporting michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) michael@0: michael@0: private: michael@0: virtual ~CacheStorageService(); michael@0: void ShutdownBackground(); michael@0: michael@0: private: michael@0: // The following methods may only be called on the management michael@0: // thread. michael@0: friend class CacheEntry; michael@0: michael@0: /** michael@0: * Registers the entry in management ordered arrays, a mechanism michael@0: * helping with weighted purge of entries. michael@0: * Management arrays keep hard reference to the entry. Entry is michael@0: * responsible to remove it self or the service is responsible to michael@0: * remove the entry when it's no longer needed. michael@0: */ michael@0: void RegisterEntry(CacheEntry* aEntry); michael@0: michael@0: /** michael@0: * Deregisters the entry from management arrays. References are michael@0: * then released. michael@0: */ michael@0: void UnregisterEntry(CacheEntry* aEntry); michael@0: michael@0: /** michael@0: * Removes the entry from the related entry hash table, if still present. michael@0: */ michael@0: bool RemoveEntry(CacheEntry* aEntry, bool aOnlyUnreferenced = false); michael@0: michael@0: /** michael@0: * Tells the storage service whether this entry is only to be stored in michael@0: * memory. michael@0: */ michael@0: void RecordMemoryOnlyEntry(CacheEntry* aEntry, michael@0: bool aOnlyInMemory, michael@0: bool aOverwrite); michael@0: michael@0: private: michael@0: // Following methods are thread safe to call. michael@0: friend class CacheStorage; michael@0: michael@0: /** michael@0: * Get, or create when not existing and demanded, an entry for the storage michael@0: * and uri+id extension. michael@0: */ michael@0: nsresult AddStorageEntry(CacheStorage const* aStorage, michael@0: nsIURI* aURI, michael@0: const nsACString & aIdExtension, michael@0: bool aCreateIfNotExist, michael@0: bool aReplace, michael@0: CacheEntryHandle** aResult); michael@0: michael@0: /** michael@0: * Removes the entry from the related entry hash table, if still present michael@0: * and returns it. michael@0: */ michael@0: nsresult DoomStorageEntry(CacheStorage const* aStorage, michael@0: nsIURI* aURI, michael@0: const nsACString & aIdExtension, michael@0: nsICacheEntryDoomCallback* aCallback); michael@0: michael@0: /** michael@0: * Removes and returns entry table for the storage. michael@0: */ michael@0: nsresult DoomStorageEntries(CacheStorage const* aStorage, michael@0: nsICacheEntryDoomCallback* aCallback); michael@0: michael@0: /** michael@0: * Walk all entiries beloging to the storage. michael@0: */ michael@0: nsresult WalkStorageEntries(CacheStorage const* aStorage, michael@0: bool aVisitEntries, michael@0: nsICacheStorageVisitor* aVisitor); michael@0: michael@0: private: michael@0: friend class CacheFileIOManager; michael@0: michael@0: /** michael@0: * CacheFileIOManager uses this method to notify CacheStorageService that michael@0: * an active entry was removed. This method is called even if the entry michael@0: * removal was originated by CacheStorageService. michael@0: */ michael@0: void CacheFileDoomed(nsILoadContextInfo* aLoadContextInfo, michael@0: const nsACString & aIdExtension, michael@0: const nsACString & aURISpec); michael@0: michael@0: private: michael@0: friend class CacheMemoryConsumer; michael@0: michael@0: /** michael@0: * When memory consumption of this entry radically changes, this method michael@0: * is called to reflect the size of allocated memory. This call may purge michael@0: * unspecified number of entries from memory (but not from disk). michael@0: */ michael@0: void OnMemoryConsumptionChange(CacheMemoryConsumer* aConsumer, michael@0: uint32_t aCurrentMemoryConsumption); michael@0: michael@0: /** michael@0: * If not already pending, it schedules mPurgeTimer that fires after 1 second michael@0: * and dispatches PurgeOverMemoryLimit(). michael@0: */ michael@0: void SchedulePurgeOverMemoryLimit(); michael@0: michael@0: /** michael@0: * Called on the management thread, removes all expired and then least used michael@0: * entries from the memory, first from the disk pool and then from the memory michael@0: * pool. michael@0: */ michael@0: void PurgeOverMemoryLimit(); michael@0: michael@0: private: michael@0: nsresult DoomStorageEntries(nsCSubstring const& aContextKey, michael@0: nsILoadContextInfo* aContext, michael@0: bool aDiskStorage, michael@0: nsICacheEntryDoomCallback* aCallback); michael@0: nsresult AddStorageEntry(nsCSubstring const& aContextKey, michael@0: nsIURI* aURI, michael@0: const nsACString & aIdExtension, michael@0: bool aWriteToDisk, michael@0: bool aCreateIfNotExist, michael@0: bool aReplace, michael@0: CacheEntryHandle** aResult); michael@0: michael@0: static CacheStorageService* sSelf; michael@0: michael@0: mozilla::Mutex mLock; michael@0: michael@0: bool mShutdown; michael@0: michael@0: // Accessible only on the service thread michael@0: class MemoryPool michael@0: { michael@0: public: michael@0: enum EType michael@0: { michael@0: DISK, michael@0: MEMORY, michael@0: } mType; michael@0: michael@0: MemoryPool(EType aType); michael@0: ~MemoryPool(); michael@0: michael@0: nsTArray > mFrecencyArray; michael@0: nsTArray > mExpirationArray; michael@0: mozilla::Atomic mMemorySize; michael@0: michael@0: bool OnMemoryConsumptionChange(uint32_t aSavedMemorySize, michael@0: uint32_t aCurrentMemoryConsumption); michael@0: /** michael@0: * Purges entries from memory based on the frecency ordered array. michael@0: */ michael@0: void PurgeOverMemoryLimit(); michael@0: void PurgeExpired(); michael@0: void PurgeByFrecency(bool &aFrecencyNeedsSort, uint32_t aWhat); michael@0: void PurgeAll(uint32_t aWhat); michael@0: michael@0: private: michael@0: uint32_t const Limit() const; michael@0: MemoryPool() MOZ_DELETE; michael@0: }; michael@0: michael@0: MemoryPool mDiskPool; michael@0: MemoryPool mMemoryPool; michael@0: MemoryPool& Pool(bool aUsingDisk) michael@0: { michael@0: return aUsingDisk ? mDiskPool : mMemoryPool; michael@0: } michael@0: MemoryPool const& Pool(bool aUsingDisk) const michael@0: { michael@0: return aUsingDisk ? mDiskPool : mMemoryPool; michael@0: } michael@0: michael@0: nsCOMPtr mPurgeTimer; michael@0: michael@0: class PurgeFromMemoryRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: PurgeFromMemoryRunnable(CacheStorageService* aService, uint32_t aWhat) michael@0: : mService(aService), mWhat(aWhat) { } michael@0: michael@0: private: michael@0: virtual ~PurgeFromMemoryRunnable() { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: // TODO not all flags apply to both pools michael@0: mService->Pool(true).PurgeAll(mWhat); michael@0: mService->Pool(false).PurgeAll(mWhat); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr mService; michael@0: uint32_t mWhat; michael@0: }; michael@0: }; michael@0: michael@0: template michael@0: void ProxyRelease(nsCOMPtr &object, nsIThread* thread) michael@0: { michael@0: T* release; michael@0: object.forget(&release); michael@0: michael@0: NS_ProxyRelease(thread, release); michael@0: } michael@0: michael@0: template michael@0: void ProxyReleaseMainThread(nsCOMPtr &object) michael@0: { michael@0: nsCOMPtr mainThread = do_GetMainThread(); michael@0: ProxyRelease(object, mainThread); michael@0: } michael@0: michael@0: } // net michael@0: } // mozilla michael@0: michael@0: #define NS_CACHE_STORAGE_SERVICE_CID \ michael@0: { 0xea70b098, 0x5014, 0x4e21, \ michael@0: { 0xae, 0xe1, 0x75, 0xe6, 0xb2, 0xc4, 0xb8, 0xe0 } } \ michael@0: michael@0: #define NS_CACHE_STORAGE_SERVICE_CONTRACTID \ michael@0: "@mozilla.org/netwerk/cache-storage-service;1" michael@0: michael@0: #endif