Line data Source code
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 CacheIndex__h__
6 : #define CacheIndex__h__
7 :
8 : #include "CacheLog.h"
9 : #include "CacheFileIOManager.h"
10 : #include "nsIRunnable.h"
11 : #include "CacheHashUtils.h"
12 : #include "nsICacheStorageService.h"
13 : #include "nsICacheEntry.h"
14 : #include "nsILoadContextInfo.h"
15 : #include "nsTHashtable.h"
16 : #include "nsThreadUtils.h"
17 : #include "nsWeakReference.h"
18 : #include "mozilla/IntegerPrintfMacros.h"
19 : #include "mozilla/SHA1.h"
20 : #include "mozilla/StaticMutex.h"
21 : #include "mozilla/StaticPtr.h"
22 : #include "mozilla/EndianUtils.h"
23 : #include "mozilla/TimeStamp.h"
24 :
25 : class nsIFile;
26 : class nsIDirectoryEnumerator;
27 : class nsITimer;
28 :
29 :
30 : #ifdef DEBUG
31 : #define DEBUG_STATS 1
32 : #endif
33 :
34 : namespace mozilla {
35 : namespace net {
36 :
37 : class CacheFileMetadata;
38 : class FileOpenHelper;
39 : class CacheIndexIterator;
40 :
41 : const uint16_t kIndexTimeNotAvailable = 0xFFFFU;
42 : const uint16_t kIndexTimeOutOfBound = 0xFFFEU;
43 :
44 : typedef struct {
45 : // Version of the index. The index must be ignored and deleted when the file
46 : // on disk was written with a newer version.
47 : uint32_t mVersion;
48 :
49 : // Timestamp of time when the last successful write of the index started.
50 : // During update process we use this timestamp for a quick validation of entry
51 : // files. If last modified time of the file is lower than this timestamp, we
52 : // skip parsing of such file since the information in index should be up to
53 : // date.
54 : uint32_t mTimeStamp;
55 :
56 : // We set this flag as soon as possible after parsing index during startup
57 : // and clean it after we write journal to disk during shutdown. We ignore the
58 : // journal and start update process whenever this flag is set during index
59 : // parsing.
60 : uint32_t mIsDirty;
61 : } CacheIndexHeader;
62 :
63 : static_assert(
64 : sizeof(CacheIndexHeader::mVersion) + sizeof(CacheIndexHeader::mTimeStamp) +
65 : sizeof(CacheIndexHeader::mIsDirty) == sizeof(CacheIndexHeader),
66 : "Unexpected sizeof(CacheIndexHeader)!");
67 :
68 : #pragma pack(push, 4)
69 : struct CacheIndexRecord {
70 : SHA1Sum::Hash mHash;
71 : uint32_t mFrecency;
72 : OriginAttrsHash mOriginAttrsHash;
73 : uint32_t mExpirationTime;
74 : uint16_t mOnStartTime;
75 : uint16_t mOnStopTime;
76 :
77 : /*
78 : * 1000 0000 0000 0000 0000 0000 0000 0000 : initialized
79 : * 0100 0000 0000 0000 0000 0000 0000 0000 : anonymous
80 : * 0010 0000 0000 0000 0000 0000 0000 0000 : removed
81 : * 0001 0000 0000 0000 0000 0000 0000 0000 : dirty
82 : * 0000 1000 0000 0000 0000 0000 0000 0000 : fresh
83 : * 0000 0100 0000 0000 0000 0000 0000 0000 : pinned
84 : * 0000 0010 0000 0000 0000 0000 0000 0000 : has cached alt data
85 : * 0000 0001 0000 0000 0000 0000 0000 0000 : reserved
86 : * 0000 0000 1111 1111 1111 1111 1111 1111 : file size (in kB)
87 : */
88 : uint32_t mFlags;
89 :
90 5 : CacheIndexRecord()
91 5 : : mFrecency(0)
92 : , mOriginAttrsHash(0)
93 : , mExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
94 : , mOnStartTime(kIndexTimeNotAvailable)
95 : , mOnStopTime(kIndexTimeNotAvailable)
96 5 : , mFlags(0)
97 5 : {}
98 : };
99 : #pragma pack(pop)
100 :
101 : static_assert(
102 : sizeof(CacheIndexRecord::mHash) + sizeof(CacheIndexRecord::mFrecency) +
103 : sizeof(CacheIndexRecord::mOriginAttrsHash) + sizeof(CacheIndexRecord::mExpirationTime) +
104 : sizeof(CacheIndexRecord::mOnStartTime) + sizeof(CacheIndexRecord::mOnStopTime) +
105 : sizeof(CacheIndexRecord::mFlags) == sizeof(CacheIndexRecord),
106 : "Unexpected sizeof(CacheIndexRecord)!");
107 :
108 : class CacheIndexEntry : public PLDHashEntryHdr
109 : {
110 : public:
111 : typedef const SHA1Sum::Hash& KeyType;
112 : typedef const SHA1Sum::Hash* KeyTypePointer;
113 :
114 5 : explicit CacheIndexEntry(KeyTypePointer aKey)
115 5 : {
116 5 : MOZ_COUNT_CTOR(CacheIndexEntry);
117 5 : mRec = new CacheIndexRecord();
118 5 : LOG(("CacheIndexEntry::CacheIndexEntry() - Created record [rec=%p]", mRec.get()));
119 5 : memcpy(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash));
120 5 : }
121 : CacheIndexEntry(const CacheIndexEntry& aOther)
122 : {
123 : NS_NOTREACHED("CacheIndexEntry copy constructor is forbidden!");
124 : }
125 0 : ~CacheIndexEntry()
126 0 : {
127 0 : MOZ_COUNT_DTOR(CacheIndexEntry);
128 0 : LOG(("CacheIndexEntry::~CacheIndexEntry() - Deleting record [rec=%p]",
129 : mRec.get()));
130 0 : }
131 :
132 : // KeyEquals(): does this entry match this key?
133 88 : bool KeyEquals(KeyTypePointer aKey) const
134 : {
135 88 : return memcmp(&mRec->mHash, aKey, sizeof(SHA1Sum::Hash)) == 0;
136 : }
137 :
138 : // KeyToPointer(): Convert KeyType to KeyTypePointer
139 111 : static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
140 :
141 : // HashKey(): calculate the hash number
142 108 : static PLDHashNumber HashKey(KeyTypePointer aKey)
143 : {
144 108 : return (reinterpret_cast<const uint32_t *>(aKey))[0];
145 : }
146 :
147 : // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
148 : // to use the copy constructor?
149 : enum { ALLOW_MEMMOVE = true };
150 :
151 : bool operator==(const CacheIndexEntry& aOther) const
152 : {
153 : return KeyEquals(&aOther.mRec->mHash);
154 : }
155 :
156 0 : CacheIndexEntry& operator=(const CacheIndexEntry& aOther)
157 : {
158 0 : MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
159 : sizeof(SHA1Sum::Hash)) == 0);
160 0 : mRec->mFrecency = aOther.mRec->mFrecency;
161 0 : mRec->mExpirationTime = aOther.mRec->mExpirationTime;
162 0 : mRec->mOriginAttrsHash = aOther.mRec->mOriginAttrsHash;
163 0 : mRec->mOnStartTime = aOther.mRec->mOnStartTime;
164 0 : mRec->mOnStopTime = aOther.mRec->mOnStopTime;
165 0 : mRec->mFlags = aOther.mRec->mFlags;
166 0 : return *this;
167 : }
168 :
169 5 : void InitNew()
170 : {
171 5 : mRec->mFrecency = 0;
172 5 : mRec->mExpirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
173 5 : mRec->mOriginAttrsHash = 0;
174 5 : mRec->mOnStartTime = kIndexTimeNotAvailable;
175 5 : mRec->mOnStopTime = kIndexTimeNotAvailable;
176 5 : mRec->mFlags = 0;
177 5 : }
178 :
179 5 : void Init(OriginAttrsHash aOriginAttrsHash, bool aAnonymous, bool aPinned)
180 : {
181 5 : MOZ_ASSERT(mRec->mFrecency == 0);
182 5 : MOZ_ASSERT(mRec->mExpirationTime == nsICacheEntry::NO_EXPIRATION_TIME);
183 5 : MOZ_ASSERT(mRec->mOriginAttrsHash == 0);
184 5 : MOZ_ASSERT(mRec->mOnStartTime == kIndexTimeNotAvailable);
185 5 : MOZ_ASSERT(mRec->mOnStopTime == kIndexTimeNotAvailable);
186 : // When we init the entry it must be fresh and may be dirty
187 5 : MOZ_ASSERT((mRec->mFlags & ~kDirtyMask) == kFreshMask);
188 :
189 5 : mRec->mOriginAttrsHash = aOriginAttrsHash;
190 5 : mRec->mFlags |= kInitializedMask;
191 5 : if (aAnonymous) {
192 0 : mRec->mFlags |= kAnonymousMask;
193 : }
194 5 : if (aPinned) {
195 0 : mRec->mFlags |= kPinnedMask;
196 : }
197 5 : }
198 :
199 0 : const SHA1Sum::Hash * Hash() const { return &mRec->mHash; }
200 :
201 146 : bool IsInitialized() const { return !!(mRec->mFlags & kInitializedMask); }
202 :
203 0 : mozilla::net::OriginAttrsHash OriginAttrsHash() const { return mRec->mOriginAttrsHash; }
204 :
205 0 : bool Anonymous() const { return !!(mRec->mFlags & kAnonymousMask); }
206 :
207 137 : bool IsRemoved() const { return !!(mRec->mFlags & kRemovedMask); }
208 0 : void MarkRemoved() { mRec->mFlags |= kRemovedMask; }
209 :
210 59 : bool IsDirty() const { return !!(mRec->mFlags & kDirtyMask); }
211 28 : void MarkDirty() { mRec->mFlags |= kDirtyMask; }
212 0 : void ClearDirty() { mRec->mFlags &= ~kDirtyMask; }
213 :
214 82 : bool IsFresh() const { return !!(mRec->mFlags & kFreshMask); }
215 5 : void MarkFresh() { mRec->mFlags |= kFreshMask; }
216 :
217 0 : bool IsPinned() const { return !!(mRec->mFlags & kPinnedMask); }
218 :
219 9 : void SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
220 11 : uint32_t GetFrecency() const { return mRec->mFrecency; }
221 :
222 5 : void SetExpirationTime(uint32_t aExpirationTime)
223 : {
224 5 : mRec->mExpirationTime = aExpirationTime;
225 5 : }
226 4 : uint32_t GetExpirationTime() const { return mRec->mExpirationTime; }
227 :
228 3 : void SetHasAltData(bool aHasAltData)
229 : {
230 0 : aHasAltData ? mRec->mFlags |= kHasAltDataMask
231 6 : : mRec->mFlags &= ~kHasAltDataMask;
232 3 : }
233 4 : bool GetHasAltData() const { return !!(mRec->mFlags & kHasAltDataMask); }
234 :
235 4 : void SetOnStartTime(uint16_t aTime)
236 : {
237 4 : mRec->mOnStartTime = aTime;
238 4 : }
239 3 : uint16_t GetOnStartTime() const { return mRec->mOnStartTime; }
240 :
241 4 : void SetOnStopTime(uint16_t aTime)
242 : {
243 4 : mRec->mOnStopTime = aTime;
244 4 : }
245 2 : uint16_t GetOnStopTime() const { return mRec->mOnStopTime; }
246 :
247 : // Sets filesize in kilobytes.
248 6 : void SetFileSize(uint32_t aFileSize)
249 : {
250 6 : if (aFileSize > kFileSizeMask) {
251 0 : LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
252 : "truncating to %u", kFileSizeMask));
253 0 : aFileSize = kFileSizeMask;
254 : }
255 6 : mRec->mFlags &= ~kFileSizeMask;
256 6 : mRec->mFlags |= aFileSize;
257 6 : }
258 : // Returns filesize in kilobytes.
259 88 : uint32_t GetFileSize() const { return GetFileSize(mRec); }
260 88 : static uint32_t GetFileSize(CacheIndexRecord *aRec)
261 : {
262 88 : return aRec->mFlags & kFileSizeMask;
263 : }
264 0 : static uint32_t IsPinned(CacheIndexRecord *aRec)
265 : {
266 0 : return aRec->mFlags & kPinnedMask;
267 : }
268 49 : bool IsFileEmpty() const { return GetFileSize() == 0; }
269 :
270 0 : void WriteToBuf(void *aBuf)
271 : {
272 0 : uint8_t* ptr = static_cast<uint8_t*>(aBuf);
273 0 : memcpy(ptr, mRec->mHash, sizeof(SHA1Sum::Hash)); ptr += sizeof(SHA1Sum::Hash);
274 0 : NetworkEndian::writeUint32(ptr, mRec->mFrecency); ptr += sizeof(uint32_t);
275 0 : NetworkEndian::writeUint64(ptr, mRec->mOriginAttrsHash); ptr += sizeof(uint64_t);
276 0 : NetworkEndian::writeUint32(ptr, mRec->mExpirationTime); ptr += sizeof(uint32_t);
277 0 : NetworkEndian::writeUint16(ptr, mRec->mOnStartTime); ptr += sizeof(uint16_t);
278 0 : NetworkEndian::writeUint16(ptr, mRec->mOnStopTime); ptr += sizeof(uint16_t);
279 : // Dirty and fresh flags should never go to disk, since they make sense only
280 : // during current session.
281 0 : NetworkEndian::writeUint32(ptr, mRec->mFlags & ~(kDirtyMask | kFreshMask));
282 0 : }
283 :
284 0 : void ReadFromBuf(void *aBuf)
285 : {
286 0 : const uint8_t* ptr = static_cast<const uint8_t*>(aBuf);
287 0 : MOZ_ASSERT(memcmp(&mRec->mHash, ptr, sizeof(SHA1Sum::Hash)) == 0); ptr += sizeof(SHA1Sum::Hash);
288 0 : mRec->mFrecency = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
289 0 : mRec->mOriginAttrsHash = NetworkEndian::readUint64(ptr); ptr += sizeof(uint64_t);
290 0 : mRec->mExpirationTime = NetworkEndian::readUint32(ptr); ptr += sizeof(uint32_t);
291 0 : mRec->mOnStartTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
292 0 : mRec->mOnStopTime = NetworkEndian::readUint16(ptr); ptr += sizeof(uint16_t);
293 0 : mRec->mFlags = NetworkEndian::readUint32(ptr);
294 0 : }
295 :
296 0 : void Log() const {
297 0 : LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
298 : " initialized=%u, removed=%u, dirty=%u, anonymous=%u, "
299 : "originAttrsHash=%" PRIx64 ", frecency=%u, expirationTime=%u, "
300 : "hasAltData=%u, onStartTime=%u, onStopTime=%u, size=%u]",
301 : this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
302 : IsDirty(), Anonymous(), OriginAttrsHash(), GetFrecency(),
303 : GetExpirationTime(), GetHasAltData(), GetOnStartTime(),
304 : GetOnStopTime(), GetFileSize()));
305 0 : }
306 :
307 0 : static bool RecordMatchesLoadContextInfo(CacheIndexRecord *aRec,
308 : nsILoadContextInfo *aInfo)
309 : {
310 0 : MOZ_ASSERT(aInfo);
311 :
312 0 : if (!aInfo->IsPrivate() &&
313 0 : GetOriginAttrsHash(*aInfo->OriginAttributesPtr()) == aRec->mOriginAttrsHash &&
314 0 : aInfo->IsAnonymous() == !!(aRec->mFlags & kAnonymousMask)) {
315 0 : return true;
316 : }
317 :
318 0 : return false;
319 : }
320 :
321 : // Memory reporting
322 0 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
323 : {
324 0 : return mallocSizeOf(mRec.get());
325 : }
326 :
327 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
328 : {
329 : return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
330 : }
331 :
332 : private:
333 : friend class CacheIndexEntryUpdate;
334 : friend class CacheIndex;
335 : friend class CacheIndexEntryAutoManage;
336 :
337 : static const uint32_t kInitializedMask = 0x80000000;
338 : static const uint32_t kAnonymousMask = 0x40000000;
339 :
340 : // This flag is set when the entry was removed. We need to keep this
341 : // information in memory until we write the index file.
342 : static const uint32_t kRemovedMask = 0x20000000;
343 :
344 : // This flag is set when the information in memory is not in sync with the
345 : // information in index file on disk.
346 : static const uint32_t kDirtyMask = 0x10000000;
347 :
348 : // This flag is set when the information about the entry is fresh, i.e.
349 : // we've created or opened this entry during this session, or we've seen
350 : // this entry during update or build process.
351 : static const uint32_t kFreshMask = 0x08000000;
352 :
353 : // Indicates a pinned entry.
354 : static const uint32_t kPinnedMask = 0x04000000;
355 :
356 : // Indicates there is cached alternative data in the entry.
357 : static const uint32_t kHasAltDataMask = 0x02000000;
358 : static const uint32_t kReservedMask = 0x01000000;
359 :
360 : // FileSize in kilobytes
361 : static const uint32_t kFileSizeMask = 0x00FFFFFF;
362 :
363 : nsAutoPtr<CacheIndexRecord> mRec;
364 : };
365 :
366 : class CacheIndexEntryUpdate : public CacheIndexEntry
367 : {
368 : public:
369 0 : explicit CacheIndexEntryUpdate(CacheIndexEntry::KeyTypePointer aKey)
370 0 : : CacheIndexEntry(aKey)
371 0 : , mUpdateFlags(0)
372 : {
373 0 : MOZ_COUNT_CTOR(CacheIndexEntryUpdate);
374 0 : LOG(("CacheIndexEntryUpdate::CacheIndexEntryUpdate()"));
375 0 : }
376 0 : ~CacheIndexEntryUpdate()
377 0 : {
378 0 : MOZ_COUNT_DTOR(CacheIndexEntryUpdate);
379 0 : LOG(("CacheIndexEntryUpdate::~CacheIndexEntryUpdate()"));
380 0 : }
381 :
382 0 : CacheIndexEntryUpdate& operator=(const CacheIndexEntry& aOther)
383 : {
384 0 : MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
385 : sizeof(SHA1Sum::Hash)) == 0);
386 0 : mUpdateFlags = 0;
387 0 : *(static_cast<CacheIndexEntry *>(this)) = aOther;
388 0 : return *this;
389 : }
390 :
391 0 : void InitNew()
392 : {
393 0 : mUpdateFlags = kFrecencyUpdatedMask | kExpirationUpdatedMask |
394 : kHasAltDataUpdatedMask | kOnStartTimeUpdatedMask |
395 : kOnStopTimeUpdatedMask | kFileSizeUpdatedMask;
396 0 : CacheIndexEntry::InitNew();
397 0 : }
398 :
399 0 : void SetFrecency(uint32_t aFrecency)
400 : {
401 0 : mUpdateFlags |= kFrecencyUpdatedMask;
402 0 : CacheIndexEntry::SetFrecency(aFrecency);
403 0 : }
404 :
405 0 : void SetExpirationTime(uint32_t aExpirationTime)
406 : {
407 0 : mUpdateFlags |= kExpirationUpdatedMask;
408 0 : CacheIndexEntry::SetExpirationTime(aExpirationTime);
409 0 : }
410 :
411 0 : void SetHasAltData(bool aHasAltData)
412 : {
413 0 : mUpdateFlags |= kHasAltDataUpdatedMask;
414 0 : CacheIndexEntry::SetHasAltData(aHasAltData);
415 0 : }
416 :
417 0 : void SetOnStartTime(uint16_t aTime)
418 : {
419 0 : mUpdateFlags |= kOnStartTimeUpdatedMask;
420 0 : CacheIndexEntry::SetOnStartTime(aTime);
421 0 : }
422 :
423 0 : void SetOnStopTime(uint16_t aTime)
424 : {
425 0 : mUpdateFlags |= kOnStopTimeUpdatedMask;
426 0 : CacheIndexEntry::SetOnStopTime(aTime);
427 0 : }
428 :
429 0 : void SetFileSize(uint32_t aFileSize)
430 : {
431 0 : mUpdateFlags |= kFileSizeUpdatedMask;
432 0 : CacheIndexEntry::SetFileSize(aFileSize);
433 0 : }
434 :
435 0 : void ApplyUpdate(CacheIndexEntry *aDst) {
436 0 : MOZ_ASSERT(memcmp(&mRec->mHash, &aDst->mRec->mHash,
437 : sizeof(SHA1Sum::Hash)) == 0);
438 0 : if (mUpdateFlags & kFrecencyUpdatedMask) {
439 0 : aDst->mRec->mFrecency = mRec->mFrecency;
440 : }
441 0 : if (mUpdateFlags & kExpirationUpdatedMask) {
442 0 : aDst->mRec->mExpirationTime = mRec->mExpirationTime;
443 : }
444 0 : aDst->mRec->mOriginAttrsHash = mRec->mOriginAttrsHash;
445 0 : if (mUpdateFlags & kOnStartTimeUpdatedMask) {
446 0 : aDst->mRec->mOnStartTime = mRec->mOnStartTime;
447 : }
448 0 : if (mUpdateFlags & kOnStopTimeUpdatedMask) {
449 0 : aDst->mRec->mOnStopTime = mRec->mOnStopTime;
450 : }
451 0 : if (mUpdateFlags & kHasAltDataUpdatedMask &&
452 0 : ((aDst->mRec->mFlags ^ mRec->mFlags) & kHasAltDataMask)) {
453 : // Toggle the bit if we need to.
454 0 : aDst->mRec->mFlags ^= kHasAltDataMask;
455 : }
456 :
457 0 : if (mUpdateFlags & kFileSizeUpdatedMask) {
458 : // Copy all flags except |HasAltData|.
459 0 : aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask);
460 : } else {
461 : // Copy all flags except |HasAltData| and file size.
462 0 : aDst->mRec->mFlags &= kFileSizeMask;
463 0 : aDst->mRec->mFlags |= (mRec->mFlags & ~kHasAltDataMask & ~kFileSizeMask);
464 : }
465 0 : }
466 :
467 : private:
468 : static const uint32_t kFrecencyUpdatedMask = 0x00000001;
469 : static const uint32_t kExpirationUpdatedMask = 0x00000002;
470 : static const uint32_t kFileSizeUpdatedMask = 0x00000004;
471 : static const uint32_t kHasAltDataUpdatedMask = 0x00000008;
472 : static const uint32_t kOnStartTimeUpdatedMask = 0x00000010;
473 : static const uint32_t kOnStopTimeUpdatedMask = 0x00000020;
474 :
475 : uint32_t mUpdateFlags;
476 : };
477 :
478 : class CacheIndexStats
479 : {
480 : public:
481 3 : CacheIndexStats()
482 3 : : mCount(0)
483 : , mNotInitialized(0)
484 : , mRemoved(0)
485 : , mDirty(0)
486 : , mFresh(0)
487 : , mEmpty(0)
488 : , mSize(0)
489 : #ifdef DEBUG
490 : , mStateLogged(false)
491 3 : , mDisableLogging(false)
492 : #endif
493 : {
494 3 : }
495 :
496 1 : bool operator==(const CacheIndexStats& aOther) const
497 : {
498 : return
499 : #ifdef DEBUG
500 2 : aOther.mStateLogged == mStateLogged &&
501 : #endif
502 2 : aOther.mCount == mCount &&
503 2 : aOther.mNotInitialized == mNotInitialized &&
504 2 : aOther.mRemoved == mRemoved &&
505 2 : aOther.mDirty == mDirty &&
506 2 : aOther.mFresh == mFresh &&
507 3 : aOther.mEmpty == mEmpty &&
508 2 : aOther.mSize == mSize;
509 : }
510 :
511 : #ifdef DEBUG
512 2 : void DisableLogging() {
513 2 : mDisableLogging = true;
514 2 : }
515 : #endif
516 :
517 65 : void Log() {
518 65 : LOG(("CacheIndexStats::Log() [count=%u, notInitialized=%u, removed=%u, "
519 : "dirty=%u, fresh=%u, empty=%u, size=%u]", mCount, mNotInitialized,
520 : mRemoved, mDirty, mFresh, mEmpty, mSize));
521 65 : }
522 :
523 0 : void Clear() {
524 0 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Clear() - state logged!");
525 :
526 0 : mCount = 0;
527 0 : mNotInitialized = 0;
528 0 : mRemoved = 0;
529 0 : mDirty = 0;
530 0 : mFresh = 0;
531 0 : mEmpty = 0;
532 0 : mSize = 0;
533 0 : }
534 :
535 : #ifdef DEBUG
536 : bool StateLogged() {
537 : return mStateLogged;
538 : }
539 : #endif
540 :
541 0 : uint32_t Count() {
542 0 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Count() - state logged!");
543 0 : return mCount;
544 : }
545 :
546 0 : uint32_t Dirty() {
547 0 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Dirty() - state logged!");
548 0 : return mDirty;
549 : }
550 :
551 1 : uint32_t Fresh() {
552 1 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Fresh() - state logged!");
553 1 : return mFresh;
554 : }
555 :
556 0 : uint32_t ActiveEntriesCount() {
557 0 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::ActiveEntriesCount() - state "
558 : "logged!");
559 0 : return mCount - mRemoved - mNotInitialized - mEmpty;
560 : }
561 :
562 4 : uint32_t Size() {
563 4 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Size() - state logged!");
564 4 : return mSize;
565 : }
566 :
567 32 : void BeforeChange(const CacheIndexEntry *aEntry) {
568 : #ifdef DEBUG_STATS
569 32 : if (!mDisableLogging) {
570 32 : LOG(("CacheIndexStats::BeforeChange()"));
571 32 : Log();
572 : }
573 : #endif
574 :
575 32 : MOZ_ASSERT(!mStateLogged, "CacheIndexStats::BeforeChange() - state "
576 : "logged!");
577 : #ifdef DEBUG
578 32 : mStateLogged = true;
579 : #endif
580 32 : if (aEntry) {
581 27 : MOZ_ASSERT(mCount);
582 27 : mCount--;
583 27 : if (aEntry->IsDirty()) {
584 27 : MOZ_ASSERT(mDirty);
585 27 : mDirty--;
586 : }
587 27 : if (aEntry->IsFresh()) {
588 27 : MOZ_ASSERT(mFresh);
589 27 : mFresh--;
590 : }
591 27 : if (aEntry->IsRemoved()) {
592 0 : MOZ_ASSERT(mRemoved);
593 0 : mRemoved--;
594 : } else {
595 27 : if (!aEntry->IsInitialized()) {
596 5 : MOZ_ASSERT(mNotInitialized);
597 5 : mNotInitialized--;
598 : } else {
599 22 : if (aEntry->IsFileEmpty()) {
600 14 : MOZ_ASSERT(mEmpty);
601 14 : mEmpty--;
602 : } else {
603 8 : MOZ_ASSERT(mSize >= aEntry->GetFileSize());
604 8 : mSize -= aEntry->GetFileSize();
605 : }
606 : }
607 : }
608 : }
609 32 : }
610 :
611 32 : void AfterChange(const CacheIndexEntry *aEntry) {
612 32 : MOZ_ASSERT(mStateLogged, "CacheIndexStats::AfterChange() - state not "
613 : "logged!");
614 : #ifdef DEBUG
615 32 : mStateLogged = false;
616 : #endif
617 32 : if (aEntry) {
618 32 : ++mCount;
619 32 : if (aEntry->IsDirty()) {
620 32 : mDirty++;
621 : }
622 32 : if (aEntry->IsFresh()) {
623 32 : mFresh++;
624 : }
625 32 : if (aEntry->IsRemoved()) {
626 0 : mRemoved++;
627 : } else {
628 32 : if (!aEntry->IsInitialized()) {
629 5 : mNotInitialized++;
630 : } else {
631 27 : if (aEntry->IsFileEmpty()) {
632 14 : mEmpty++;
633 : } else {
634 13 : mSize += aEntry->GetFileSize();
635 : }
636 : }
637 : }
638 : }
639 :
640 : #ifdef DEBUG_STATS
641 32 : if (!mDisableLogging) {
642 32 : LOG(("CacheIndexStats::AfterChange()"));
643 32 : Log();
644 : }
645 : #endif
646 32 : }
647 :
648 : private:
649 : uint32_t mCount;
650 : uint32_t mNotInitialized;
651 : uint32_t mRemoved;
652 : uint32_t mDirty;
653 : uint32_t mFresh;
654 : uint32_t mEmpty;
655 : uint32_t mSize;
656 : #ifdef DEBUG
657 : // We completely remove the data about an entry from the stats in
658 : // BeforeChange() and set this flag to true. The entry is then modified,
659 : // deleted or created and the data is again put into the stats and this flag
660 : // set to false. Statistics must not be read during this time since the
661 : // information is not correct.
662 : bool mStateLogged;
663 :
664 : // Disables logging in this instance of CacheIndexStats
665 : bool mDisableLogging;
666 : #endif
667 : };
668 :
669 : class CacheIndex : public CacheFileIOListener
670 : , public nsIRunnable
671 : {
672 : public:
673 : NS_DECL_THREADSAFE_ISUPPORTS
674 : NS_DECL_NSIRUNNABLE
675 :
676 : CacheIndex();
677 :
678 : static nsresult Init(nsIFile *aCacheDirectory);
679 : static nsresult PreShutdown();
680 : static nsresult Shutdown();
681 :
682 : // Following methods can be called only on IO thread.
683 :
684 : // Add entry to the index. The entry shouldn't be present in index. This
685 : // method is called whenever a new handle for a new entry file is created. The
686 : // newly created entry is not initialized and it must be either initialized
687 : // with InitEntry() or removed with RemoveEntry().
688 : static nsresult AddEntry(const SHA1Sum::Hash *aHash);
689 :
690 : // Inform index about an existing entry that should be present in index. This
691 : // method is called whenever a new handle for an existing entry file is
692 : // created. Like in case of AddEntry(), either InitEntry() or RemoveEntry()
693 : // must be called on the entry, since the entry is not initizlized if the
694 : // index is outdated.
695 : static nsresult EnsureEntryExists(const SHA1Sum::Hash *aHash);
696 :
697 : // Initialize the entry. It MUST be present in index. Call to AddEntry() or
698 : // EnsureEntryExists() must precede the call to this method.
699 : static nsresult InitEntry(const SHA1Sum::Hash *aHash,
700 : OriginAttrsHash aOriginAttrsHash,
701 : bool aAnonymous,
702 : bool aPinned);
703 :
704 : // Remove entry from index. The entry should be present in index.
705 : static nsresult RemoveEntry(const SHA1Sum::Hash *aHash);
706 :
707 : // Update some information in entry. The entry MUST be present in index and
708 : // MUST be initialized. Call to AddEntry() or EnsureEntryExists() and to
709 : // InitEntry() must precede the call to this method.
710 : // Pass nullptr if the value didn't change.
711 : static nsresult UpdateEntry(const SHA1Sum::Hash *aHash,
712 : const uint32_t *aFrecency,
713 : const uint32_t *aExpirationTime,
714 : const bool *aHasAltData,
715 : const uint16_t *aOnStartTime,
716 : const uint16_t *aOnStopTime,
717 : const uint32_t *aSize);
718 :
719 : // Remove all entries from the index. Called when clearing the whole cache.
720 : static nsresult RemoveAll();
721 :
722 : enum EntryStatus {
723 : EXISTS = 0,
724 : DOES_NOT_EXIST = 1,
725 : DO_NOT_KNOW = 2
726 : };
727 :
728 : // Returns status of the entry in index for the given key. It can be called
729 : // on any thread.
730 : // If the optional aCB callback is given, the it will be called with a
731 : // CacheIndexEntry only if _retval is EXISTS when the method returns.
732 : static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval,
733 : const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
734 : static nsresult HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval,
735 : const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
736 :
737 : // Returns a hash of the least important entry that should be evicted if the
738 : // cache size is over limit and also returns a total number of all entries in
739 : // the index minus the number of forced valid entries and unpinned entries
740 : // that we encounter when searching (see below)
741 : static nsresult GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt);
742 :
743 : // Checks if a cache entry is currently forced valid. Used to prevent an entry
744 : // (that has been forced valid) from being evicted when the cache size reaches
745 : // its limit.
746 : static bool IsForcedValidEntry(const SHA1Sum::Hash *aHash);
747 :
748 : // Returns cache size in kB.
749 : static nsresult GetCacheSize(uint32_t *_retval);
750 :
751 : // Returns number of entry files in the cache
752 : static nsresult GetEntryFileCount(uint32_t *_retval);
753 :
754 : // Synchronously returns the disk occupation and number of entries per-context.
755 : // Callable on any thread. It will ignore loadContextInfo and get stats for
756 : // all entries if the aInfo is a nullptr.
757 : static nsresult GetCacheStats(nsILoadContextInfo *aInfo, uint32_t *aSize, uint32_t *aCount);
758 :
759 : // Asynchronously gets the disk cache size, used for display in the UI.
760 : static nsresult AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver);
761 :
762 : // Returns an iterator that returns entries matching a given context that were
763 : // present in the index at the time this method was called. If aAddNew is true
764 : // then the iterator will also return entries created after this call.
765 : // NOTE: When some entry is removed from index it is removed also from the
766 : // iterator regardless what aAddNew was passed.
767 : static nsresult GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
768 : CacheIndexIterator **_retval);
769 :
770 : // Returns true if we _think_ that the index is up to date. I.e. the state is
771 : // READY or WRITING and mIndexNeedsUpdate as well as mShuttingDown is false.
772 : static nsresult IsUpToDate(bool *_retval);
773 :
774 : // Called from CacheStorageService::Clear() and CacheFileContextEvictor::EvictEntries(),
775 : // sets a flag that blocks notification to AsyncGetDiskConsumption.
776 : static void OnAsyncEviction(bool aEvicting);
777 :
778 : // Memory reporting
779 : static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
780 : static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
781 :
782 : private:
783 : friend class CacheIndexEntryAutoManage;
784 : friend class FileOpenHelper;
785 : friend class CacheIndexIterator;
786 :
787 : virtual ~CacheIndex();
788 :
789 : NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
790 : nsresult OnFileOpenedInternal(FileOpenHelper *aOpener,
791 : CacheFileHandle *aHandle, nsresult aResult);
792 : NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
793 : nsresult aResult) override;
794 : NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override;
795 : NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
796 : NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override;
797 : NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override;
798 :
799 : nsresult InitInternal(nsIFile *aCacheDirectory);
800 : void PreShutdownInternal();
801 :
802 : // This method returns false when index is not initialized or is shut down.
803 : bool IsIndexUsable();
804 :
805 : // This method checks whether the entry has the same values of
806 : // originAttributes and isAnonymous. We don't expect to find a collision
807 : // since these values are part of the key that we hash and we use a strong
808 : // hash function.
809 : static bool IsCollision(CacheIndexEntry *aEntry,
810 : OriginAttrsHash aOriginAttrsHash,
811 : bool aAnonymous);
812 :
813 : // Checks whether any of the information about the entry has changed.
814 : static bool HasEntryChanged(CacheIndexEntry *aEntry,
815 : const uint32_t *aFrecency,
816 : const uint32_t *aExpirationTime,
817 : const bool *aHasAltData,
818 : const uint16_t *aOnStartTime,
819 : const uint16_t *aOnStopTime,
820 : const uint32_t *aSize);
821 :
822 : // Merge all pending operations from mPendingUpdates into mIndex.
823 : void ProcessPendingOperations();
824 :
825 : // Following methods perform writing of the index file.
826 : //
827 : // The index is written periodically, but not earlier than once in
828 : // kMinDumpInterval and there must be at least kMinUnwrittenChanges
829 : // differences between index on disk and in memory. Index is always first
830 : // written to a temporary file and the old index file is replaced when the
831 : // writing process succeeds.
832 : //
833 : // Starts writing of index when both limits (minimal delay between writes and
834 : // minimum number of changes in index) were exceeded.
835 : bool WriteIndexToDiskIfNeeded();
836 : // Starts writing of index file.
837 : void WriteIndexToDisk();
838 : // Serializes part of mIndex hashtable to the write buffer a writes the buffer
839 : // to the file.
840 : void WriteRecords();
841 : // Finalizes writing process.
842 : void FinishWrite(bool aSucceeded);
843 :
844 : // Following methods perform writing of the journal during shutdown. All these
845 : // methods must be called only during shutdown since they write/delete files
846 : // directly on the main thread instead of using CacheFileIOManager that does
847 : // it asynchronously on IO thread. Journal contains only entries that are
848 : // dirty, i.e. changes that are not present in the index file on the disk.
849 : // When the log is written successfully, the dirty flag in index file is
850 : // cleared.
851 : nsresult GetFile(const nsACString &aName, nsIFile **_retval);
852 : nsresult RemoveFile(const nsACString &aName);
853 : void RemoveAllIndexFiles();
854 : void RemoveJournalAndTempFile();
855 : // Writes journal to the disk and clears dirty flag in index header.
856 : nsresult WriteLogToDisk();
857 :
858 : // Following methods perform reading of the index from the disk.
859 : //
860 : // Index is read at startup just after initializing the CacheIndex. There are
861 : // 3 files used when manipulating with index: index file, journal file and
862 : // a temporary file. All files contain the hash of the data, so we can check
863 : // whether the content is valid and complete. Index file contains also a dirty
864 : // flag in the index header which is unset on a clean shutdown. During opening
865 : // and reading of the files we determine the status of the whole index from
866 : // the states of the separate files. Following table shows all possible
867 : // combinations:
868 : //
869 : // index, journal, tmpfile
870 : // M * * - index is missing -> BUILD
871 : // I * * - index is invalid -> BUILD
872 : // D * * - index is dirty -> UPDATE
873 : // C M * - index is dirty -> UPDATE
874 : // C I * - unexpected state -> UPDATE
875 : // C V E - unexpected state -> UPDATE
876 : // C V M - index is up to date -> READY
877 : //
878 : // where the letters mean:
879 : // * - any state
880 : // E - file exists
881 : // M - file is missing
882 : // I - data is invalid (parsing failed or hash didn't match)
883 : // D - dirty (data in index file is correct, but dirty flag is set)
884 : // C - clean (index file is clean)
885 : // V - valid (data in journal file is correct)
886 : //
887 : // Note: We accept the data from journal only when the index is up to date as
888 : // a whole (i.e. C,V,M state).
889 : //
890 : // We rename the journal file to the temporary file as soon as possible after
891 : // initial test to ensure that we start update process on the next startup if
892 : // FF crashes during parsing of the index.
893 : //
894 : // Initiates reading index from disk.
895 : void ReadIndexFromDisk();
896 : // Starts reading data from index file.
897 : void StartReadingIndex();
898 : // Parses data read from index file.
899 : void ParseRecords();
900 : // Starts reading data from journal file.
901 : void StartReadingJournal();
902 : // Parses data read from journal file.
903 : void ParseJournal();
904 : // Merges entries from journal into mIndex.
905 : void MergeJournal();
906 : // In debug build this method checks that we have no fresh entry in mIndex
907 : // after we finish reading index and before we process pending operations.
908 : void EnsureNoFreshEntry();
909 : // In debug build this method is called after processing pending operations
910 : // to make sure mIndexStats contains correct information.
911 : void EnsureCorrectStats();
912 : // Finalizes reading process.
913 : void FinishRead(bool aSucceeded);
914 :
915 : // Following methods perform updating and building of the index.
916 : // Timer callback that starts update or build process.
917 : static void DelayedUpdate(nsITimer *aTimer, void *aClosure);
918 : void DelayedUpdateLocked();
919 : // Posts timer event that start update or build process.
920 : nsresult ScheduleUpdateTimer(uint32_t aDelay);
921 : nsresult SetupDirectoryEnumerator();
922 : nsresult InitEntryFromDiskData(CacheIndexEntry *aEntry,
923 : CacheFileMetadata *aMetaData,
924 : int64_t aFileSize);
925 : // Returns true when either a timer is scheduled or event is posted.
926 : bool IsUpdatePending();
927 : // Iterates through all files in entries directory that we didn't create/open
928 : // during this session, parses them and adds the entries to the index.
929 : void BuildIndex();
930 :
931 : bool StartUpdatingIndexIfNeeded(bool aSwitchingToReadyState = false);
932 : // Starts update or build process or fires a timer when it is too early after
933 : // startup.
934 : void StartUpdatingIndex(bool aRebuild);
935 : // Iterates through all files in entries directory that we didn't create/open
936 : // during this session and theirs last modified time is newer than timestamp
937 : // in the index header. Parses the files and adds the entries to the index.
938 : void UpdateIndex();
939 : // Finalizes update or build process.
940 : void FinishUpdate(bool aSucceeded);
941 :
942 : void RemoveNonFreshEntries();
943 :
944 : enum EState {
945 : // Initial state in which the index is not usable
946 : // Possible transitions:
947 : // -> READING
948 : INITIAL = 0,
949 :
950 : // Index is being read from the disk.
951 : // Possible transitions:
952 : // -> INITIAL - We failed to dispatch a read event.
953 : // -> BUILDING - No or corrupted index file was found.
954 : // -> UPDATING - No or corrupted journal file was found.
955 : // - Dirty flag was set in index header.
956 : // -> READY - Index was read successfully or was interrupted by
957 : // pre-shutdown.
958 : // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
959 : READING = 1,
960 :
961 : // Index is being written to the disk.
962 : // Possible transitions:
963 : // -> READY - Writing of index finished or was interrupted by
964 : // pre-shutdown..
965 : // -> UPDATING - Writing of index finished, but index was found outdated
966 : // during writing.
967 : // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
968 : WRITING = 2,
969 :
970 : // Index is being build.
971 : // Possible transitions:
972 : // -> READY - Building of index finished or was interrupted by
973 : // pre-shutdown.
974 : // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
975 : BUILDING = 3,
976 :
977 : // Index is being updated.
978 : // Possible transitions:
979 : // -> READY - Updating of index finished or was interrupted by
980 : // pre-shutdown.
981 : // -> SHUTDOWN - This could happen only in case of pre-shutdown failure.
982 : UPDATING = 4,
983 :
984 : // Index is ready.
985 : // Possible transitions:
986 : // -> UPDATING - Index was found outdated.
987 : // -> SHUTDOWN - Index is shutting down.
988 : READY = 5,
989 :
990 : // Index is shutting down.
991 : SHUTDOWN = 6
992 : };
993 :
994 : static char const * StateString(EState aState);
995 : void ChangeState(EState aNewState);
996 : void NotifyAsyncGetDiskConsumptionCallbacks();
997 :
998 : // Allocates and releases buffer used for reading and writing index.
999 : void AllocBuffer();
1000 : void ReleaseBuffer();
1001 :
1002 : // Methods used by CacheIndexEntryAutoManage to keep the iterators up to date.
1003 : void AddRecordToIterators(CacheIndexRecord *aRecord);
1004 : void RemoveRecordFromIterators(CacheIndexRecord *aRecord);
1005 : void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
1006 : CacheIndexRecord *aNewRecord);
1007 :
1008 : // Memory reporting (private part)
1009 : size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
1010 :
1011 : void ReportHashStats();
1012 :
1013 : static mozilla::StaticRefPtr<CacheIndex> gInstance;
1014 : static StaticMutex sLock;
1015 :
1016 : nsCOMPtr<nsIFile> mCacheDirectory;
1017 :
1018 : EState mState;
1019 : // Timestamp of time when the index was initialized. We use it to delay
1020 : // initial update or build of index.
1021 : TimeStamp mStartTime;
1022 : // Set to true in PreShutdown(), it is checked on variaous places to prevent
1023 : // starting any process (write, update, etc.) during shutdown.
1024 : bool mShuttingDown;
1025 : // When set to true, update process should start as soon as possible. This
1026 : // flag is set whenever we find some inconsistency which would be fixed by
1027 : // update process. The flag is checked always when switching to READY state.
1028 : // To make sure we start the update process as soon as possible, methods that
1029 : // set this flag should also call StartUpdatingIndexIfNeeded() to cover the
1030 : // case when we are currently in READY state.
1031 : bool mIndexNeedsUpdate;
1032 : // Set at the beginning of RemoveAll() which clears the whole index. When
1033 : // removing all entries we must stop any pending reading, writing, updating or
1034 : // building operation. This flag is checked at various places and it prevents
1035 : // we won't start another operation (e.g. canceling reading of the index would
1036 : // normally start update or build process)
1037 : bool mRemovingAll;
1038 : // Whether the index file on disk exists and is valid.
1039 : bool mIndexOnDiskIsValid;
1040 : // When something goes wrong during updating or building process, we don't
1041 : // mark index clean (and also don't write journal) to ensure that update or
1042 : // build will be initiated on the next start.
1043 : bool mDontMarkIndexClean;
1044 : // Timestamp value from index file. It is used during update process to skip
1045 : // entries that were last modified before this timestamp.
1046 : uint32_t mIndexTimeStamp;
1047 : // Timestamp of last time the index was dumped to disk.
1048 : // NOTE: The index might not be necessarily dumped at this time. The value
1049 : // is used to schedule next dump of the index.
1050 : TimeStamp mLastDumpTime;
1051 :
1052 : // Timer of delayed update/build.
1053 : nsCOMPtr<nsITimer> mUpdateTimer;
1054 : // True when build or update event is posted
1055 : bool mUpdateEventPending;
1056 :
1057 : // Helper members used when reading/writing index from/to disk.
1058 : // Contains number of entries that should be skipped:
1059 : // - in hashtable when writing index because they were already written
1060 : // - in index file when reading index because they were already read
1061 : uint32_t mSkipEntries;
1062 : // Number of entries that should be written to disk. This is number of entries
1063 : // in hashtable that are initialized and are not marked as removed when writing
1064 : // begins.
1065 : uint32_t mProcessEntries;
1066 : char *mRWBuf;
1067 : uint32_t mRWBufSize;
1068 : uint32_t mRWBufPos;
1069 : RefPtr<CacheHash> mRWHash;
1070 :
1071 : // True if read or write operation is pending. It is used to ensure that
1072 : // mRWBuf is not freed until OnDataRead or OnDataWritten is called.
1073 : bool mRWPending;
1074 :
1075 : // Reading of journal succeeded if true.
1076 : bool mJournalReadSuccessfully;
1077 :
1078 : // Handle used for writing and reading index file.
1079 : RefPtr<CacheFileHandle> mIndexHandle;
1080 : // Handle used for reading journal file.
1081 : RefPtr<CacheFileHandle> mJournalHandle;
1082 : // Used to check the existence of the file during reading process.
1083 : RefPtr<CacheFileHandle> mTmpHandle;
1084 :
1085 : RefPtr<FileOpenHelper> mIndexFileOpener;
1086 : RefPtr<FileOpenHelper> mJournalFileOpener;
1087 : RefPtr<FileOpenHelper> mTmpFileOpener;
1088 :
1089 : // Directory enumerator used when building and updating index.
1090 : nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
1091 :
1092 : // Main index hashtable.
1093 : nsTHashtable<CacheIndexEntry> mIndex;
1094 :
1095 : // We cannot add, remove or change any entry in mIndex in states READING and
1096 : // WRITING. We track all changes in mPendingUpdates during these states.
1097 : nsTHashtable<CacheIndexEntryUpdate> mPendingUpdates;
1098 :
1099 : // Contains information statistics for mIndex + mPendingUpdates.
1100 : CacheIndexStats mIndexStats;
1101 :
1102 : // When reading journal, we must first parse the whole file and apply the
1103 : // changes iff the journal was read successfully. mTmpJournal is used to store
1104 : // entries from the journal file. We throw away all these entries if parsing
1105 : // of the journal fails or the hash does not match.
1106 : nsTHashtable<CacheIndexEntry> mTmpJournal;
1107 :
1108 : // FrecencyArray maintains order of entry records for eviction. Ideally, the
1109 : // records would be ordered by frecency all the time, but since this would be
1110 : // quite expensive, we allow certain amount of entries to be out of order.
1111 : // When the frecency is updated the new value is always bigger than the old
1112 : // one. Instead of keeping updated entries at the same position, we move them
1113 : // at the end of the array. This protects recently updated entries from
1114 : // eviction. The array is sorted once we hit the limit of maximum unsorted
1115 : // entries.
1116 0 : class FrecencyArray
1117 : {
1118 : class Iterator
1119 : {
1120 : public:
1121 0 : explicit Iterator(nsTArray<CacheIndexRecord *> *aRecs)
1122 0 : : mRecs(aRecs)
1123 0 : , mIdx(0)
1124 : {
1125 0 : while (!Done() && !(*mRecs)[mIdx]) {
1126 0 : mIdx++;
1127 : }
1128 0 : }
1129 :
1130 0 : bool Done() const { return mIdx == mRecs->Length(); }
1131 :
1132 0 : CacheIndexRecord* Get() const
1133 : {
1134 0 : MOZ_ASSERT(!Done());
1135 0 : return (*mRecs)[mIdx];
1136 : }
1137 :
1138 0 : void Next()
1139 : {
1140 0 : MOZ_ASSERT(!Done());
1141 0 : ++mIdx;
1142 0 : while (!Done() && !(*mRecs)[mIdx]) {
1143 0 : mIdx++;
1144 : }
1145 0 : }
1146 :
1147 : private:
1148 : nsTArray<CacheIndexRecord *> *mRecs;
1149 : uint32_t mIdx;
1150 : };
1151 :
1152 : public:
1153 0 : Iterator Iter() { return Iterator(&mRecs); }
1154 :
1155 1 : FrecencyArray() : mUnsortedElements(0)
1156 1 : , mRemovedElements(0) {}
1157 :
1158 : // Methods used by CacheIndexEntryAutoManage to keep the array up to date.
1159 : void AppendRecord(CacheIndexRecord *aRecord);
1160 : void RemoveRecord(CacheIndexRecord *aRecord);
1161 : void ReplaceRecord(CacheIndexRecord *aOldRecord,
1162 : CacheIndexRecord *aNewRecord);
1163 : void SortIfNeeded();
1164 :
1165 25 : size_t Length() const { return mRecs.Length() - mRemovedElements; }
1166 0 : void Clear() { mRecs.Clear(); }
1167 :
1168 : private:
1169 : friend class CacheIndex;
1170 :
1171 : nsTArray<CacheIndexRecord *> mRecs;
1172 : uint32_t mUnsortedElements;
1173 : // Instead of removing elements from the array immediately, we null them out
1174 : // and the iterator skips them when accessing the array. The null pointers
1175 : // are placed at the end during sorting and we strip them out all at once.
1176 : // This saves moving a lot of memory in nsTArray::RemoveElementsAt.
1177 : uint32_t mRemovedElements;
1178 : };
1179 :
1180 : FrecencyArray mFrecencyArray;
1181 :
1182 : nsTArray<CacheIndexIterator *> mIterators;
1183 :
1184 : // This flag is true iff we are between CacheStorageService:Clear() and processing
1185 : // all contexts to be evicted. It will make UI to show "calculating" instead of
1186 : // any intermediate cache size.
1187 : bool mAsyncGetDiskConsumptionBlocked;
1188 :
1189 : class DiskConsumptionObserver : public Runnable
1190 : {
1191 : public:
1192 0 : static DiskConsumptionObserver* Init(nsICacheStorageConsumptionObserver* aObserver)
1193 : {
1194 0 : nsWeakPtr observer = do_GetWeakReference(aObserver);
1195 0 : if (!observer)
1196 0 : return nullptr;
1197 :
1198 0 : return new DiskConsumptionObserver(observer);
1199 : }
1200 :
1201 0 : void OnDiskConsumption(int64_t aSize)
1202 : {
1203 0 : mSize = aSize;
1204 0 : NS_DispatchToMainThread(this);
1205 0 : }
1206 :
1207 : private:
1208 0 : explicit DiskConsumptionObserver(nsWeakPtr const& aWeakObserver)
1209 0 : : Runnable("net::CacheIndex::DiskConsumptionObserver")
1210 0 : , mObserver(aWeakObserver)
1211 : {
1212 0 : }
1213 0 : virtual ~DiskConsumptionObserver() {
1214 0 : if (mObserver && !NS_IsMainThread()) {
1215 : NS_ReleaseOnMainThread(
1216 0 : "DiskConsumptionObserver::mObserver", mObserver.forget());
1217 : }
1218 0 : }
1219 :
1220 0 : NS_IMETHOD Run() override
1221 : {
1222 0 : MOZ_ASSERT(NS_IsMainThread());
1223 :
1224 : nsCOMPtr<nsICacheStorageConsumptionObserver> observer =
1225 0 : do_QueryReferent(mObserver);
1226 :
1227 0 : mObserver = nullptr;
1228 :
1229 0 : if (observer) {
1230 0 : observer->OnNetworkCacheDiskConsumption(mSize);
1231 : }
1232 :
1233 0 : return NS_OK;
1234 : }
1235 :
1236 : nsWeakPtr mObserver;
1237 : int64_t mSize;
1238 : };
1239 :
1240 : // List of async observers that want to get disk consumption information
1241 : nsTArray<RefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
1242 : };
1243 :
1244 : } // namespace net
1245 : } // namespace mozilla
1246 :
1247 : #endif
|