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