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/. */
5 #ifndef CacheFileIOManager__h__
6 #define CacheFileIOManager__h__
8 #include "CacheIOThread.h"
9 #include "CacheStorageService.h"
10 #include "nsIEventTarget.h"
13 #include "mozilla/SHA1.h"
14 #include "mozilla/TimeStamp.h"
17 #include "nsTHashtable.h"
20 //#define DEBUG_HANDLES 1
24 class nsIDirectoryEnumerator
;
25 class nsILoadContextInfo
;
26 class nsICacheStorageVisitor
;
33 class CacheFileHandlesEntry
;
36 const char kEntriesDir
[] = "entries";
37 const char kDoomedDir
[] = "doomed";
38 const char kTrashDir
[] = "trash";
41 class CacheFileHandle
: public nsISupports
44 NS_DECL_THREADSAFE_ISUPPORTS
45 bool DispatchRelease();
47 CacheFileHandle(const SHA1Sum::Hash
*aHash
, bool aPriority
);
48 CacheFileHandle(const nsACString
&aKey
, bool aPriority
);
49 CacheFileHandle(const CacheFileHandle
&aOther
);
51 bool IsDoomed() const { return mIsDoomed
; }
52 const SHA1Sum::Hash
*Hash() const { return mHash
; }
53 int64_t FileSize() const { return mFileSize
; }
54 uint32_t FileSizeInK() const;
55 bool IsPriority() const { return mPriority
; }
56 bool FileExists() const { return mFileExists
; }
57 bool IsClosed() const { return mClosed
; }
58 bool IsSpecialFile() const { return mSpecialFile
; }
59 nsCString
& Key() { return mKey
; }
62 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
63 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
66 friend class CacheFileIOManager
;
67 friend class CacheFileHandles
;
68 friend class ReleaseNSPRHandleEvent
;
70 virtual ~CacheFileHandle();
72 const SHA1Sum::Hash
*mHash
;
78 bool mFileExists
; // This means that the file should exists,
79 // but it can be still deleted by OS/user
80 // and then a subsequent OpenNSPRFileDesc()
82 nsCOMPtr
<nsIFile
> mFile
;
84 PRFileDesc
*mFD
; // if null then the file doesn't exists on the disk
88 class CacheFileHandles
{
93 nsresult
GetHandle(const SHA1Sum::Hash
*aHash
, bool aReturnDoomed
, CacheFileHandle
**_retval
);
94 nsresult
NewHandle(const SHA1Sum::Hash
*aHash
, bool aPriority
, CacheFileHandle
**_retval
);
95 void RemoveHandle(CacheFileHandle
*aHandlle
);
96 void GetAllHandles(nsTArray
<nsRefPtr
<CacheFileHandle
> > *_retval
);
97 void GetActiveHandles(nsTArray
<nsRefPtr
<CacheFileHandle
> > *_retval
);
99 uint32_t HandleCount();
102 void Log(CacheFileHandlesEntry
*entry
);
106 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
107 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
109 class HandleHashKey
: public PLDHashEntryHdr
112 typedef const SHA1Sum::Hash
& KeyType
;
113 typedef const SHA1Sum::Hash
* KeyTypePointer
;
115 explicit HandleHashKey(KeyTypePointer aKey
)
117 MOZ_COUNT_CTOR(HandleHashKey
);
118 mHash
= (SHA1Sum::Hash
*)new uint8_t[SHA1Sum::kHashSize
];
119 memcpy(mHash
, aKey
, sizeof(SHA1Sum::Hash
));
121 HandleHashKey(const HandleHashKey
& aOther
)
123 NS_NOTREACHED("HandleHashKey copy constructor is forbidden!");
127 MOZ_COUNT_DTOR(HandleHashKey
);
130 bool KeyEquals(KeyTypePointer aKey
) const
132 return memcmp(mHash
, aKey
, sizeof(SHA1Sum::Hash
)) == 0;
134 static KeyTypePointer
KeyToPointer(KeyType aKey
)
138 static PLDHashNumber
HashKey(KeyTypePointer aKey
)
140 return (reinterpret_cast<const uint32_t *>(aKey
))[0];
143 void AddHandle(CacheFileHandle
* aHandle
);
144 void RemoveHandle(CacheFileHandle
* aHandle
);
145 already_AddRefed
<CacheFileHandle
> GetNewestHandle();
146 void GetHandles(nsTArray
<nsRefPtr
<CacheFileHandle
> > &aResult
);
148 SHA1Sum::Hash
*Hash() const { return mHash
; }
149 bool IsEmpty() const { return mHandles
.Length() == 0; }
151 enum { ALLOW_MEMMOVE
= true };
154 void AssertHandlesState();
158 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
159 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const;
162 nsAutoArrayPtr
<SHA1Sum::Hash
> mHash
;
163 // Use weak pointers since the hash table access is on a single thread
164 // only and CacheFileHandle removes itself from this table in its dtor
165 // that may only be called on the same thread as we work with the hashtable
166 // since we dispatch its Release() to this thread.
167 nsTArray
<CacheFileHandle
*> mHandles
;
171 nsTHashtable
<HandleHashKey
> mTable
;
174 ////////////////////////////////////////////////////////////////////////////////
177 class CloseFileEvent
;
180 class MetadataWriteScheduleEvent
;
181 class CacheFileContextEvictor
;
183 #define CACHEFILEIOLISTENER_IID \
184 { /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */ \
188 {0xbc, 0xa1, 0x8c, 0x86, 0x93, 0x63, 0x75, 0xa5} \
191 class CacheFileIOListener
: public nsISupports
194 NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEIOLISTENER_IID
)
196 NS_IMETHOD
OnFileOpened(CacheFileHandle
*aHandle
, nsresult aResult
) = 0;
197 NS_IMETHOD
OnDataWritten(CacheFileHandle
*aHandle
, const char *aBuf
,
198 nsresult aResult
) = 0;
199 NS_IMETHOD
OnDataRead(CacheFileHandle
*aHandle
, char *aBuf
,
200 nsresult aResult
) = 0;
201 NS_IMETHOD
OnFileDoomed(CacheFileHandle
*aHandle
, nsresult aResult
) = 0;
202 NS_IMETHOD
OnEOFSet(CacheFileHandle
*aHandle
, nsresult aResult
) = 0;
203 NS_IMETHOD
OnFileRenamed(CacheFileHandle
*aHandle
, nsresult aResult
) = 0;
206 NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener
, CACHEFILEIOLISTENER_IID
)
209 class CacheFileIOManager
: public nsITimerCallback
212 NS_DECL_THREADSAFE_ISUPPORTS
213 NS_DECL_NSITIMERCALLBACK
223 CacheFileIOManager();
225 static nsresult
Init();
226 static nsresult
Shutdown();
227 static nsresult
OnProfile();
228 static already_AddRefed
<nsIEventTarget
> IOTarget();
229 static already_AddRefed
<CacheIOThread
> IOThread();
230 static bool IsOnIOThread();
231 static bool IsOnIOThreadOrCeased();
232 static bool IsShutdown();
234 // Make aFile's WriteMetadataIfNeeded be called automatically after
236 static nsresult
ScheduleMetadataWrite(CacheFile
* aFile
);
237 // Remove aFile from the scheduling registry array.
238 // WriteMetadataIfNeeded will not be automatically called.
239 static nsresult
UnscheduleMetadataWrite(CacheFile
* aFile
);
240 // Shuts the scheduling off and flushes all pending metadata writes.
241 static nsresult
ShutdownMetadataWriteScheduling();
243 static nsresult
OpenFile(const nsACString
&aKey
,
244 uint32_t aFlags
, CacheFileIOListener
*aCallback
);
245 static nsresult
Read(CacheFileHandle
*aHandle
, int64_t aOffset
,
246 char *aBuf
, int32_t aCount
,
247 CacheFileIOListener
*aCallback
);
248 static nsresult
Write(CacheFileHandle
*aHandle
, int64_t aOffset
,
249 const char *aBuf
, int32_t aCount
, bool aValidate
,
250 CacheFileIOListener
*aCallback
);
251 static nsresult
DoomFile(CacheFileHandle
*aHandle
,
252 CacheFileIOListener
*aCallback
);
253 static nsresult
DoomFileByKey(const nsACString
&aKey
,
254 CacheFileIOListener
*aCallback
);
255 static nsresult
ReleaseNSPRHandle(CacheFileHandle
*aHandle
);
256 static nsresult
TruncateSeekSetEOF(CacheFileHandle
*aHandle
,
257 int64_t aTruncatePos
, int64_t aEOFPos
,
258 CacheFileIOListener
*aCallback
);
259 static nsresult
RenameFile(CacheFileHandle
*aHandle
,
260 const nsACString
&aNewName
,
261 CacheFileIOListener
*aCallback
);
262 static nsresult
EvictIfOverLimit();
263 static nsresult
EvictAll();
264 static nsresult
EvictByContext(nsILoadContextInfo
*aLoadContextInfo
);
266 static nsresult
InitIndexEntry(CacheFileHandle
*aHandle
,
270 static nsresult
UpdateIndexEntry(CacheFileHandle
*aHandle
,
271 const uint32_t *aFrecency
,
272 const uint32_t *aExpirationTime
);
274 static nsresult
UpdateIndexEntry();
276 enum EEnumerateMode
{
281 static void GetCacheDirectory(nsIFile
** result
);
282 #if defined(MOZ_WIDGET_ANDROID)
283 static void GetProfilelessCacheDirectory(nsIFile
** result
);
286 // Calls synchronously OnEntryInfo for an entry with the given hash.
287 // Tries to find an existing entry in the service hashtables first, if not
288 // found, loads synchronously from disk file.
289 // Callable on the IO thread only.
290 static nsresult
GetEntryInfo(const SHA1Sum::Hash
*aHash
,
291 CacheStorageService::EntryInfoCallback
*aCallback
);
294 static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
);
295 static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
);
298 friend class CacheFileHandle
;
299 friend class CacheFileChunk
;
300 friend class CacheFile
;
301 friend class ShutdownEvent
;
302 friend class OpenFileEvent
;
303 friend class CloseHandleEvent
;
304 friend class ReadEvent
;
305 friend class WriteEvent
;
306 friend class DoomFileEvent
;
307 friend class DoomFileByKeyEvent
;
308 friend class ReleaseNSPRHandleEvent
;
309 friend class TruncateSeekSetEOFEvent
;
310 friend class RenameFileEvent
;
311 friend class CacheIndex
;
312 friend class MetadataWriteScheduleEvent
;
313 friend class CacheFileContextEvictor
;
315 virtual ~CacheFileIOManager();
317 nsresult
InitInternal();
318 nsresult
ShutdownInternal();
320 nsresult
OpenFileInternal(const SHA1Sum::Hash
*aHash
,
321 const nsACString
&aKey
,
323 CacheFileHandle
**_retval
);
324 nsresult
OpenSpecialFileInternal(const nsACString
&aKey
,
326 CacheFileHandle
**_retval
);
327 nsresult
CloseHandleInternal(CacheFileHandle
*aHandle
);
328 nsresult
ReadInternal(CacheFileHandle
*aHandle
, int64_t aOffset
,
329 char *aBuf
, int32_t aCount
);
330 nsresult
WriteInternal(CacheFileHandle
*aHandle
, int64_t aOffset
,
331 const char *aBuf
, int32_t aCount
, bool aValidate
);
332 nsresult
DoomFileInternal(CacheFileHandle
*aHandle
);
333 nsresult
DoomFileByKeyInternal(const SHA1Sum::Hash
*aHash
,
334 bool aFailIfAlreadyDoomed
);
335 nsresult
ReleaseNSPRHandleInternal(CacheFileHandle
*aHandle
);
336 nsresult
TruncateSeekSetEOFInternal(CacheFileHandle
*aHandle
,
337 int64_t aTruncatePos
, int64_t aEOFPos
);
338 nsresult
RenameFileInternal(CacheFileHandle
*aHandle
,
339 const nsACString
&aNewName
);
340 nsresult
EvictIfOverLimitInternal();
341 nsresult
OverLimitEvictionInternal();
342 nsresult
EvictAllInternal();
343 nsresult
EvictByContextInternal(nsILoadContextInfo
*aLoadContextInfo
);
345 nsresult
TrashDirectory(nsIFile
*aFile
);
346 static void OnTrashTimer(nsITimer
*aTimer
, void *aClosure
);
347 nsresult
StartRemovingTrash();
348 nsresult
RemoveTrashInternal();
349 nsresult
FindTrashDirToRemove();
351 nsresult
CreateFile(CacheFileHandle
*aHandle
);
352 static void HashToStr(const SHA1Sum::Hash
*aHash
, nsACString
&_retval
);
353 static nsresult
StrToHash(const nsACString
&aHash
, SHA1Sum::Hash
*_retval
);
354 nsresult
GetFile(const SHA1Sum::Hash
*aHash
, nsIFile
**_retval
);
355 nsresult
GetSpecialFile(const nsACString
&aKey
, nsIFile
**_retval
);
356 nsresult
GetDoomedFile(nsIFile
**_retval
);
357 nsresult
IsEmptyDirectory(nsIFile
*aFile
, bool *_retval
);
358 nsresult
CheckAndCreateDir(nsIFile
*aFile
, const char *aDir
,
359 bool aEnsureEmptyDir
);
360 nsresult
CreateCacheTree();
361 nsresult
OpenNSPRHandle(CacheFileHandle
*aHandle
, bool aCreate
= false);
362 void NSPRHandleUsed(CacheFileHandle
*aHandle
);
364 // Removing all cache files during shutdown
365 nsresult
SyncRemoveDir(nsIFile
*aFile
, const char *aDir
);
366 void SyncRemoveAllCacheFiles();
368 nsresult
ScheduleMetadataWriteInternal(CacheFile
* aFile
);
369 nsresult
UnscheduleMetadataWriteInternal(CacheFile
* aFile
);
370 nsresult
ShutdownMetadataWriteSchedulingInternal();
372 static nsresult
CacheIndexStateChanged();
373 nsresult
CacheIndexStateChangedInternal();
375 // Smart size calculation. UpdateSmartCacheSize() must be called on IO thread.
376 // It is called in EvictIfOverLimitInternal() just before we decide whether to
377 // start overlimit eviction or not and also in OverLimitEvictionInternal()
378 // before we start an eviction loop.
379 nsresult
UpdateSmartCacheSize(int64_t aFreeSpace
);
381 // Memory reporting (private part)
382 size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf
) const;
384 static CacheFileIOManager
*gInstance
;
385 TimeStamp mStartTime
;
387 nsRefPtr
<CacheIOThread
> mIOThread
;
388 nsCOMPtr
<nsIFile
> mCacheDirectory
;
389 #if defined(MOZ_WIDGET_ANDROID)
390 // On Android we add the active profile directory name between the path
391 // and the 'cache2' leaf name. However, to delete any leftover data from
392 // times before we were doing it, we still need to access the directory
393 // w/o the profile name in the path. Here it is stored.
394 nsCOMPtr
<nsIFile
> mCacheProfilelessDirectory
;
397 CacheFileHandles mHandles
;
398 nsTArray
<CacheFileHandle
*> mHandlesByLastUsed
;
399 nsTArray
<CacheFileHandle
*> mSpecialHandles
;
400 nsTArray
<nsRefPtr
<CacheFile
> > mScheduledMetadataWrites
;
401 nsCOMPtr
<nsITimer
> mMetadataWritesTimer
;
402 bool mOverLimitEvicting
;
403 bool mRemovingTrashDirs
;
404 nsCOMPtr
<nsITimer
> mTrashTimer
;
405 nsCOMPtr
<nsIFile
> mTrashDir
;
406 nsCOMPtr
<nsIDirectoryEnumerator
> mTrashDirEnumerator
;
407 nsTArray
<nsCString
> mFailedTrashDirs
;
408 nsRefPtr
<CacheFileContextEvictor
> mContextEvictor
;
409 TimeStamp mLastSmartSizeTime
;