michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=80: */ 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 _nsCacheService_h_ michael@0: #define _nsCacheService_h_ michael@0: michael@0: #include "nsICacheService.h" michael@0: #include "nsCacheSession.h" michael@0: #include "nsCacheDevice.h" michael@0: #include "nsCacheEntry.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsICacheListener.h" michael@0: #include "nsIMemoryReporter.h" michael@0: michael@0: #include "prthread.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsString.h" michael@0: #include "nsTArray.h" michael@0: #include "nsRefPtrHashtable.h" michael@0: #include "mozilla/CondVar.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: class nsCacheRequest; michael@0: class nsCacheProfilePrefObserver; michael@0: class nsDiskCacheDevice; michael@0: class nsMemoryCacheDevice; michael@0: class nsOfflineCacheDevice; michael@0: class nsCacheServiceAutoLock; michael@0: class nsITimer; michael@0: class mozIStorageService; michael@0: michael@0: michael@0: /****************************************************************************** michael@0: * nsNotifyDoomListener michael@0: *****************************************************************************/ michael@0: michael@0: class nsNotifyDoomListener : public nsRunnable { michael@0: public: michael@0: nsNotifyDoomListener(nsICacheListener *listener, michael@0: nsresult status) michael@0: : mListener(listener) // transfers reference michael@0: , mStatus(status) michael@0: {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mListener->OnCacheEntryDoomed(mStatus); michael@0: NS_RELEASE(mListener); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsICacheListener *mListener; michael@0: nsresult mStatus; michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * nsCacheService michael@0: ******************************************************************************/ michael@0: michael@0: class nsCacheService : public nsICacheServiceInternal, michael@0: public nsIMemoryReporter michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICACHESERVICE michael@0: NS_DECL_NSICACHESERVICEINTERNAL michael@0: NS_DECL_NSIMEMORYREPORTER michael@0: michael@0: nsCacheService(); michael@0: virtual ~nsCacheService(); michael@0: michael@0: // Define a Create method to be used with a factory: michael@0: static nsresult michael@0: Create(nsISupports* outer, const nsIID& iid, void* *result); michael@0: michael@0: michael@0: /** michael@0: * Methods called by nsCacheSession michael@0: */ michael@0: static nsresult OpenCacheEntry(nsCacheSession * session, michael@0: const nsACString & key, michael@0: nsCacheAccessMode accessRequested, michael@0: bool blockingMode, michael@0: nsICacheListener * listener, michael@0: nsICacheEntryDescriptor ** result); michael@0: michael@0: static nsresult EvictEntriesForSession(nsCacheSession * session); michael@0: michael@0: static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy, michael@0: bool * result); michael@0: michael@0: static nsresult DoomEntry(nsCacheSession *session, michael@0: const nsACString &key, michael@0: nsICacheListener *listener); michael@0: michael@0: /** michael@0: * Methods called by nsCacheEntryDescriptor michael@0: */ michael@0: michael@0: static void CloseDescriptor(nsCacheEntryDescriptor * descriptor); michael@0: michael@0: static nsresult GetFileForEntry(nsCacheEntry * entry, michael@0: nsIFile ** result); michael@0: michael@0: static nsresult OpenInputStreamForEntry(nsCacheEntry * entry, michael@0: nsCacheAccessMode mode, michael@0: uint32_t offset, michael@0: nsIInputStream ** result); michael@0: michael@0: static nsresult OpenOutputStreamForEntry(nsCacheEntry * entry, michael@0: nsCacheAccessMode mode, michael@0: uint32_t offset, michael@0: nsIOutputStream ** result); michael@0: michael@0: static nsresult OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize); michael@0: michael@0: static nsresult SetCacheElement(nsCacheEntry * entry, nsISupports * element); michael@0: michael@0: static nsresult ValidateEntry(nsCacheEntry * entry); michael@0: michael@0: static int32_t CacheCompressionLevel(); michael@0: michael@0: static bool GetClearingEntries(); michael@0: michael@0: static void GetCacheBaseDirectoty(nsIFile ** result); michael@0: static void GetDiskCacheDirectory(nsIFile ** result); michael@0: static void GetAppCacheDirectory(nsIFile ** result); michael@0: michael@0: /** michael@0: * Methods called by any cache classes michael@0: */ michael@0: michael@0: static michael@0: nsCacheService * GlobalInstance() { return gService; } michael@0: michael@0: static nsresult DoomEntry(nsCacheEntry * entry); michael@0: michael@0: static bool IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy policy); michael@0: michael@0: /** michael@0: * Called by disk cache to notify us to use the new max smart size michael@0: */ michael@0: static void MarkStartingFresh(); michael@0: michael@0: /** michael@0: * Methods called by nsApplicationCacheService michael@0: */ michael@0: michael@0: nsresult GetOfflineDevice(nsOfflineCacheDevice ** aDevice); michael@0: michael@0: /** michael@0: * Creates an offline cache device that works over a specific profile directory. michael@0: * A tool to preload offline cache for profiles different from the current michael@0: * application's profile directory. michael@0: */ michael@0: nsresult GetCustomOfflineDevice(nsIFile *aProfileDir, michael@0: int32_t aQuota, michael@0: nsOfflineCacheDevice **aDevice); michael@0: michael@0: // This method may be called to release an object while the cache service michael@0: // lock is being held. If a non-null target is specified and the target michael@0: // does not correspond to the current thread, then the release will be michael@0: // proxied to the specified target. Otherwise, the object will be added to michael@0: // the list of objects to be released when the cache service is unlocked. michael@0: static void ReleaseObject_Locked(nsISupports * object, michael@0: nsIEventTarget * target = nullptr); michael@0: michael@0: static nsresult DispatchToCacheIOThread(nsIRunnable* event); michael@0: michael@0: // Calling this method will block the calling thread until all pending michael@0: // events on the cache-io thread has finished. The calling thread must michael@0: // hold the cache-lock michael@0: static nsresult SyncWithCacheIOThread(); michael@0: michael@0: michael@0: /** michael@0: * Methods called by nsCacheProfilePrefObserver michael@0: */ michael@0: static void OnProfileShutdown(bool cleanse); michael@0: static void OnProfileChanged(); michael@0: michael@0: static void SetDiskCacheEnabled(bool enabled); michael@0: // Sets the disk cache capacity (in kilobytes) michael@0: static void SetDiskCacheCapacity(int32_t capacity); michael@0: // Set max size for a disk-cache entry (in KB). -1 disables limit up to michael@0: // 1/8th of disk cache size michael@0: static void SetDiskCacheMaxEntrySize(int32_t maxSize); michael@0: // Set max size for a memory-cache entry (in kilobytes). -1 disables michael@0: // limit up to 90% of memory cache size michael@0: static void SetMemoryCacheMaxEntrySize(int32_t maxSize); michael@0: michael@0: static void SetOfflineCacheEnabled(bool enabled); michael@0: // Sets the offline cache capacity (in kilobytes) michael@0: static void SetOfflineCacheCapacity(int32_t capacity); michael@0: michael@0: static void SetMemoryCache(); michael@0: michael@0: static void SetCacheCompressionLevel(int32_t level); michael@0: michael@0: // Starts smart cache size computation if disk device is available michael@0: static nsresult SetDiskSmartSize(); michael@0: michael@0: static void MoveOrRemoveDiskCache(nsIFile *aOldCacheDir, michael@0: nsIFile *aNewCacheDir, michael@0: const char *aCacheSubdir); michael@0: michael@0: nsresult Init(); michael@0: void Shutdown(); michael@0: michael@0: static bool IsInitialized() michael@0: { michael@0: if (!gService) { michael@0: return false; michael@0: } michael@0: return gService->mInitialized; michael@0: } michael@0: michael@0: static void AssertOwnsLock() michael@0: { gService->mLock.AssertCurrentThreadOwns(); } michael@0: michael@0: static void LeavePrivateBrowsing(); michael@0: bool IsDoomListEmpty(); michael@0: michael@0: typedef bool (*DoomCheckFn)(nsCacheEntry* entry); michael@0: michael@0: private: michael@0: friend class nsCacheServiceAutoLock; michael@0: friend class nsOfflineCacheDevice; michael@0: friend class nsProcessRequestEvent; michael@0: friend class nsSetSmartSizeEvent; michael@0: friend class nsBlockOnCacheThreadEvent; michael@0: friend class nsSetDiskSmartSizeCallback; michael@0: friend class nsDoomEvent; michael@0: friend class nsDisableOldMaxSmartSizePrefEvent; michael@0: friend class nsDiskCacheMap; michael@0: friend class nsAsyncDoomEvent; michael@0: friend class nsCacheEntryDescriptor; michael@0: michael@0: /** michael@0: * Internal Methods michael@0: */ michael@0: michael@0: static void Lock(::mozilla::Telemetry::ID mainThreadLockerID); michael@0: static void Unlock(); michael@0: void LockAcquired(); michael@0: void LockReleased(); michael@0: michael@0: nsresult CreateDiskDevice(); michael@0: nsresult CreateOfflineDevice(); michael@0: nsresult CreateCustomOfflineDevice(nsIFile *aProfileDir, michael@0: int32_t aQuota, michael@0: nsOfflineCacheDevice **aDevice); michael@0: nsresult CreateMemoryDevice(); michael@0: michael@0: nsresult RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice); michael@0: michael@0: nsresult CreateRequest(nsCacheSession * session, michael@0: const nsACString & clientKey, michael@0: nsCacheAccessMode accessRequested, michael@0: bool blockingMode, michael@0: nsICacheListener * listener, michael@0: nsCacheRequest ** request); michael@0: michael@0: nsresult DoomEntry_Internal(nsCacheEntry * entry, michael@0: bool doProcessPendingRequests); michael@0: michael@0: nsresult EvictEntriesForClient(const char * clientID, michael@0: nsCacheStoragePolicy storagePolicy); michael@0: michael@0: // Notifies request listener asynchronously on the request's thread, and michael@0: // releases the descriptor on the request's thread. If this method fails, michael@0: // the descriptor is not released. michael@0: nsresult NotifyListener(nsCacheRequest * request, michael@0: nsICacheEntryDescriptor * descriptor, michael@0: nsCacheAccessMode accessGranted, michael@0: nsresult error); michael@0: michael@0: nsresult ActivateEntry(nsCacheRequest * request, michael@0: nsCacheEntry ** entry, michael@0: nsCacheEntry ** doomedEntry); michael@0: michael@0: nsCacheDevice * EnsureEntryHasDevice(nsCacheEntry * entry); michael@0: michael@0: nsCacheEntry * SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision); michael@0: michael@0: void DeactivateEntry(nsCacheEntry * entry); michael@0: michael@0: nsresult ProcessRequest(nsCacheRequest * request, michael@0: bool calledFromOpenCacheEntry, michael@0: nsICacheEntryDescriptor ** result); michael@0: michael@0: nsresult ProcessPendingRequests(nsCacheEntry * entry); michael@0: michael@0: void ClearDoomList(void); michael@0: void DoomActiveEntries(DoomCheckFn check); michael@0: void CloseAllStreams(); michael@0: void FireClearNetworkCacheStoredAnywhereNotification(); michael@0: michael@0: static michael@0: PLDHashOperator GetActiveEntries(PLDHashTable * table, michael@0: PLDHashEntryHdr * hdr, michael@0: uint32_t number, michael@0: void * arg); michael@0: static michael@0: PLDHashOperator RemoveActiveEntry(PLDHashTable * table, michael@0: PLDHashEntryHdr * hdr, michael@0: uint32_t number, michael@0: void * arg); michael@0: michael@0: static michael@0: PLDHashOperator ShutdownCustomCacheDeviceEnum(const nsAString& aProfileDir, michael@0: nsRefPtr& aDevice, michael@0: void* aUserArg); michael@0: #if defined(PR_LOGGING) michael@0: void LogCacheStatistics(); michael@0: #endif michael@0: michael@0: nsresult SetDiskSmartSize_Locked(); michael@0: michael@0: /** michael@0: * Data Members michael@0: */ michael@0: michael@0: static nsCacheService * gService; // there can be only one... michael@0: michael@0: nsCOMPtr mStorageService; michael@0: michael@0: nsCacheProfilePrefObserver * mObserver; michael@0: michael@0: mozilla::Mutex mLock; michael@0: mozilla::CondVar mCondVar; michael@0: michael@0: mozilla::Mutex mTimeStampLock; michael@0: mozilla::TimeStamp mLockAcquiredTimeStamp; michael@0: michael@0: nsCOMPtr mCacheIOThread; michael@0: michael@0: nsTArray mDoomedObjects; michael@0: nsCOMPtr mSmartSizeTimer; michael@0: michael@0: bool mInitialized; michael@0: bool mClearingEntries; michael@0: michael@0: bool mEnableMemoryDevice; michael@0: bool mEnableDiskDevice; michael@0: bool mEnableOfflineDevice; michael@0: michael@0: nsMemoryCacheDevice * mMemoryDevice; michael@0: nsDiskCacheDevice * mDiskDevice; michael@0: nsOfflineCacheDevice * mOfflineDevice; michael@0: michael@0: nsRefPtrHashtable mCustomOfflineDevices; michael@0: michael@0: nsCacheEntryHashTable mActiveEntries; michael@0: PRCList mDoomedEntries; michael@0: michael@0: // stats michael@0: michael@0: uint32_t mTotalEntries; michael@0: uint32_t mCacheHits; michael@0: uint32_t mCacheMisses; michael@0: uint32_t mMaxKeyLength; michael@0: uint32_t mMaxDataSize; michael@0: uint32_t mMaxMetaSize; michael@0: michael@0: // Unexpected error totals michael@0: uint32_t mDeactivateFailures; michael@0: uint32_t mDeactivatedUnboundEntries; michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * nsCacheServiceAutoLock michael@0: ******************************************************************************/ michael@0: michael@0: #define LOCK_TELEM(x) \ michael@0: (::mozilla::Telemetry::CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_##x) michael@0: michael@0: // Instantiate this class to acquire the cache service lock for a particular michael@0: // execution scope. michael@0: class nsCacheServiceAutoLock { michael@0: public: michael@0: nsCacheServiceAutoLock(mozilla::Telemetry::ID mainThreadLockerID) { michael@0: nsCacheService::Lock(mainThreadLockerID); michael@0: } michael@0: ~nsCacheServiceAutoLock() { michael@0: nsCacheService::Unlock(); michael@0: } michael@0: }; michael@0: michael@0: #endif // _nsCacheService_h_