|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include <limits.h> |
|
8 |
|
9 #include "mozilla/DebugOnly.h" |
|
10 |
|
11 #include "nsCache.h" |
|
12 #include "nsIMemoryReporter.h" |
|
13 |
|
14 // include files for ftruncate (or equivalent) |
|
15 #if defined(XP_UNIX) |
|
16 #include <unistd.h> |
|
17 #elif defined(XP_WIN) |
|
18 #include <windows.h> |
|
19 #else |
|
20 // XXX add necessary include file for ftruncate (or equivalent) |
|
21 #endif |
|
22 |
|
23 #include "prthread.h" |
|
24 |
|
25 #include "private/pprio.h" |
|
26 |
|
27 #include "nsDiskCacheDevice.h" |
|
28 #include "nsDiskCacheEntry.h" |
|
29 #include "nsDiskCacheMap.h" |
|
30 #include "nsDiskCacheStreams.h" |
|
31 |
|
32 #include "nsDiskCache.h" |
|
33 |
|
34 #include "nsCacheService.h" |
|
35 |
|
36 #include "nsDeleteDir.h" |
|
37 |
|
38 #include "nsICacheVisitor.h" |
|
39 #include "nsReadableUtils.h" |
|
40 #include "nsIInputStream.h" |
|
41 #include "nsIOutputStream.h" |
|
42 #include "nsCRT.h" |
|
43 #include "nsCOMArray.h" |
|
44 #include "nsISimpleEnumerator.h" |
|
45 |
|
46 #include "nsThreadUtils.h" |
|
47 #include "mozilla/MemoryReporting.h" |
|
48 #include "mozilla/Telemetry.h" |
|
49 |
|
50 static const char DISK_CACHE_DEVICE_ID[] = { "disk" }; |
|
51 using namespace mozilla; |
|
52 |
|
53 class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable { |
|
54 public: |
|
55 nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device, |
|
56 nsCacheEntry * entry, |
|
57 nsDiskCacheBinding * binding) |
|
58 : mCanceled(false), |
|
59 mEntry(entry), |
|
60 mDevice(device), |
|
61 mBinding(binding) |
|
62 { |
|
63 } |
|
64 |
|
65 NS_IMETHOD Run() |
|
66 { |
|
67 nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEDEVICEDEACTIVATEENTRYEVENT_RUN)); |
|
68 #ifdef PR_LOGGING |
|
69 CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this)); |
|
70 #endif |
|
71 if (!mCanceled) { |
|
72 (void) mDevice->DeactivateEntry_Private(mEntry, mBinding); |
|
73 } |
|
74 return NS_OK; |
|
75 } |
|
76 |
|
77 void CancelEvent() { mCanceled = true; } |
|
78 private: |
|
79 bool mCanceled; |
|
80 nsCacheEntry *mEntry; |
|
81 nsDiskCacheDevice *mDevice; |
|
82 nsDiskCacheBinding *mBinding; |
|
83 }; |
|
84 |
|
85 class nsEvictDiskCacheEntriesEvent : public nsRunnable { |
|
86 public: |
|
87 nsEvictDiskCacheEntriesEvent(nsDiskCacheDevice *device) |
|
88 : mDevice(device) {} |
|
89 |
|
90 NS_IMETHOD Run() |
|
91 { |
|
92 nsCacheServiceAutoLock lock(LOCK_TELEM(NSEVICTDISKCACHEENTRIESEVENT_RUN)); |
|
93 mDevice->EvictDiskCacheEntries(mDevice->mCacheCapacity); |
|
94 return NS_OK; |
|
95 } |
|
96 |
|
97 private: |
|
98 nsDiskCacheDevice *mDevice; |
|
99 }; |
|
100 |
|
101 /****************************************************************************** |
|
102 * nsDiskCacheEvictor |
|
103 * |
|
104 * Helper class for nsDiskCacheDevice. |
|
105 * |
|
106 *****************************************************************************/ |
|
107 |
|
108 class nsDiskCacheEvictor : public nsDiskCacheRecordVisitor |
|
109 { |
|
110 public: |
|
111 nsDiskCacheEvictor( nsDiskCacheMap * cacheMap, |
|
112 nsDiskCacheBindery * cacheBindery, |
|
113 uint32_t targetSize, |
|
114 const char * clientID) |
|
115 : mCacheMap(cacheMap) |
|
116 , mBindery(cacheBindery) |
|
117 , mTargetSize(targetSize) |
|
118 , mClientID(clientID) |
|
119 { |
|
120 mClientIDSize = clientID ? strlen(clientID) : 0; |
|
121 } |
|
122 |
|
123 virtual int32_t VisitRecord(nsDiskCacheRecord * mapRecord); |
|
124 |
|
125 private: |
|
126 nsDiskCacheMap * mCacheMap; |
|
127 nsDiskCacheBindery * mBindery; |
|
128 uint32_t mTargetSize; |
|
129 const char * mClientID; |
|
130 uint32_t mClientIDSize; |
|
131 }; |
|
132 |
|
133 |
|
134 int32_t |
|
135 nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord * mapRecord) |
|
136 { |
|
137 if (mCacheMap->TotalSize() < mTargetSize) |
|
138 return kStopVisitingRecords; |
|
139 |
|
140 if (mClientID) { |
|
141 // we're just evicting records for a specific client |
|
142 nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord); |
|
143 if (!diskEntry) |
|
144 return kVisitNextRecord; // XXX or delete record? |
|
145 |
|
146 // Compare clientID's without malloc |
|
147 if ((diskEntry->mKeySize <= mClientIDSize) || |
|
148 (diskEntry->Key()[mClientIDSize] != ':') || |
|
149 (memcmp(diskEntry->Key(), mClientID, mClientIDSize) != 0)) { |
|
150 return kVisitNextRecord; // clientID doesn't match, skip it |
|
151 } |
|
152 } |
|
153 |
|
154 nsDiskCacheBinding * binding = mBindery->FindActiveBinding(mapRecord->HashNumber()); |
|
155 if (binding) { |
|
156 // If the entry is pending deactivation, cancel deactivation and doom |
|
157 // the entry |
|
158 if (binding->mDeactivateEvent) { |
|
159 binding->mDeactivateEvent->CancelEvent(); |
|
160 binding->mDeactivateEvent = nullptr; |
|
161 } |
|
162 // We are currently using this entry, so all we can do is doom it. |
|
163 // Since we're enumerating the records, we don't want to call |
|
164 // DeleteRecord when nsCacheService::DoomEntry() calls us back. |
|
165 binding->mDoomed = true; // mark binding record as 'deleted' |
|
166 nsCacheService::DoomEntry(binding->mCacheEntry); |
|
167 } else { |
|
168 // entry not in use, just delete storage because we're enumerating the records |
|
169 (void) mCacheMap->DeleteStorage(mapRecord); |
|
170 } |
|
171 |
|
172 return kDeleteRecordAndContinue; // this will REALLY delete the record |
|
173 } |
|
174 |
|
175 |
|
176 /****************************************************************************** |
|
177 * nsDiskCacheDeviceInfo |
|
178 *****************************************************************************/ |
|
179 |
|
180 class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo { |
|
181 public: |
|
182 NS_DECL_ISUPPORTS |
|
183 NS_DECL_NSICACHEDEVICEINFO |
|
184 |
|
185 nsDiskCacheDeviceInfo(nsDiskCacheDevice* device) |
|
186 : mDevice(device) |
|
187 { |
|
188 } |
|
189 |
|
190 virtual ~nsDiskCacheDeviceInfo() {} |
|
191 |
|
192 private: |
|
193 nsDiskCacheDevice* mDevice; |
|
194 }; |
|
195 |
|
196 NS_IMPL_ISUPPORTS(nsDiskCacheDeviceInfo, nsICacheDeviceInfo) |
|
197 |
|
198 /* readonly attribute string description; */ |
|
199 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetDescription(char ** aDescription) |
|
200 { |
|
201 NS_ENSURE_ARG_POINTER(aDescription); |
|
202 *aDescription = NS_strdup("Disk cache device"); |
|
203 return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY; |
|
204 } |
|
205 |
|
206 /* readonly attribute string usageReport; */ |
|
207 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport) |
|
208 { |
|
209 NS_ENSURE_ARG_POINTER(usageReport); |
|
210 nsCString buffer; |
|
211 |
|
212 buffer.AssignLiteral(" <tr>\n" |
|
213 " <th>Cache Directory:</th>\n" |
|
214 " <td>"); |
|
215 nsCOMPtr<nsIFile> cacheDir; |
|
216 nsAutoString path; |
|
217 mDevice->getCacheDirectory(getter_AddRefs(cacheDir)); |
|
218 nsresult rv = cacheDir->GetPath(path); |
|
219 if (NS_SUCCEEDED(rv)) { |
|
220 AppendUTF16toUTF8(path, buffer); |
|
221 } else { |
|
222 buffer.AppendLiteral("directory unavailable"); |
|
223 } |
|
224 buffer.AppendLiteral("</td>\n" |
|
225 " </tr>\n"); |
|
226 |
|
227 *usageReport = ToNewCString(buffer); |
|
228 if (!*usageReport) return NS_ERROR_OUT_OF_MEMORY; |
|
229 |
|
230 return NS_OK; |
|
231 } |
|
232 |
|
233 /* readonly attribute unsigned long entryCount; */ |
|
234 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetEntryCount(uint32_t *aEntryCount) |
|
235 { |
|
236 NS_ENSURE_ARG_POINTER(aEntryCount); |
|
237 *aEntryCount = mDevice->getEntryCount(); |
|
238 return NS_OK; |
|
239 } |
|
240 |
|
241 /* readonly attribute unsigned long totalSize; */ |
|
242 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetTotalSize(uint32_t *aTotalSize) |
|
243 { |
|
244 NS_ENSURE_ARG_POINTER(aTotalSize); |
|
245 // Returned unit's are in bytes |
|
246 *aTotalSize = mDevice->getCacheSize() * 1024; |
|
247 return NS_OK; |
|
248 } |
|
249 |
|
250 /* readonly attribute unsigned long maximumSize; */ |
|
251 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetMaximumSize(uint32_t *aMaximumSize) |
|
252 { |
|
253 NS_ENSURE_ARG_POINTER(aMaximumSize); |
|
254 // Returned unit's are in bytes |
|
255 *aMaximumSize = mDevice->getCacheCapacity() * 1024; |
|
256 return NS_OK; |
|
257 } |
|
258 |
|
259 |
|
260 /****************************************************************************** |
|
261 * nsDiskCache |
|
262 *****************************************************************************/ |
|
263 |
|
264 /** |
|
265 * nsDiskCache::Hash(const char * key, PLDHashNumber initval) |
|
266 * |
|
267 * See http://burtleburtle.net/bob/hash/evahash.html for more information |
|
268 * about this hash function. |
|
269 * |
|
270 * This algorithm of this method implies nsDiskCacheRecords will be stored |
|
271 * in a certain order on disk. If the algorithm changes, existing cache |
|
272 * map files may become invalid, and therefore the kCurrentVersion needs |
|
273 * to be revised. |
|
274 */ |
|
275 |
|
276 static inline void hashmix(uint32_t& a, uint32_t& b, uint32_t& c) |
|
277 { |
|
278 a -= b; a -= c; a ^= (c>>13); |
|
279 b -= c; b -= a; b ^= (a<<8); |
|
280 c -= a; c -= b; c ^= (b>>13); |
|
281 a -= b; a -= c; a ^= (c>>12); |
|
282 b -= c; b -= a; b ^= (a<<16); |
|
283 c -= a; c -= b; c ^= (b>>5); |
|
284 a -= b; a -= c; a ^= (c>>3); |
|
285 b -= c; b -= a; b ^= (a<<10); |
|
286 c -= a; c -= b; c ^= (b>>15); |
|
287 } |
|
288 |
|
289 PLDHashNumber |
|
290 nsDiskCache::Hash(const char * key, PLDHashNumber initval) |
|
291 { |
|
292 const uint8_t *k = reinterpret_cast<const uint8_t*>(key); |
|
293 uint32_t a, b, c, len, length; |
|
294 |
|
295 length = strlen(key); |
|
296 /* Set up the internal state */ |
|
297 len = length; |
|
298 a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ |
|
299 c = initval; /* variable initialization of internal state */ |
|
300 |
|
301 /*---------------------------------------- handle most of the key */ |
|
302 while (len >= 12) |
|
303 { |
|
304 a += k[0] + (uint32_t(k[1])<<8) + (uint32_t(k[2])<<16) + (uint32_t(k[3])<<24); |
|
305 b += k[4] + (uint32_t(k[5])<<8) + (uint32_t(k[6])<<16) + (uint32_t(k[7])<<24); |
|
306 c += k[8] + (uint32_t(k[9])<<8) + (uint32_t(k[10])<<16) + (uint32_t(k[11])<<24); |
|
307 hashmix(a, b, c); |
|
308 k += 12; len -= 12; |
|
309 } |
|
310 |
|
311 /*------------------------------------- handle the last 11 bytes */ |
|
312 c += length; |
|
313 switch(len) { /* all the case statements fall through */ |
|
314 case 11: c += (uint32_t(k[10])<<24); |
|
315 case 10: c += (uint32_t(k[9])<<16); |
|
316 case 9 : c += (uint32_t(k[8])<<8); |
|
317 /* the low-order byte of c is reserved for the length */ |
|
318 case 8 : b += (uint32_t(k[7])<<24); |
|
319 case 7 : b += (uint32_t(k[6])<<16); |
|
320 case 6 : b += (uint32_t(k[5])<<8); |
|
321 case 5 : b += k[4]; |
|
322 case 4 : a += (uint32_t(k[3])<<24); |
|
323 case 3 : a += (uint32_t(k[2])<<16); |
|
324 case 2 : a += (uint32_t(k[1])<<8); |
|
325 case 1 : a += k[0]; |
|
326 /* case 0: nothing left to add */ |
|
327 } |
|
328 hashmix(a, b, c); |
|
329 |
|
330 return c; |
|
331 } |
|
332 |
|
333 nsresult |
|
334 nsDiskCache::Truncate(PRFileDesc * fd, uint32_t newEOF) |
|
335 { |
|
336 // use modified SetEOF from nsFileStreams::SetEOF() |
|
337 |
|
338 #if defined(XP_UNIX) |
|
339 if (ftruncate(PR_FileDesc2NativeHandle(fd), newEOF) != 0) { |
|
340 NS_ERROR("ftruncate failed"); |
|
341 return NS_ERROR_FAILURE; |
|
342 } |
|
343 |
|
344 #elif defined(XP_WIN) |
|
345 int32_t cnt = PR_Seek(fd, newEOF, PR_SEEK_SET); |
|
346 if (cnt == -1) return NS_ERROR_FAILURE; |
|
347 if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(fd))) { |
|
348 NS_ERROR("SetEndOfFile failed"); |
|
349 return NS_ERROR_FAILURE; |
|
350 } |
|
351 |
|
352 #else |
|
353 // add implementations for other platforms here |
|
354 #endif |
|
355 return NS_OK; |
|
356 } |
|
357 |
|
358 |
|
359 /****************************************************************************** |
|
360 * nsDiskCacheDevice |
|
361 *****************************************************************************/ |
|
362 |
|
363 nsDiskCacheDevice::nsDiskCacheDevice() |
|
364 : mCacheCapacity(0) |
|
365 , mMaxEntrySize(-1) // -1 means "no limit" |
|
366 , mInitialized(false) |
|
367 , mClearingDiskCache(false) |
|
368 { |
|
369 } |
|
370 |
|
371 nsDiskCacheDevice::~nsDiskCacheDevice() |
|
372 { |
|
373 Shutdown(); |
|
374 } |
|
375 |
|
376 |
|
377 /** |
|
378 * methods of nsCacheDevice |
|
379 */ |
|
380 nsresult |
|
381 nsDiskCacheDevice::Init() |
|
382 { |
|
383 nsresult rv; |
|
384 |
|
385 if (Initialized()) { |
|
386 NS_ERROR("Disk cache already initialized!"); |
|
387 return NS_ERROR_UNEXPECTED; |
|
388 } |
|
389 |
|
390 if (!mCacheDirectory) |
|
391 return NS_ERROR_FAILURE; |
|
392 |
|
393 rv = mBindery.Init(); |
|
394 if (NS_FAILED(rv)) |
|
395 return rv; |
|
396 |
|
397 nsDeleteDir::RemoveOldTrashes(mCacheDirectory); |
|
398 |
|
399 // Open Disk Cache |
|
400 rv = OpenDiskCache(); |
|
401 if (NS_FAILED(rv)) { |
|
402 (void) mCacheMap.Close(false); |
|
403 return rv; |
|
404 } |
|
405 |
|
406 mInitialized = true; |
|
407 return NS_OK; |
|
408 } |
|
409 |
|
410 |
|
411 /** |
|
412 * NOTE: called while holding the cache service lock |
|
413 */ |
|
414 nsresult |
|
415 nsDiskCacheDevice::Shutdown() |
|
416 { |
|
417 nsCacheService::AssertOwnsLock(); |
|
418 |
|
419 nsresult rv = Shutdown_Private(true); |
|
420 if (NS_FAILED(rv)) |
|
421 return rv; |
|
422 |
|
423 return NS_OK; |
|
424 } |
|
425 |
|
426 |
|
427 nsresult |
|
428 nsDiskCacheDevice::Shutdown_Private(bool flush) |
|
429 { |
|
430 CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush)); |
|
431 |
|
432 if (Initialized()) { |
|
433 // check cache limits in case we need to evict. |
|
434 EvictDiskCacheEntries(mCacheCapacity); |
|
435 |
|
436 // At this point there may be a number of pending cache-requests on the |
|
437 // cache-io thread. Wait for all these to run before we wipe out our |
|
438 // datastructures (see bug #620660) |
|
439 (void) nsCacheService::SyncWithCacheIOThread(); |
|
440 |
|
441 // write out persistent information about the cache. |
|
442 (void) mCacheMap.Close(flush); |
|
443 |
|
444 mBindery.Reset(); |
|
445 |
|
446 mInitialized = false; |
|
447 } |
|
448 |
|
449 return NS_OK; |
|
450 } |
|
451 |
|
452 |
|
453 const char * |
|
454 nsDiskCacheDevice::GetDeviceID() |
|
455 { |
|
456 return DISK_CACHE_DEVICE_ID; |
|
457 } |
|
458 |
|
459 /** |
|
460 * FindEntry - |
|
461 * |
|
462 * cases: key not in disk cache, hash number free |
|
463 * key not in disk cache, hash number used |
|
464 * key in disk cache |
|
465 * |
|
466 * NOTE: called while holding the cache service lock |
|
467 */ |
|
468 nsCacheEntry * |
|
469 nsDiskCacheDevice::FindEntry(nsCString * key, bool *collision) |
|
470 { |
|
471 Telemetry::AutoTimer<Telemetry::CACHE_DISK_SEARCH_2> timer; |
|
472 if (!Initialized()) return nullptr; // NS_ERROR_NOT_INITIALIZED |
|
473 if (mClearingDiskCache) return nullptr; |
|
474 nsDiskCacheRecord record; |
|
475 nsDiskCacheBinding * binding = nullptr; |
|
476 PLDHashNumber hashNumber = nsDiskCache::Hash(key->get()); |
|
477 |
|
478 *collision = false; |
|
479 |
|
480 binding = mBindery.FindActiveBinding(hashNumber); |
|
481 if (binding && !binding->mCacheEntry->Key()->Equals(*key)) { |
|
482 *collision = true; |
|
483 return nullptr; |
|
484 } else if (binding && binding->mDeactivateEvent) { |
|
485 binding->mDeactivateEvent->CancelEvent(); |
|
486 binding->mDeactivateEvent = nullptr; |
|
487 CACHE_LOG_DEBUG(("CACHE: reusing deactivated entry %p " \ |
|
488 "req-key=%s entry-key=%s\n", |
|
489 binding->mCacheEntry, key, binding->mCacheEntry->Key())); |
|
490 |
|
491 return binding->mCacheEntry; // just return this one, observing that |
|
492 // FindActiveBinding() does not return |
|
493 // bindings to doomed entries |
|
494 } |
|
495 binding = nullptr; |
|
496 |
|
497 // lookup hash number in cache map |
|
498 nsresult rv = mCacheMap.FindRecord(hashNumber, &record); |
|
499 if (NS_FAILED(rv)) return nullptr; // XXX log error? |
|
500 |
|
501 nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record); |
|
502 if (!diskEntry) return nullptr; |
|
503 |
|
504 // compare key to be sure |
|
505 if (!key->Equals(diskEntry->Key())) { |
|
506 *collision = true; |
|
507 return nullptr; |
|
508 } |
|
509 |
|
510 nsCacheEntry * entry = diskEntry->CreateCacheEntry(this); |
|
511 if (entry) { |
|
512 binding = mBindery.CreateBinding(entry, &record); |
|
513 if (!binding) { |
|
514 delete entry; |
|
515 entry = nullptr; |
|
516 } |
|
517 } |
|
518 |
|
519 if (!entry) { |
|
520 (void) mCacheMap.DeleteStorage(&record); |
|
521 (void) mCacheMap.DeleteRecord(&record); |
|
522 } |
|
523 |
|
524 return entry; |
|
525 } |
|
526 |
|
527 |
|
528 /** |
|
529 * NOTE: called while holding the cache service lock |
|
530 */ |
|
531 nsresult |
|
532 nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) |
|
533 { |
|
534 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
535 if (!IsValidBinding(binding)) |
|
536 return NS_ERROR_UNEXPECTED; |
|
537 |
|
538 CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n", |
|
539 entry, binding->mRecord.HashNumber())); |
|
540 |
|
541 nsDiskCacheDeviceDeactivateEntryEvent *event = |
|
542 new nsDiskCacheDeviceDeactivateEntryEvent(this, entry, binding); |
|
543 |
|
544 // ensure we can cancel the event via the binding later if necessary |
|
545 binding->mDeactivateEvent = event; |
|
546 |
|
547 DebugOnly<nsresult> rv = nsCacheService::DispatchToCacheIOThread(event); |
|
548 NS_ASSERTION(NS_SUCCEEDED(rv), "DeactivateEntry: Failed dispatching " |
|
549 "deactivation event"); |
|
550 return NS_OK; |
|
551 } |
|
552 |
|
553 /** |
|
554 * NOTE: called while holding the cache service lock |
|
555 */ |
|
556 nsresult |
|
557 nsDiskCacheDevice::DeactivateEntry_Private(nsCacheEntry * entry, |
|
558 nsDiskCacheBinding * binding) |
|
559 { |
|
560 nsresult rv = NS_OK; |
|
561 if (entry->IsDoomed()) { |
|
562 // delete data, entry, record from disk for entry |
|
563 rv = mCacheMap.DeleteStorage(&binding->mRecord); |
|
564 |
|
565 } else { |
|
566 // save stuff to disk for entry |
|
567 rv = mCacheMap.WriteDiskCacheEntry(binding); |
|
568 if (NS_FAILED(rv)) { |
|
569 // clean up as best we can |
|
570 (void) mCacheMap.DeleteStorage(&binding->mRecord); |
|
571 (void) mCacheMap.DeleteRecord(&binding->mRecord); |
|
572 binding->mDoomed = true; // record is no longer in cache map |
|
573 } |
|
574 } |
|
575 |
|
576 mBindery.RemoveBinding(binding); // extract binding from collision detection stuff |
|
577 delete entry; // which will release binding |
|
578 return rv; |
|
579 } |
|
580 |
|
581 |
|
582 /** |
|
583 * BindEntry() |
|
584 * no hash number collision -> no problem |
|
585 * collision |
|
586 * record not active -> evict, no problem |
|
587 * record is active |
|
588 * record is already doomed -> record shouldn't have been in map, no problem |
|
589 * record is not doomed -> doom, and replace record in map |
|
590 * |
|
591 * walk matching hashnumber list to find lowest generation number |
|
592 * take generation number from other (data/meta) location, |
|
593 * or walk active list |
|
594 * |
|
595 * NOTE: called while holding the cache service lock |
|
596 */ |
|
597 nsresult |
|
598 nsDiskCacheDevice::BindEntry(nsCacheEntry * entry) |
|
599 { |
|
600 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; |
|
601 if (mClearingDiskCache) return NS_ERROR_NOT_AVAILABLE; |
|
602 nsresult rv = NS_OK; |
|
603 nsDiskCacheRecord record, oldRecord; |
|
604 nsDiskCacheBinding *binding; |
|
605 PLDHashNumber hashNumber = nsDiskCache::Hash(entry->Key()->get()); |
|
606 |
|
607 // Find out if there is already an active binding for this hash. If yes it |
|
608 // should have another key since BindEntry() shouldn't be called twice for |
|
609 // the same entry. Doom the old entry, the new one will get another |
|
610 // generation number so files won't collide. |
|
611 binding = mBindery.FindActiveBinding(hashNumber); |
|
612 if (binding) { |
|
613 NS_ASSERTION(!binding->mCacheEntry->Key()->Equals(*entry->Key()), |
|
614 "BindEntry called for already bound entry!"); |
|
615 // If the entry is pending deactivation, cancel deactivation |
|
616 if (binding->mDeactivateEvent) { |
|
617 binding->mDeactivateEvent->CancelEvent(); |
|
618 binding->mDeactivateEvent = nullptr; |
|
619 } |
|
620 nsCacheService::DoomEntry(binding->mCacheEntry); |
|
621 binding = nullptr; |
|
622 } |
|
623 |
|
624 // Lookup hash number in cache map. There can be a colliding inactive entry. |
|
625 // See bug #321361 comment 21 for the scenario. If there is such entry, |
|
626 // delete it. |
|
627 rv = mCacheMap.FindRecord(hashNumber, &record); |
|
628 if (NS_SUCCEEDED(rv)) { |
|
629 nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record); |
|
630 if (diskEntry) { |
|
631 // compare key to be sure |
|
632 if (!entry->Key()->Equals(diskEntry->Key())) { |
|
633 mCacheMap.DeleteStorage(&record); |
|
634 rv = mCacheMap.DeleteRecord(&record); |
|
635 if (NS_FAILED(rv)) return rv; |
|
636 } |
|
637 } |
|
638 record = nsDiskCacheRecord(); |
|
639 } |
|
640 |
|
641 // create a new record for this entry |
|
642 record.SetHashNumber(nsDiskCache::Hash(entry->Key()->get())); |
|
643 record.SetEvictionRank(ULONG_MAX - SecondsFromPRTime(PR_Now())); |
|
644 |
|
645 CACHE_LOG_DEBUG(("CACHE: disk BindEntry [%p %x]\n", |
|
646 entry, record.HashNumber())); |
|
647 |
|
648 if (!entry->IsDoomed()) { |
|
649 // if entry isn't doomed, add it to the cache map |
|
650 rv = mCacheMap.AddRecord(&record, &oldRecord); // deletes old record, if any |
|
651 if (NS_FAILED(rv)) return rv; |
|
652 |
|
653 uint32_t oldHashNumber = oldRecord.HashNumber(); |
|
654 if (oldHashNumber) { |
|
655 // gotta evict this one first |
|
656 nsDiskCacheBinding * oldBinding = mBindery.FindActiveBinding(oldHashNumber); |
|
657 if (oldBinding) { |
|
658 // XXX if debug : compare keys for hashNumber collision |
|
659 |
|
660 if (!oldBinding->mCacheEntry->IsDoomed()) { |
|
661 // If the old entry is pending deactivation, cancel deactivation |
|
662 if (oldBinding->mDeactivateEvent) { |
|
663 oldBinding->mDeactivateEvent->CancelEvent(); |
|
664 oldBinding->mDeactivateEvent = nullptr; |
|
665 } |
|
666 // we've got a live one! |
|
667 nsCacheService::DoomEntry(oldBinding->mCacheEntry); |
|
668 // storage will be delete when oldBinding->mCacheEntry is Deactivated |
|
669 } |
|
670 } else { |
|
671 // delete storage |
|
672 // XXX if debug : compare keys for hashNumber collision |
|
673 rv = mCacheMap.DeleteStorage(&oldRecord); |
|
674 if (NS_FAILED(rv)) return rv; // XXX delete record we just added? |
|
675 } |
|
676 } |
|
677 } |
|
678 |
|
679 // Make sure this entry has its associated nsDiskCacheBinding attached. |
|
680 binding = mBindery.CreateBinding(entry, &record); |
|
681 NS_ASSERTION(binding, "nsDiskCacheDevice::BindEntry"); |
|
682 if (!binding) return NS_ERROR_OUT_OF_MEMORY; |
|
683 NS_ASSERTION(binding->mRecord.ValidRecord(), "bad cache map record"); |
|
684 |
|
685 return NS_OK; |
|
686 } |
|
687 |
|
688 |
|
689 /** |
|
690 * NOTE: called while holding the cache service lock |
|
691 */ |
|
692 void |
|
693 nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry) |
|
694 { |
|
695 CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry)); |
|
696 |
|
697 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
698 NS_ASSERTION(binding, "DoomEntry: binding == nullptr"); |
|
699 if (!binding) |
|
700 return; |
|
701 |
|
702 if (!binding->mDoomed) { |
|
703 // so it can't be seen by FindEntry() ever again. |
|
704 #ifdef DEBUG |
|
705 nsresult rv = |
|
706 #endif |
|
707 mCacheMap.DeleteRecord(&binding->mRecord); |
|
708 NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed."); |
|
709 binding->mDoomed = true; // record in no longer in cache map |
|
710 } |
|
711 } |
|
712 |
|
713 |
|
714 /** |
|
715 * NOTE: called while holding the cache service lock |
|
716 */ |
|
717 nsresult |
|
718 nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry * entry, |
|
719 nsCacheAccessMode mode, |
|
720 uint32_t offset, |
|
721 nsIInputStream ** result) |
|
722 { |
|
723 CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n", |
|
724 entry, mode, offset)); |
|
725 |
|
726 NS_ENSURE_ARG_POINTER(entry); |
|
727 NS_ENSURE_ARG_POINTER(result); |
|
728 |
|
729 nsresult rv; |
|
730 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
731 if (!IsValidBinding(binding)) |
|
732 return NS_ERROR_UNEXPECTED; |
|
733 |
|
734 NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other"); |
|
735 |
|
736 rv = binding->EnsureStreamIO(); |
|
737 if (NS_FAILED(rv)) return rv; |
|
738 |
|
739 return binding->mStreamIO->GetInputStream(offset, result); |
|
740 } |
|
741 |
|
742 |
|
743 /** |
|
744 * NOTE: called while holding the cache service lock |
|
745 */ |
|
746 nsresult |
|
747 nsDiskCacheDevice::OpenOutputStreamForEntry(nsCacheEntry * entry, |
|
748 nsCacheAccessMode mode, |
|
749 uint32_t offset, |
|
750 nsIOutputStream ** result) |
|
751 { |
|
752 CACHE_LOG_DEBUG(("CACHE: disk OpenOutputStreamForEntry [%p %x %u]\n", |
|
753 entry, mode, offset)); |
|
754 |
|
755 NS_ENSURE_ARG_POINTER(entry); |
|
756 NS_ENSURE_ARG_POINTER(result); |
|
757 |
|
758 nsresult rv; |
|
759 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
760 if (!IsValidBinding(binding)) |
|
761 return NS_ERROR_UNEXPECTED; |
|
762 |
|
763 NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other"); |
|
764 |
|
765 rv = binding->EnsureStreamIO(); |
|
766 if (NS_FAILED(rv)) return rv; |
|
767 |
|
768 return binding->mStreamIO->GetOutputStream(offset, result); |
|
769 } |
|
770 |
|
771 |
|
772 /** |
|
773 * NOTE: called while holding the cache service lock |
|
774 */ |
|
775 nsresult |
|
776 nsDiskCacheDevice::GetFileForEntry(nsCacheEntry * entry, |
|
777 nsIFile ** result) |
|
778 { |
|
779 NS_ENSURE_ARG_POINTER(result); |
|
780 *result = nullptr; |
|
781 |
|
782 nsresult rv; |
|
783 |
|
784 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
785 if (!IsValidBinding(binding)) |
|
786 return NS_ERROR_UNEXPECTED; |
|
787 |
|
788 // check/set binding->mRecord for separate file, sync w/mCacheMap |
|
789 if (binding->mRecord.DataLocationInitialized()) { |
|
790 if (binding->mRecord.DataFile() != 0) |
|
791 return NS_ERROR_NOT_AVAILABLE; // data not stored as separate file |
|
792 |
|
793 NS_ASSERTION(binding->mRecord.DataFileGeneration() == binding->mGeneration, "error generations out of sync"); |
|
794 } else { |
|
795 binding->mRecord.SetDataFileGeneration(binding->mGeneration); |
|
796 binding->mRecord.SetDataFileSize(0); // 1k minimum |
|
797 if (!binding->mDoomed) { |
|
798 // record stored in cache map, so update it |
|
799 rv = mCacheMap.UpdateRecord(&binding->mRecord); |
|
800 if (NS_FAILED(rv)) return rv; |
|
801 } |
|
802 } |
|
803 |
|
804 nsCOMPtr<nsIFile> file; |
|
805 rv = mCacheMap.GetFileForDiskCacheRecord(&binding->mRecord, |
|
806 nsDiskCache::kData, |
|
807 false, |
|
808 getter_AddRefs(file)); |
|
809 if (NS_FAILED(rv)) return rv; |
|
810 |
|
811 NS_IF_ADDREF(*result = file); |
|
812 return NS_OK; |
|
813 } |
|
814 |
|
815 |
|
816 /** |
|
817 * This routine will get called every time an open descriptor is written to. |
|
818 * |
|
819 * NOTE: called while holding the cache service lock |
|
820 */ |
|
821 nsresult |
|
822 nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize) |
|
823 { |
|
824 CACHE_LOG_DEBUG(("CACHE: disk OnDataSizeChange [%p %d]\n", |
|
825 entry, deltaSize)); |
|
826 |
|
827 // If passed a negative value, then there's nothing to do. |
|
828 if (deltaSize < 0) |
|
829 return NS_OK; |
|
830 |
|
831 nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); |
|
832 if (!IsValidBinding(binding)) |
|
833 return NS_ERROR_UNEXPECTED; |
|
834 |
|
835 NS_ASSERTION(binding->mRecord.ValidRecord(), "bad record"); |
|
836 |
|
837 uint32_t newSize = entry->DataSize() + deltaSize; |
|
838 uint32_t newSizeK = ((newSize + 0x3FF) >> 10); |
|
839 |
|
840 // If the new size is larger than max. file size or larger than |
|
841 // 1/8 the cache capacity (which is in KiB's), doom the entry and abort. |
|
842 if (EntryIsTooBig(newSize)) { |
|
843 #ifdef DEBUG |
|
844 nsresult rv = |
|
845 #endif |
|
846 nsCacheService::DoomEntry(entry); |
|
847 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); |
|
848 return NS_ERROR_ABORT; |
|
849 } |
|
850 |
|
851 uint32_t sizeK = ((entry->DataSize() + 0x03FF) >> 10); // round up to next 1k |
|
852 |
|
853 // In total count we ignore anything over kMaxDataSizeK (bug #651100), so |
|
854 // the target capacity should be calculated the same way. |
|
855 if (sizeK > kMaxDataSizeK) sizeK = kMaxDataSizeK; |
|
856 if (newSizeK > kMaxDataSizeK) newSizeK = kMaxDataSizeK; |
|
857 |
|
858 // pre-evict entries to make space for new data |
|
859 uint32_t targetCapacity = mCacheCapacity > (newSizeK - sizeK) |
|
860 ? mCacheCapacity - (newSizeK - sizeK) |
|
861 : 0; |
|
862 EvictDiskCacheEntries(targetCapacity); |
|
863 |
|
864 return NS_OK; |
|
865 } |
|
866 |
|
867 |
|
868 /****************************************************************************** |
|
869 * EntryInfoVisitor |
|
870 *****************************************************************************/ |
|
871 class EntryInfoVisitor : public nsDiskCacheRecordVisitor |
|
872 { |
|
873 public: |
|
874 EntryInfoVisitor(nsDiskCacheMap * cacheMap, |
|
875 nsICacheVisitor * visitor) |
|
876 : mCacheMap(cacheMap) |
|
877 , mVisitor(visitor) |
|
878 {} |
|
879 |
|
880 virtual int32_t VisitRecord(nsDiskCacheRecord * mapRecord) |
|
881 { |
|
882 // XXX optimization: do we have this record in memory? |
|
883 |
|
884 // read in the entry (metadata) |
|
885 nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord); |
|
886 if (!diskEntry) { |
|
887 return kVisitNextRecord; |
|
888 } |
|
889 |
|
890 // create nsICacheEntryInfo |
|
891 nsDiskCacheEntryInfo * entryInfo = new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID, diskEntry); |
|
892 if (!entryInfo) { |
|
893 return kStopVisitingRecords; |
|
894 } |
|
895 nsCOMPtr<nsICacheEntryInfo> ref(entryInfo); |
|
896 |
|
897 bool keepGoing; |
|
898 (void)mVisitor->VisitEntry(DISK_CACHE_DEVICE_ID, entryInfo, &keepGoing); |
|
899 return keepGoing ? kVisitNextRecord : kStopVisitingRecords; |
|
900 } |
|
901 |
|
902 private: |
|
903 nsDiskCacheMap * mCacheMap; |
|
904 nsICacheVisitor * mVisitor; |
|
905 }; |
|
906 |
|
907 |
|
908 nsresult |
|
909 nsDiskCacheDevice::Visit(nsICacheVisitor * visitor) |
|
910 { |
|
911 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; |
|
912 nsDiskCacheDeviceInfo* deviceInfo = new nsDiskCacheDeviceInfo(this); |
|
913 nsCOMPtr<nsICacheDeviceInfo> ref(deviceInfo); |
|
914 |
|
915 bool keepGoing; |
|
916 nsresult rv = visitor->VisitDevice(DISK_CACHE_DEVICE_ID, deviceInfo, &keepGoing); |
|
917 if (NS_FAILED(rv)) return rv; |
|
918 |
|
919 if (keepGoing) { |
|
920 EntryInfoVisitor infoVisitor(&mCacheMap, visitor); |
|
921 return mCacheMap.VisitRecords(&infoVisitor); |
|
922 } |
|
923 |
|
924 return NS_OK; |
|
925 } |
|
926 |
|
927 // Max allowed size for an entry is currently MIN(mMaxEntrySize, 1/8 CacheCapacity) |
|
928 bool |
|
929 nsDiskCacheDevice::EntryIsTooBig(int64_t entrySize) |
|
930 { |
|
931 if (mMaxEntrySize == -1) // no limit |
|
932 return entrySize > (static_cast<int64_t>(mCacheCapacity) * 1024 / 8); |
|
933 else |
|
934 return entrySize > mMaxEntrySize || |
|
935 entrySize > (static_cast<int64_t>(mCacheCapacity) * 1024 / 8); |
|
936 } |
|
937 |
|
938 nsresult |
|
939 nsDiskCacheDevice::EvictEntries(const char * clientID) |
|
940 { |
|
941 CACHE_LOG_DEBUG(("CACHE: disk EvictEntries [%s]\n", clientID)); |
|
942 |
|
943 if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; |
|
944 nsresult rv; |
|
945 |
|
946 if (clientID == nullptr) { |
|
947 // we're clearing the entire disk cache |
|
948 rv = ClearDiskCache(); |
|
949 if (rv != NS_ERROR_CACHE_IN_USE) |
|
950 return rv; |
|
951 } |
|
952 |
|
953 nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, 0, clientID); |
|
954 rv = mCacheMap.VisitRecords(&evictor); |
|
955 |
|
956 if (clientID == nullptr) // we tried to clear the entire cache |
|
957 rv = mCacheMap.Trim(); // so trim cache block files (if possible) |
|
958 return rv; |
|
959 } |
|
960 |
|
961 |
|
962 /** |
|
963 * private methods |
|
964 */ |
|
965 |
|
966 nsresult |
|
967 nsDiskCacheDevice::OpenDiskCache() |
|
968 { |
|
969 Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_OPEN> timer; |
|
970 // if we don't have a cache directory, create one and open it |
|
971 bool exists; |
|
972 nsresult rv = mCacheDirectory->Exists(&exists); |
|
973 if (NS_FAILED(rv)) |
|
974 return rv; |
|
975 |
|
976 if (exists) { |
|
977 // Try opening cache map file. |
|
978 nsDiskCache::CorruptCacheInfo corruptInfo; |
|
979 rv = mCacheMap.Open(mCacheDirectory, &corruptInfo, true); |
|
980 |
|
981 if (NS_SUCCEEDED(rv)) { |
|
982 Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS, |
|
983 corruptInfo); |
|
984 } else if (rv == NS_ERROR_ALREADY_INITIALIZED) { |
|
985 NS_WARNING("nsDiskCacheDevice::OpenDiskCache: already open!"); |
|
986 } else { |
|
987 // Consider cache corrupt: delete it |
|
988 Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS, |
|
989 corruptInfo); |
|
990 // delay delete by 1 minute to avoid IO thrash at startup |
|
991 rv = nsDeleteDir::DeleteDir(mCacheDirectory, true, 60000); |
|
992 if (NS_FAILED(rv)) |
|
993 return rv; |
|
994 exists = false; |
|
995 } |
|
996 } |
|
997 |
|
998 // if we don't have a cache directory, create one and open it |
|
999 if (!exists) { |
|
1000 nsCacheService::MarkStartingFresh(); |
|
1001 rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777); |
|
1002 CACHE_LOG_PATH(PR_LOG_ALWAYS, "\ncreate cache directory: %s\n", mCacheDirectory); |
|
1003 CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv)); |
|
1004 if (NS_FAILED(rv)) |
|
1005 return rv; |
|
1006 |
|
1007 // reopen the cache map |
|
1008 nsDiskCache::CorruptCacheInfo corruptInfo; |
|
1009 rv = mCacheMap.Open(mCacheDirectory, &corruptInfo, false); |
|
1010 if (NS_FAILED(rv)) |
|
1011 return rv; |
|
1012 } |
|
1013 |
|
1014 return NS_OK; |
|
1015 } |
|
1016 |
|
1017 |
|
1018 nsresult |
|
1019 nsDiskCacheDevice::ClearDiskCache() |
|
1020 { |
|
1021 if (mBindery.ActiveBindings()) |
|
1022 return NS_ERROR_CACHE_IN_USE; |
|
1023 |
|
1024 mClearingDiskCache = true; |
|
1025 |
|
1026 nsresult rv = Shutdown_Private(false); // false: don't bother flushing |
|
1027 if (NS_FAILED(rv)) |
|
1028 return rv; |
|
1029 |
|
1030 mClearingDiskCache = false; |
|
1031 |
|
1032 // If the disk cache directory is already gone, then it's not an error if |
|
1033 // we fail to delete it ;-) |
|
1034 rv = nsDeleteDir::DeleteDir(mCacheDirectory, true); |
|
1035 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) |
|
1036 return rv; |
|
1037 |
|
1038 return Init(); |
|
1039 } |
|
1040 |
|
1041 |
|
1042 nsresult |
|
1043 nsDiskCacheDevice::EvictDiskCacheEntries(uint32_t targetCapacity) |
|
1044 { |
|
1045 CACHE_LOG_DEBUG(("CACHE: disk EvictDiskCacheEntries [%u]\n", |
|
1046 targetCapacity)); |
|
1047 |
|
1048 NS_ASSERTION(targetCapacity > 0, "oops"); |
|
1049 |
|
1050 if (mCacheMap.TotalSize() < targetCapacity) |
|
1051 return NS_OK; |
|
1052 |
|
1053 // targetCapacity is in KiB's |
|
1054 nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, targetCapacity, nullptr); |
|
1055 return mCacheMap.EvictRecords(&evictor); |
|
1056 } |
|
1057 |
|
1058 |
|
1059 /** |
|
1060 * methods for prefs |
|
1061 */ |
|
1062 |
|
1063 void |
|
1064 nsDiskCacheDevice::SetCacheParentDirectory(nsIFile * parentDir) |
|
1065 { |
|
1066 nsresult rv; |
|
1067 bool exists; |
|
1068 |
|
1069 if (Initialized()) { |
|
1070 NS_ASSERTION(false, "Cannot switch cache directory when initialized"); |
|
1071 return; |
|
1072 } |
|
1073 |
|
1074 if (!parentDir) { |
|
1075 mCacheDirectory = nullptr; |
|
1076 return; |
|
1077 } |
|
1078 |
|
1079 // ensure parent directory exists |
|
1080 rv = parentDir->Exists(&exists); |
|
1081 if (NS_SUCCEEDED(rv) && !exists) |
|
1082 rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700); |
|
1083 if (NS_FAILED(rv)) return; |
|
1084 |
|
1085 // ensure cache directory exists |
|
1086 nsCOMPtr<nsIFile> directory; |
|
1087 |
|
1088 rv = parentDir->Clone(getter_AddRefs(directory)); |
|
1089 if (NS_FAILED(rv)) return; |
|
1090 rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache")); |
|
1091 if (NS_FAILED(rv)) return; |
|
1092 |
|
1093 mCacheDirectory = do_QueryInterface(directory); |
|
1094 } |
|
1095 |
|
1096 |
|
1097 void |
|
1098 nsDiskCacheDevice::getCacheDirectory(nsIFile ** result) |
|
1099 { |
|
1100 *result = mCacheDirectory; |
|
1101 NS_IF_ADDREF(*result); |
|
1102 } |
|
1103 |
|
1104 |
|
1105 /** |
|
1106 * NOTE: called while holding the cache service lock |
|
1107 */ |
|
1108 void |
|
1109 nsDiskCacheDevice::SetCapacity(uint32_t capacity) |
|
1110 { |
|
1111 // Units are KiB's |
|
1112 mCacheCapacity = capacity; |
|
1113 if (Initialized()) { |
|
1114 if (NS_IsMainThread()) { |
|
1115 // Do not evict entries on the main thread |
|
1116 nsCacheService::DispatchToCacheIOThread( |
|
1117 new nsEvictDiskCacheEntriesEvent(this)); |
|
1118 } else { |
|
1119 // start evicting entries if the new size is smaller! |
|
1120 EvictDiskCacheEntries(mCacheCapacity); |
|
1121 } |
|
1122 } |
|
1123 // Let cache map know of the new capacity |
|
1124 mCacheMap.NotifyCapacityChange(capacity); |
|
1125 } |
|
1126 |
|
1127 |
|
1128 uint32_t nsDiskCacheDevice::getCacheCapacity() |
|
1129 { |
|
1130 return mCacheCapacity; |
|
1131 } |
|
1132 |
|
1133 |
|
1134 uint32_t nsDiskCacheDevice::getCacheSize() |
|
1135 { |
|
1136 return mCacheMap.TotalSize(); |
|
1137 } |
|
1138 |
|
1139 |
|
1140 uint32_t nsDiskCacheDevice::getEntryCount() |
|
1141 { |
|
1142 return mCacheMap.EntryCount(); |
|
1143 } |
|
1144 |
|
1145 void |
|
1146 nsDiskCacheDevice::SetMaxEntrySize(int32_t maxSizeInKilobytes) |
|
1147 { |
|
1148 // Internal units are bytes. Changing this only takes effect *after* the |
|
1149 // change and has no consequences for existing cache-entries |
|
1150 if (maxSizeInKilobytes >= 0) |
|
1151 mMaxEntrySize = maxSizeInKilobytes * 1024; |
|
1152 else |
|
1153 mMaxEntrySize = -1; |
|
1154 } |
|
1155 |
|
1156 size_t |
|
1157 nsDiskCacheDevice::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) |
|
1158 { |
|
1159 size_t usage = aMallocSizeOf(this); |
|
1160 |
|
1161 usage += mCacheMap.SizeOfExcludingThis(aMallocSizeOf); |
|
1162 usage += mBindery.SizeOfExcludingThis(aMallocSizeOf); |
|
1163 |
|
1164 return usage; |
|
1165 } |
|
1166 |