Source code

Revision control

Other Tools

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 CacheFileIOManager__h__
6
#define CacheFileIOManager__h__
7
8
#include "CacheIOThread.h"
9
#include "CacheStorageService.h"
10
#include "CacheHashUtils.h"
11
#include "nsIEventTarget.h"
12
#include "nsINamed.h"
13
#include "nsITimer.h"
14
#include "nsCOMPtr.h"
15
#include "mozilla/Atomics.h"
16
#include "mozilla/SHA1.h"
17
#include "mozilla/StaticPtr.h"
18
#include "mozilla/TimeStamp.h"
19
#include "nsTArray.h"
20
#include "nsString.h"
21
#include "nsTHashtable.h"
22
#include "prio.h"
23
24
//#define DEBUG_HANDLES 1
25
26
class nsIFile;
27
class nsITimer;
28
class nsIDirectoryEnumerator;
29
class nsILoadContextInfo;
30
31
namespace mozilla {
32
namespace net {
33
34
class CacheFile;
35
class CacheFileIOListener;
36
37
#ifdef DEBUG_HANDLES
38
class CacheFileHandlesEntry;
39
#endif
40
41
#define ENTRIES_DIR "entries"
42
#define DOOMED_DIR "doomed"
43
#define TRASH_DIR "trash"
44
45
class CacheFileHandle final : public nsISupports {
46
public:
47
enum class PinningStatus : uint32_t { UNKNOWN, NON_PINNED, PINNED };
48
49
NS_DECL_THREADSAFE_ISUPPORTS
50
bool DispatchRelease();
51
52
CacheFileHandle(const SHA1Sum::Hash* aHash, bool aPriority,
53
PinningStatus aPinning);
54
CacheFileHandle(const nsACString& aKey, bool aPriority,
55
PinningStatus aPinning);
56
void Log();
57
bool IsDoomed() const { return mIsDoomed; }
58
const SHA1Sum::Hash* Hash() const { return mHash; }
59
int64_t FileSize() const { return mFileSize; }
60
uint32_t FileSizeInK() const;
61
bool IsPriority() const { return mPriority; }
62
bool FileExists() const { return mFileExists; }
63
bool IsClosed() const { return mClosed; }
64
bool IsSpecialFile() const { return mSpecialFile; }
65
nsCString& Key() { return mKey; }
66
67
// Returns false when this handle has been doomed based on the pinning state
68
// update.
69
bool SetPinned(bool aPinned);
70
void SetInvalid() { mInvalid = true; }
71
72
// Memory reporting
73
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
74
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
75
76
private:
77
friend class CacheFileIOManager;
78
friend class CacheFileHandles;
79
friend class ReleaseNSPRHandleEvent;
80
81
virtual ~CacheFileHandle();
82
83
const SHA1Sum::Hash* mHash;
84
mozilla::Atomic<bool, ReleaseAcquire> mIsDoomed;
85
mozilla::Atomic<bool, ReleaseAcquire> mClosed;
86
87
// mPriority and mSpecialFile are plain "bool", not "bool:1", so as to
88
// avoid bitfield races with the byte containing mInvalid et al. See
89
// bug 1278502.
90
bool const mPriority;
91
bool const mSpecialFile;
92
93
mozilla::Atomic<bool, Relaxed> mInvalid;
94
95
// These bit flags are all accessed only on the IO thread
96
bool mFileExists : 1; // This means that the file should exists,
97
// but it can be still deleted by OS/user
98
// and then a subsequent OpenNSPRFileDesc()
99
// will fail.
100
101
// Both initially false. Can be raised to true only when this handle is to be
102
// doomed during the period when the pinning status is unknown. After the
103
// pinning status determination we check these flags and possibly doom. These
104
// flags are only accessed on the IO thread.
105
bool mDoomWhenFoundPinned : 1;
106
bool mDoomWhenFoundNonPinned : 1;
107
// Set when after shutdown AND:
108
// - when writing: writing data (not metadata) OR the physical file handle is
109
// not currently open
110
// - when truncating: the physical file handle is not currently open
111
// When set it prevents any further writes or truncates on such handles to
112
// happen immediately after shutdown and gives a chance to write metadata of
113
// already open files quickly as possible (only that renders them actually
114
// usable by the cache.)
115
bool mKilled : 1;
116
// For existing files this is always pre-set to UNKNOWN. The status is
117
// udpated accordingly after the matadata has been parsed. For new files the
118
// flag is set according to which storage kind is opening the cache entry and
119
// remains so for the handle's lifetime. The status can only change from
120
// UNKNOWN (if set so initially) to one of PINNED or NON_PINNED and it stays
121
// unchanged afterwards. This status is only accessed on the IO thread.
122
PinningStatus mPinning;
123
124
nsCOMPtr<nsIFile> mFile;
125
int64_t mFileSize;
126
PRFileDesc* mFD; // if null then the file doesn't exists on the disk
127
nsCString mKey;
128
};
129
130
class CacheFileHandles {
131
public:
132
CacheFileHandles();
133
~CacheFileHandles();
134
135
nsresult GetHandle(const SHA1Sum::Hash* aHash, CacheFileHandle** _retval);
136
nsresult NewHandle(const SHA1Sum::Hash* aHash, bool aPriority,
137
CacheFileHandle::PinningStatus aPinning,
138
CacheFileHandle** _retval);
139
void RemoveHandle(CacheFileHandle* aHandlle);
140
void GetAllHandles(nsTArray<RefPtr<CacheFileHandle> >* _retval);
141
void GetActiveHandles(nsTArray<RefPtr<CacheFileHandle> >* _retval);
142
void ClearAll();
143
uint32_t HandleCount();
144
145
#ifdef DEBUG_HANDLES
146
void Log(CacheFileHandlesEntry* entry);
147
#endif
148
149
// Memory reporting
150
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
151
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
152
153
class HandleHashKey : public PLDHashEntryHdr {
154
public:
155
typedef const SHA1Sum::Hash& KeyType;
156
typedef const SHA1Sum::Hash* KeyTypePointer;
157
158
explicit HandleHashKey(KeyTypePointer aKey) {
159
MOZ_COUNT_CTOR(HandleHashKey);
160
mHash = MakeUnique<uint8_t[]>(SHA1Sum::kHashSize);
161
memcpy(mHash.get(), aKey, sizeof(SHA1Sum::Hash));
162
}
163
HandleHashKey(const HandleHashKey& aOther) {
164
MOZ_ASSERT_UNREACHABLE("HandleHashKey copy constructor is forbidden!");
165
}
166
~HandleHashKey() { MOZ_COUNT_DTOR(HandleHashKey); }
167
168
bool KeyEquals(KeyTypePointer aKey) const {
169
return memcmp(mHash.get(), aKey, sizeof(SHA1Sum::Hash)) == 0;
170
}
171
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
172
static PLDHashNumber HashKey(KeyTypePointer aKey) {
173
return (reinterpret_cast<const uint32_t*>(aKey))[0];
174
}
175
176
void AddHandle(CacheFileHandle* aHandle);
177
void RemoveHandle(CacheFileHandle* aHandle);
178
already_AddRefed<CacheFileHandle> GetNewestHandle();
179
void GetHandles(nsTArray<RefPtr<CacheFileHandle> >& aResult);
180
181
SHA1Sum::Hash* Hash() const {
182
return reinterpret_cast<SHA1Sum::Hash*>(mHash.get());
183
}
184
bool IsEmpty() const { return mHandles.Length() == 0; }
185
186
enum { ALLOW_MEMMOVE = true };
187
188
#ifdef DEBUG
189
void AssertHandlesState();
190
#endif
191
192
// Memory reporting
193
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
194
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
195
196
private:
197
// We can't make this UniquePtr<SHA1Sum::Hash>, because you can't have
198
// UniquePtrs with known bounds. So we settle for this representation
199
// and using appropriate casts when we need to access it as a
200
// SHA1Sum::Hash.
201
UniquePtr<uint8_t[]> mHash;
202
// Use weak pointers since the hash table access is on a single thread
203
// only and CacheFileHandle removes itself from this table in its dtor
204
// that may only be called on the same thread as we work with the hashtable
205
// since we dispatch its Release() to this thread.
206
nsTArray<CacheFileHandle*> mHandles;
207
};
208
209
private:
210
nsTHashtable<HandleHashKey> mTable;
211
};
212
213
////////////////////////////////////////////////////////////////////////////////
214
215
class OpenFileEvent;
216
class ReadEvent;
217
class WriteEvent;
218
class MetadataWriteScheduleEvent;
219
class CacheFileContextEvictor;
220
221
#define CACHEFILEIOLISTENER_IID \
222
{ /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */ \
223
0xdcaf2ddc, 0x17cf, 0x4242, { \
224
0xbc, 0xa1, 0x8c, 0x86, 0x93, 0x63, 0x75, 0xa5 \
225
} \
226
}
227
228
class CacheFileIOListener : public nsISupports {
229
public:
230
NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEIOLISTENER_IID)
231
232
NS_IMETHOD OnFileOpened(CacheFileHandle* aHandle, nsresult aResult) = 0;
233
NS_IMETHOD OnDataWritten(CacheFileHandle* aHandle, const char* aBuf,
234
nsresult aResult) = 0;
235
NS_IMETHOD OnDataRead(CacheFileHandle* aHandle, char* aBuf,
236
nsresult aResult) = 0;
237
NS_IMETHOD OnFileDoomed(CacheFileHandle* aHandle, nsresult aResult) = 0;
238
NS_IMETHOD OnEOFSet(CacheFileHandle* aHandle, nsresult aResult) = 0;
239
NS_IMETHOD OnFileRenamed(CacheFileHandle* aHandle, nsresult aResult) = 0;
240
241
virtual bool IsKilled() { return false; }
242
};
243
244
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener, CACHEFILEIOLISTENER_IID)
245
246
class CacheFileIOManager final : public nsITimerCallback, public nsINamed {
247
public:
248
NS_DECL_THREADSAFE_ISUPPORTS
249
NS_DECL_NSITIMERCALLBACK
250
NS_DECL_NSINAMED
251
252
enum {
253
OPEN = 0U,
254
CREATE = 1U,
255
CREATE_NEW = 2U,
256
PRIORITY = 4U,
257
SPECIAL_FILE = 8U,
258
PINNED = 16U
259
};
260
261
CacheFileIOManager();
262
263
static nsresult Init();
264
static nsresult Shutdown();
265
static nsresult OnProfile();
266
static already_AddRefed<nsIEventTarget> IOTarget();
267
static already_AddRefed<CacheIOThread> IOThread();
268
static bool IsOnIOThread();
269
static bool IsOnIOThreadOrCeased();
270
static bool IsShutdown();
271
272
// Make aFile's WriteMetadataIfNeeded be called automatically after
273
// a short interval.
274
static nsresult ScheduleMetadataWrite(CacheFile* aFile);
275
// Remove aFile from the scheduling registry array.
276
// WriteMetadataIfNeeded will not be automatically called.
277
static nsresult UnscheduleMetadataWrite(CacheFile* aFile);
278
// Shuts the scheduling off and flushes all pending metadata writes.
279
static nsresult ShutdownMetadataWriteScheduling();
280
281
static nsresult OpenFile(const nsACString& aKey, uint32_t aFlags,
282
CacheFileIOListener* aCallback);
283
static nsresult Read(CacheFileHandle* aHandle, int64_t aOffset, char* aBuf,
284
int32_t aCount, CacheFileIOListener* aCallback);
285
static nsresult Write(CacheFileHandle* aHandle, int64_t aOffset,
286
const char* aBuf, int32_t aCount, bool aValidate,
287
bool aTruncate, CacheFileIOListener* aCallback);
288
// PinningDoomRestriction:
289
// NO_RESTRICTION
290
// no restriction is checked, the file is simply always doomed
291
// DOOM_WHEN_(NON)_PINNED, we branch based on the pinning status of the
292
// handle:
293
// UNKNOWN: the handle is marked to be doomed when later found (non)pinned
294
// PINNED/NON_PINNED: doom only when the restriction matches the pin status
295
// and the handle has not yet been required to doom during the UNKNOWN
296
// period
297
enum PinningDoomRestriction {
298
NO_RESTRICTION,
299
DOOM_WHEN_NON_PINNED,
300
DOOM_WHEN_PINNED
301
};
302
static nsresult DoomFile(CacheFileHandle* aHandle,
303
CacheFileIOListener* aCallback);
304
static nsresult DoomFileByKey(const nsACString& aKey,
305
CacheFileIOListener* aCallback);
306
static nsresult ReleaseNSPRHandle(CacheFileHandle* aHandle);
307
static nsresult TruncateSeekSetEOF(CacheFileHandle* aHandle,
308
int64_t aTruncatePos, int64_t aEOFPos,
309
CacheFileIOListener* aCallback);
310
static nsresult RenameFile(CacheFileHandle* aHandle,
311
const nsACString& aNewName,
312
CacheFileIOListener* aCallback);
313
static nsresult EvictIfOverLimit();
314
static nsresult EvictAll();
315
static nsresult EvictByContext(nsILoadContextInfo* aLoadContextInfo,
316
bool aPinning, const nsAString& aOrigin);
317
318
static nsresult InitIndexEntry(CacheFileHandle* aHandle,
319
OriginAttrsHash aOriginAttrsHash,
320
bool aAnonymous, bool aPinning);
321
static nsresult UpdateIndexEntry(CacheFileHandle* aHandle,
322
const uint32_t* aFrecency,
323
const bool* aHasAltData,
324
const uint16_t* aOnStartTime,
325
const uint16_t* aOnStopTime,
326
const uint8_t* aContentType,
327
const uint16_t* aBaseDomainAccessCount,
328
const uint32_t aTelemetryReportID);
329
330
static nsresult UpdateIndexEntry();
331
332
enum EEnumerateMode { ENTRIES, DOOMED };
333
334
static void GetCacheDirectory(nsIFile** result);
335
#if defined(MOZ_WIDGET_ANDROID)
336
static void GetProfilelessCacheDirectory(nsIFile** result);
337
#endif
338
339
// Calls synchronously OnEntryInfo for an entry with the given hash.
340
// Tries to find an existing entry in the service hashtables first, if not
341
// found, loads synchronously from disk file.
342
// Callable on the IO thread only.
343
static nsresult GetEntryInfo(
344
const SHA1Sum::Hash* aHash,
345
CacheStorageService::EntryInfoCallback* aCallback);
346
347
// Memory reporting
348
static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
349
static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
350
351
private:
352
friend class CacheFileHandle;
353
friend class CacheFileChunk;
354
friend class CacheFile;
355
friend class ShutdownEvent;
356
friend class OpenFileEvent;
357
friend class CloseHandleEvent;
358
friend class ReadEvent;
359
friend class WriteEvent;
360
friend class DoomFileEvent;
361
friend class DoomFileByKeyEvent;
362
friend class ReleaseNSPRHandleEvent;
363
friend class TruncateSeekSetEOFEvent;
364
friend class RenameFileEvent;
365
friend class CacheIndex;
366
friend class MetadataWriteScheduleEvent;
367
friend class CacheFileContextEvictor;
368
369
virtual ~CacheFileIOManager();
370
371
nsresult InitInternal();
372
nsresult ShutdownInternal();
373
374
nsresult OpenFileInternal(const SHA1Sum::Hash* aHash, const nsACString& aKey,
375
uint32_t aFlags, CacheFileHandle** _retval);
376
nsresult OpenSpecialFileInternal(const nsACString& aKey, uint32_t aFlags,
377
CacheFileHandle** _retval);
378
nsresult CloseHandleInternal(CacheFileHandle* aHandle);
379
nsresult ReadInternal(CacheFileHandle* aHandle, int64_t aOffset, char* aBuf,
380
int32_t aCount);
381
nsresult WriteInternal(CacheFileHandle* aHandle, int64_t aOffset,
382
const char* aBuf, int32_t aCount, bool aValidate,
383
bool aTruncate);
384
nsresult DoomFileInternal(
385
CacheFileHandle* aHandle,
386
PinningDoomRestriction aPinningStatusRestriction = NO_RESTRICTION);
387
nsresult DoomFileByKeyInternal(const SHA1Sum::Hash* aHash);
388
nsresult MaybeReleaseNSPRHandleInternal(CacheFileHandle* aHandle,
389
bool aIgnoreShutdownLag = false);
390
nsresult TruncateSeekSetEOFInternal(CacheFileHandle* aHandle,
391
int64_t aTruncatePos, int64_t aEOFPos);
392
nsresult RenameFileInternal(CacheFileHandle* aHandle,
393
const nsACString& aNewName);
394
nsresult EvictIfOverLimitInternal();
395
nsresult OverLimitEvictionInternal();
396
nsresult EvictAllInternal();
397
nsresult EvictByContextInternal(nsILoadContextInfo* aLoadContextInfo,
398
bool aPinning, const nsAString& aOrigin);
399
400
nsresult TrashDirectory(nsIFile* aFile);
401
static void OnTrashTimer(nsITimer* aTimer, void* aClosure);
402
nsresult StartRemovingTrash();
403
nsresult RemoveTrashInternal();
404
nsresult FindTrashDirToRemove();
405
406
nsresult CreateFile(CacheFileHandle* aHandle);
407
static void HashToStr(const SHA1Sum::Hash* aHash, nsACString& _retval);
408
static nsresult StrToHash(const nsACString& aHash, SHA1Sum::Hash* _retval);
409
nsresult GetFile(const SHA1Sum::Hash* aHash, nsIFile** _retval);
410
nsresult GetSpecialFile(const nsACString& aKey, nsIFile** _retval);
411
nsresult GetDoomedFile(nsIFile** _retval);
412
nsresult IsEmptyDirectory(nsIFile* aFile, bool* _retval);
413
nsresult CheckAndCreateDir(nsIFile* aFile, const char* aDir,
414
bool aEnsureEmptyDir);
415
nsresult CreateCacheTree();
416
nsresult OpenNSPRHandle(CacheFileHandle* aHandle, bool aCreate = false);
417
void NSPRHandleUsed(CacheFileHandle* aHandle);
418
419
// Removing all cache files during shutdown
420
nsresult SyncRemoveDir(nsIFile* aFile, const char* aDir);
421
void SyncRemoveAllCacheFiles();
422
423
nsresult ScheduleMetadataWriteInternal(CacheFile* aFile);
424
nsresult UnscheduleMetadataWriteInternal(CacheFile* aFile);
425
nsresult ShutdownMetadataWriteSchedulingInternal();
426
427
static nsresult CacheIndexStateChanged();
428
nsresult CacheIndexStateChangedInternal();
429
430
// Smart size calculation. UpdateSmartCacheSize() must be called on IO thread.
431
// It is called in EvictIfOverLimitInternal() just before we decide whether to
432
// start overlimit eviction or not and also in OverLimitEvictionInternal()
433
// before we start an eviction loop.
434
nsresult UpdateSmartCacheSize(int64_t aFreeSpace);
435
436
// Memory reporting (private part)
437
size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
438
439
static StaticRefPtr<CacheFileIOManager> gInstance;
440
441
TimeStamp mStartTime;
442
// Set true on the IO thread, CLOSE level as part of the internal shutdown
443
// procedure.
444
bool mShuttingDown;
445
RefPtr<CacheIOThread> mIOThread;
446
nsCOMPtr<nsIFile> mCacheDirectory;
447
#if defined(MOZ_WIDGET_ANDROID)
448
// On Android we add the active profile directory name between the path
449
// and the 'cache2' leaf name. However, to delete any leftover data from
450
// times before we were doing it, we still need to access the directory
451
// w/o the profile name in the path. Here it is stored.
452
nsCOMPtr<nsIFile> mCacheProfilelessDirectory;
453
#endif
454
bool mTreeCreated;
455
bool mTreeCreationFailed;
456
CacheFileHandles mHandles;
457
nsTArray<CacheFileHandle*> mHandlesByLastUsed;
458
nsTArray<CacheFileHandle*> mSpecialHandles;
459
nsTArray<RefPtr<CacheFile> > mScheduledMetadataWrites;
460
nsCOMPtr<nsITimer> mMetadataWritesTimer;
461
bool mOverLimitEvicting;
462
// When overlimit eviction is too slow and cache size reaches 105% of the
463
// limit, this flag is set and no other content is cached to prevent
464
// uncontrolled cache growing.
465
bool mCacheSizeOnHardLimit;
466
bool mRemovingTrashDirs;
467
nsCOMPtr<nsITimer> mTrashTimer;
468
nsCOMPtr<nsIFile> mTrashDir;
469
nsCOMPtr<nsIDirectoryEnumerator> mTrashDirEnumerator;
470
nsTArray<nsCString> mFailedTrashDirs;
471
RefPtr<CacheFileContextEvictor> mContextEvictor;
472
TimeStamp mLastSmartSizeTime;
473
};
474
475
} // namespace net
476
} // namespace mozilla
477
478
#endif