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 CacheFileIOManager__h__ michael@0: #define CacheFileIOManager__h__ michael@0: michael@0: #include "CacheIOThread.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsITimer.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "mozilla/SHA1.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "nsTArray.h" michael@0: #include "nsString.h" michael@0: #include "nsTHashtable.h" michael@0: #include "prio.h" michael@0: michael@0: //#define DEBUG_HANDLES 1 michael@0: michael@0: class nsIFile; michael@0: class nsITimer; michael@0: class nsIDirectoryEnumerator; michael@0: class nsILoadContextInfo; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: class CacheFile; michael@0: #ifdef DEBUG_HANDLES michael@0: class CacheFileHandlesEntry; michael@0: #endif michael@0: michael@0: const char kEntriesDir[] = "entries"; michael@0: const char kDoomedDir[] = "doomed"; michael@0: const char kTrashDir[] = "trash"; michael@0: michael@0: michael@0: class CacheFileHandle : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: bool DispatchRelease(); michael@0: michael@0: CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority); michael@0: CacheFileHandle(const nsACString &aKey, bool aPriority); michael@0: CacheFileHandle(const CacheFileHandle &aOther); michael@0: void Log(); michael@0: bool IsDoomed() const { return mIsDoomed; } michael@0: const SHA1Sum::Hash *Hash() const { return mHash; } michael@0: int64_t FileSize() const { return mFileSize; } michael@0: uint32_t FileSizeInK() const; michael@0: bool IsPriority() const { return mPriority; } michael@0: bool FileExists() const { return mFileExists; } michael@0: bool IsClosed() const { return mClosed; } michael@0: bool IsSpecialFile() const { return !mHash; } michael@0: nsCString & Key() { return mKey; } 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: michael@0: private: michael@0: friend class CacheFileIOManager; michael@0: friend class CacheFileHandles; michael@0: friend class ReleaseNSPRHandleEvent; michael@0: michael@0: virtual ~CacheFileHandle(); michael@0: michael@0: const SHA1Sum::Hash *mHash; michael@0: bool mIsDoomed; michael@0: bool mPriority; michael@0: bool mClosed; michael@0: bool mInvalid; michael@0: bool mFileExists; // This means that the file should exists, michael@0: // but it can be still deleted by OS/user michael@0: // and then a subsequent OpenNSPRFileDesc() michael@0: // will fail. michael@0: nsCOMPtr mFile; michael@0: int64_t mFileSize; michael@0: PRFileDesc *mFD; // if null then the file doesn't exists on the disk michael@0: nsCString mKey; michael@0: }; michael@0: michael@0: class CacheFileHandles { michael@0: public: michael@0: CacheFileHandles(); michael@0: ~CacheFileHandles(); michael@0: michael@0: nsresult GetHandle(const SHA1Sum::Hash *aHash, bool aReturnDoomed, CacheFileHandle **_retval); michael@0: nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval); michael@0: void RemoveHandle(CacheFileHandle *aHandlle); michael@0: void GetAllHandles(nsTArray > *_retval); michael@0: void GetActiveHandles(nsTArray > *_retval); michael@0: void ClearAll(); michael@0: uint32_t HandleCount(); michael@0: michael@0: #ifdef DEBUG_HANDLES michael@0: void Log(CacheFileHandlesEntry *entry); michael@0: #endif 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: michael@0: class HandleHashKey : public PLDHashEntryHdr michael@0: { michael@0: public: michael@0: typedef const SHA1Sum::Hash& KeyType; michael@0: typedef const SHA1Sum::Hash* KeyTypePointer; michael@0: michael@0: HandleHashKey(KeyTypePointer aKey) michael@0: { michael@0: MOZ_COUNT_CTOR(HandleHashKey); michael@0: mHash = (SHA1Sum::Hash*)new uint8_t[SHA1Sum::HashSize]; michael@0: memcpy(mHash, aKey, sizeof(SHA1Sum::Hash)); michael@0: } michael@0: HandleHashKey(const HandleHashKey& aOther) michael@0: { michael@0: NS_NOTREACHED("HandleHashKey copy constructor is forbidden!"); michael@0: } michael@0: ~HandleHashKey() michael@0: { michael@0: MOZ_COUNT_DTOR(HandleHashKey); michael@0: } michael@0: michael@0: bool KeyEquals(KeyTypePointer aKey) const michael@0: { michael@0: return memcmp(mHash, aKey, sizeof(SHA1Sum::Hash)) == 0; michael@0: } michael@0: static KeyTypePointer KeyToPointer(KeyType aKey) michael@0: { michael@0: return &aKey; michael@0: } michael@0: static PLDHashNumber HashKey(KeyTypePointer aKey) michael@0: { michael@0: return (reinterpret_cast(aKey))[0]; michael@0: } michael@0: michael@0: void AddHandle(CacheFileHandle* aHandle); michael@0: void RemoveHandle(CacheFileHandle* aHandle); michael@0: already_AddRefed GetNewestHandle(); michael@0: void GetHandles(nsTArray > &aResult); michael@0: michael@0: SHA1Sum::Hash *Hash() const { return mHash; } michael@0: bool IsEmpty() const { return mHandles.Length() == 0; } michael@0: michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: #ifdef DEBUG michael@0: void AssertHandlesState(); michael@0: #endif 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: michael@0: private: michael@0: nsAutoArrayPtr mHash; michael@0: // Use weak pointers since the hash table access is on a single thread michael@0: // only and CacheFileHandle removes itself from this table in its dtor michael@0: // that may only be called on the same thread as we work with the hashtable michael@0: // since we dispatch its Release() to this thread. michael@0: nsTArray mHandles; michael@0: }; michael@0: michael@0: private: michael@0: nsTHashtable mTable; michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class OpenFileEvent; michael@0: class CloseFileEvent; michael@0: class ReadEvent; michael@0: class WriteEvent; michael@0: class MetadataWriteScheduleEvent; michael@0: class CacheFileContextEvictor; michael@0: michael@0: #define CACHEFILEIOLISTENER_IID \ michael@0: { /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */ \ michael@0: 0xdcaf2ddc, \ michael@0: 0x17cf, \ michael@0: 0x4242, \ michael@0: {0xbc, 0xa1, 0x8c, 0x86, 0x93, 0x63, 0x75, 0xa5} \ michael@0: } michael@0: michael@0: class CacheFileIOListener : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEIOLISTENER_IID) michael@0: michael@0: NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) = 0; michael@0: NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, michael@0: nsresult aResult) = 0; michael@0: NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, michael@0: nsresult aResult) = 0; michael@0: NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) = 0; michael@0: NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) = 0; michael@0: NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) = 0; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener, CACHEFILEIOLISTENER_IID) michael@0: michael@0: michael@0: class CacheFileIOManager : public nsITimerCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: enum { michael@0: OPEN = 0U, michael@0: CREATE = 1U, michael@0: CREATE_NEW = 2U, michael@0: PRIORITY = 4U, michael@0: SPECIAL_FILE = 8U michael@0: }; michael@0: michael@0: CacheFileIOManager(); michael@0: michael@0: static nsresult Init(); michael@0: static nsresult Shutdown(); michael@0: static nsresult OnProfile(); michael@0: static already_AddRefed IOTarget(); michael@0: static already_AddRefed IOThread(); michael@0: static bool IsOnIOThread(); michael@0: static bool IsOnIOThreadOrCeased(); michael@0: static bool IsShutdown(); michael@0: michael@0: // Make aFile's WriteMetadataIfNeeded be called automatically after michael@0: // a short interval. michael@0: static nsresult ScheduleMetadataWrite(CacheFile * aFile); michael@0: // Remove aFile from the scheduling registry array. michael@0: // WriteMetadataIfNeeded will not be automatically called. michael@0: static nsresult UnscheduleMetadataWrite(CacheFile * aFile); michael@0: // Shuts the scheduling off and flushes all pending metadata writes. michael@0: static nsresult ShutdownMetadataWriteScheduling(); michael@0: michael@0: static nsresult OpenFile(const nsACString &aKey, michael@0: uint32_t aFlags, bool aResultOnAnyThread, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult Read(CacheFileHandle *aHandle, int64_t aOffset, michael@0: char *aBuf, int32_t aCount, bool aResultOnAnyThread, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult Write(CacheFileHandle *aHandle, int64_t aOffset, michael@0: const char *aBuf, int32_t aCount, bool aValidate, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult DoomFile(CacheFileHandle *aHandle, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult DoomFileByKey(const nsACString &aKey, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult ReleaseNSPRHandle(CacheFileHandle *aHandle); michael@0: static nsresult TruncateSeekSetEOF(CacheFileHandle *aHandle, michael@0: int64_t aTruncatePos, int64_t aEOFPos, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult RenameFile(CacheFileHandle *aHandle, michael@0: const nsACString &aNewName, michael@0: CacheFileIOListener *aCallback); michael@0: static nsresult EvictIfOverLimit(); michael@0: static nsresult EvictAll(); michael@0: static nsresult EvictByContext(nsILoadContextInfo *aLoadContextInfo); michael@0: michael@0: static nsresult InitIndexEntry(CacheFileHandle *aHandle, michael@0: uint32_t aAppId, michael@0: bool aAnonymous, michael@0: bool aInBrowser); michael@0: static nsresult UpdateIndexEntry(CacheFileHandle *aHandle, michael@0: const uint32_t *aFrecency, michael@0: const uint32_t *aExpirationTime); michael@0: michael@0: static nsresult UpdateIndexEntry(); michael@0: michael@0: enum EEnumerateMode { michael@0: ENTRIES, michael@0: DOOMED michael@0: }; michael@0: michael@0: static void GetCacheDirectory(nsIFile** result); michael@0: michael@0: // Memory reporting michael@0: static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); michael@0: static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); michael@0: michael@0: private: michael@0: friend class CacheFileHandle; michael@0: friend class CacheFileChunk; michael@0: friend class CacheFile; michael@0: friend class ShutdownEvent; michael@0: friend class OpenFileEvent; michael@0: friend class CloseHandleEvent; michael@0: friend class ReadEvent; michael@0: friend class WriteEvent; michael@0: friend class DoomFileEvent; michael@0: friend class DoomFileByKeyEvent; michael@0: friend class ReleaseNSPRHandleEvent; michael@0: friend class TruncateSeekSetEOFEvent; michael@0: friend class RenameFileEvent; michael@0: friend class CacheIndex; michael@0: friend class MetadataWriteScheduleEvent; michael@0: friend class CacheFileContextEvictor; michael@0: michael@0: virtual ~CacheFileIOManager(); michael@0: michael@0: nsresult InitInternal(); michael@0: nsresult ShutdownInternal(); michael@0: michael@0: nsresult OpenFileInternal(const SHA1Sum::Hash *aHash, michael@0: const nsACString &aKey, michael@0: uint32_t aFlags, michael@0: CacheFileHandle **_retval); michael@0: nsresult OpenSpecialFileInternal(const nsACString &aKey, michael@0: uint32_t aFlags, michael@0: CacheFileHandle **_retval); michael@0: nsresult CloseHandleInternal(CacheFileHandle *aHandle); michael@0: nsresult ReadInternal(CacheFileHandle *aHandle, int64_t aOffset, michael@0: char *aBuf, int32_t aCount); michael@0: nsresult WriteInternal(CacheFileHandle *aHandle, int64_t aOffset, michael@0: const char *aBuf, int32_t aCount, bool aValidate); michael@0: nsresult DoomFileInternal(CacheFileHandle *aHandle); michael@0: nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash); michael@0: nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle); michael@0: nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle, michael@0: int64_t aTruncatePos, int64_t aEOFPos); michael@0: nsresult RenameFileInternal(CacheFileHandle *aHandle, michael@0: const nsACString &aNewName); michael@0: nsresult EvictIfOverLimitInternal(); michael@0: nsresult OverLimitEvictionInternal(); michael@0: nsresult EvictAllInternal(); michael@0: nsresult EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo); michael@0: michael@0: nsresult TrashDirectory(nsIFile *aFile); michael@0: static void OnTrashTimer(nsITimer *aTimer, void *aClosure); michael@0: nsresult StartRemovingTrash(); michael@0: nsresult RemoveTrashInternal(); michael@0: nsresult FindTrashDirToRemove(); michael@0: michael@0: nsresult CreateFile(CacheFileHandle *aHandle); michael@0: static void HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval); michael@0: static nsresult StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval); michael@0: nsresult GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval); michael@0: nsresult GetSpecialFile(const nsACString &aKey, nsIFile **_retval); michael@0: nsresult GetDoomedFile(nsIFile **_retval); michael@0: nsresult IsEmptyDirectory(nsIFile *aFile, bool *_retval); michael@0: nsresult CheckAndCreateDir(nsIFile *aFile, const char *aDir, michael@0: bool aEnsureEmptyDir); michael@0: nsresult CreateCacheTree(); michael@0: nsresult OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate = false); michael@0: void NSPRHandleUsed(CacheFileHandle *aHandle); michael@0: michael@0: // Removing all cache files during shutdown michael@0: nsresult SyncRemoveDir(nsIFile *aFile, const char *aDir); michael@0: void SyncRemoveAllCacheFiles(); michael@0: michael@0: nsresult ScheduleMetadataWriteInternal(CacheFile * aFile); michael@0: nsresult UnscheduleMetadataWriteInternal(CacheFile * aFile); michael@0: nsresult ShutdownMetadataWriteSchedulingInternal(); michael@0: michael@0: static nsresult CacheIndexStateChanged(); michael@0: nsresult CacheIndexStateChangedInternal(); michael@0: michael@0: // Smart size calculation. UpdateSmartCacheSize() must be called on IO thread. michael@0: // It is called in EvictIfOverLimitInternal() just before we decide whether to michael@0: // start overlimit eviction or not and also in OverLimitEvictionInternal() michael@0: // before we start an eviction loop. michael@0: nsresult UpdateSmartCacheSize(); michael@0: michael@0: // Memory reporting (private part) michael@0: size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: michael@0: static CacheFileIOManager *gInstance; michael@0: TimeStamp mStartTime; michael@0: bool mShuttingDown; michael@0: nsRefPtr mIOThread; michael@0: nsCOMPtr mCacheDirectory; michael@0: bool mTreeCreated; michael@0: CacheFileHandles mHandles; michael@0: nsTArray mHandlesByLastUsed; michael@0: nsTArray mSpecialHandles; michael@0: nsTArray > mScheduledMetadataWrites; michael@0: nsCOMPtr mMetadataWritesTimer; michael@0: bool mOverLimitEvicting; michael@0: bool mRemovingTrashDirs; michael@0: nsCOMPtr mTrashTimer; michael@0: nsCOMPtr mTrashDir; michael@0: nsCOMPtr mTrashDirEnumerator; michael@0: nsTArray mFailedTrashDirs; michael@0: nsRefPtr mContextEvictor; michael@0: TimeStamp mLastSmartSizeTime; michael@0: }; michael@0: michael@0: } // net michael@0: } // mozilla michael@0: michael@0: #endif