1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsDiskCacheDevice.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1166 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <limits.h> 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 + 1.14 +#include "nsCache.h" 1.15 +#include "nsIMemoryReporter.h" 1.16 + 1.17 +// include files for ftruncate (or equivalent) 1.18 +#if defined(XP_UNIX) 1.19 +#include <unistd.h> 1.20 +#elif defined(XP_WIN) 1.21 +#include <windows.h> 1.22 +#else 1.23 +// XXX add necessary include file for ftruncate (or equivalent) 1.24 +#endif 1.25 + 1.26 +#include "prthread.h" 1.27 + 1.28 +#include "private/pprio.h" 1.29 + 1.30 +#include "nsDiskCacheDevice.h" 1.31 +#include "nsDiskCacheEntry.h" 1.32 +#include "nsDiskCacheMap.h" 1.33 +#include "nsDiskCacheStreams.h" 1.34 + 1.35 +#include "nsDiskCache.h" 1.36 + 1.37 +#include "nsCacheService.h" 1.38 + 1.39 +#include "nsDeleteDir.h" 1.40 + 1.41 +#include "nsICacheVisitor.h" 1.42 +#include "nsReadableUtils.h" 1.43 +#include "nsIInputStream.h" 1.44 +#include "nsIOutputStream.h" 1.45 +#include "nsCRT.h" 1.46 +#include "nsCOMArray.h" 1.47 +#include "nsISimpleEnumerator.h" 1.48 + 1.49 +#include "nsThreadUtils.h" 1.50 +#include "mozilla/MemoryReporting.h" 1.51 +#include "mozilla/Telemetry.h" 1.52 + 1.53 +static const char DISK_CACHE_DEVICE_ID[] = { "disk" }; 1.54 +using namespace mozilla; 1.55 + 1.56 +class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable { 1.57 +public: 1.58 + nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device, 1.59 + nsCacheEntry * entry, 1.60 + nsDiskCacheBinding * binding) 1.61 + : mCanceled(false), 1.62 + mEntry(entry), 1.63 + mDevice(device), 1.64 + mBinding(binding) 1.65 + { 1.66 + } 1.67 + 1.68 + NS_IMETHOD Run() 1.69 + { 1.70 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSDISKCACHEDEVICEDEACTIVATEENTRYEVENT_RUN)); 1.71 +#ifdef PR_LOGGING 1.72 + CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this)); 1.73 +#endif 1.74 + if (!mCanceled) { 1.75 + (void) mDevice->DeactivateEntry_Private(mEntry, mBinding); 1.76 + } 1.77 + return NS_OK; 1.78 + } 1.79 + 1.80 + void CancelEvent() { mCanceled = true; } 1.81 +private: 1.82 + bool mCanceled; 1.83 + nsCacheEntry *mEntry; 1.84 + nsDiskCacheDevice *mDevice; 1.85 + nsDiskCacheBinding *mBinding; 1.86 +}; 1.87 + 1.88 +class nsEvictDiskCacheEntriesEvent : public nsRunnable { 1.89 +public: 1.90 + nsEvictDiskCacheEntriesEvent(nsDiskCacheDevice *device) 1.91 + : mDevice(device) {} 1.92 + 1.93 + NS_IMETHOD Run() 1.94 + { 1.95 + nsCacheServiceAutoLock lock(LOCK_TELEM(NSEVICTDISKCACHEENTRIESEVENT_RUN)); 1.96 + mDevice->EvictDiskCacheEntries(mDevice->mCacheCapacity); 1.97 + return NS_OK; 1.98 + } 1.99 + 1.100 +private: 1.101 + nsDiskCacheDevice *mDevice; 1.102 +}; 1.103 + 1.104 +/****************************************************************************** 1.105 + * nsDiskCacheEvictor 1.106 + * 1.107 + * Helper class for nsDiskCacheDevice. 1.108 + * 1.109 + *****************************************************************************/ 1.110 + 1.111 +class nsDiskCacheEvictor : public nsDiskCacheRecordVisitor 1.112 +{ 1.113 +public: 1.114 + nsDiskCacheEvictor( nsDiskCacheMap * cacheMap, 1.115 + nsDiskCacheBindery * cacheBindery, 1.116 + uint32_t targetSize, 1.117 + const char * clientID) 1.118 + : mCacheMap(cacheMap) 1.119 + , mBindery(cacheBindery) 1.120 + , mTargetSize(targetSize) 1.121 + , mClientID(clientID) 1.122 + { 1.123 + mClientIDSize = clientID ? strlen(clientID) : 0; 1.124 + } 1.125 + 1.126 + virtual int32_t VisitRecord(nsDiskCacheRecord * mapRecord); 1.127 + 1.128 +private: 1.129 + nsDiskCacheMap * mCacheMap; 1.130 + nsDiskCacheBindery * mBindery; 1.131 + uint32_t mTargetSize; 1.132 + const char * mClientID; 1.133 + uint32_t mClientIDSize; 1.134 +}; 1.135 + 1.136 + 1.137 +int32_t 1.138 +nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord * mapRecord) 1.139 +{ 1.140 + if (mCacheMap->TotalSize() < mTargetSize) 1.141 + return kStopVisitingRecords; 1.142 + 1.143 + if (mClientID) { 1.144 + // we're just evicting records for a specific client 1.145 + nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord); 1.146 + if (!diskEntry) 1.147 + return kVisitNextRecord; // XXX or delete record? 1.148 + 1.149 + // Compare clientID's without malloc 1.150 + if ((diskEntry->mKeySize <= mClientIDSize) || 1.151 + (diskEntry->Key()[mClientIDSize] != ':') || 1.152 + (memcmp(diskEntry->Key(), mClientID, mClientIDSize) != 0)) { 1.153 + return kVisitNextRecord; // clientID doesn't match, skip it 1.154 + } 1.155 + } 1.156 + 1.157 + nsDiskCacheBinding * binding = mBindery->FindActiveBinding(mapRecord->HashNumber()); 1.158 + if (binding) { 1.159 + // If the entry is pending deactivation, cancel deactivation and doom 1.160 + // the entry 1.161 + if (binding->mDeactivateEvent) { 1.162 + binding->mDeactivateEvent->CancelEvent(); 1.163 + binding->mDeactivateEvent = nullptr; 1.164 + } 1.165 + // We are currently using this entry, so all we can do is doom it. 1.166 + // Since we're enumerating the records, we don't want to call 1.167 + // DeleteRecord when nsCacheService::DoomEntry() calls us back. 1.168 + binding->mDoomed = true; // mark binding record as 'deleted' 1.169 + nsCacheService::DoomEntry(binding->mCacheEntry); 1.170 + } else { 1.171 + // entry not in use, just delete storage because we're enumerating the records 1.172 + (void) mCacheMap->DeleteStorage(mapRecord); 1.173 + } 1.174 + 1.175 + return kDeleteRecordAndContinue; // this will REALLY delete the record 1.176 +} 1.177 + 1.178 + 1.179 +/****************************************************************************** 1.180 + * nsDiskCacheDeviceInfo 1.181 + *****************************************************************************/ 1.182 + 1.183 +class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo { 1.184 +public: 1.185 + NS_DECL_ISUPPORTS 1.186 + NS_DECL_NSICACHEDEVICEINFO 1.187 + 1.188 + nsDiskCacheDeviceInfo(nsDiskCacheDevice* device) 1.189 + : mDevice(device) 1.190 + { 1.191 + } 1.192 + 1.193 + virtual ~nsDiskCacheDeviceInfo() {} 1.194 + 1.195 +private: 1.196 + nsDiskCacheDevice* mDevice; 1.197 +}; 1.198 + 1.199 +NS_IMPL_ISUPPORTS(nsDiskCacheDeviceInfo, nsICacheDeviceInfo) 1.200 + 1.201 +/* readonly attribute string description; */ 1.202 +NS_IMETHODIMP nsDiskCacheDeviceInfo::GetDescription(char ** aDescription) 1.203 +{ 1.204 + NS_ENSURE_ARG_POINTER(aDescription); 1.205 + *aDescription = NS_strdup("Disk cache device"); 1.206 + return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.207 +} 1.208 + 1.209 +/* readonly attribute string usageReport; */ 1.210 +NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport) 1.211 +{ 1.212 + NS_ENSURE_ARG_POINTER(usageReport); 1.213 + nsCString buffer; 1.214 + 1.215 + buffer.AssignLiteral(" <tr>\n" 1.216 + " <th>Cache Directory:</th>\n" 1.217 + " <td>"); 1.218 + nsCOMPtr<nsIFile> cacheDir; 1.219 + nsAutoString path; 1.220 + mDevice->getCacheDirectory(getter_AddRefs(cacheDir)); 1.221 + nsresult rv = cacheDir->GetPath(path); 1.222 + if (NS_SUCCEEDED(rv)) { 1.223 + AppendUTF16toUTF8(path, buffer); 1.224 + } else { 1.225 + buffer.AppendLiteral("directory unavailable"); 1.226 + } 1.227 + buffer.AppendLiteral("</td>\n" 1.228 + " </tr>\n"); 1.229 + 1.230 + *usageReport = ToNewCString(buffer); 1.231 + if (!*usageReport) return NS_ERROR_OUT_OF_MEMORY; 1.232 + 1.233 + return NS_OK; 1.234 +} 1.235 + 1.236 +/* readonly attribute unsigned long entryCount; */ 1.237 +NS_IMETHODIMP nsDiskCacheDeviceInfo::GetEntryCount(uint32_t *aEntryCount) 1.238 +{ 1.239 + NS_ENSURE_ARG_POINTER(aEntryCount); 1.240 + *aEntryCount = mDevice->getEntryCount(); 1.241 + return NS_OK; 1.242 +} 1.243 + 1.244 +/* readonly attribute unsigned long totalSize; */ 1.245 +NS_IMETHODIMP nsDiskCacheDeviceInfo::GetTotalSize(uint32_t *aTotalSize) 1.246 +{ 1.247 + NS_ENSURE_ARG_POINTER(aTotalSize); 1.248 + // Returned unit's are in bytes 1.249 + *aTotalSize = mDevice->getCacheSize() * 1024; 1.250 + return NS_OK; 1.251 +} 1.252 + 1.253 +/* readonly attribute unsigned long maximumSize; */ 1.254 +NS_IMETHODIMP nsDiskCacheDeviceInfo::GetMaximumSize(uint32_t *aMaximumSize) 1.255 +{ 1.256 + NS_ENSURE_ARG_POINTER(aMaximumSize); 1.257 + // Returned unit's are in bytes 1.258 + *aMaximumSize = mDevice->getCacheCapacity() * 1024; 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 + 1.263 +/****************************************************************************** 1.264 + * nsDiskCache 1.265 + *****************************************************************************/ 1.266 + 1.267 +/** 1.268 + * nsDiskCache::Hash(const char * key, PLDHashNumber initval) 1.269 + * 1.270 + * See http://burtleburtle.net/bob/hash/evahash.html for more information 1.271 + * about this hash function. 1.272 + * 1.273 + * This algorithm of this method implies nsDiskCacheRecords will be stored 1.274 + * in a certain order on disk. If the algorithm changes, existing cache 1.275 + * map files may become invalid, and therefore the kCurrentVersion needs 1.276 + * to be revised. 1.277 + */ 1.278 + 1.279 +static inline void hashmix(uint32_t& a, uint32_t& b, uint32_t& c) 1.280 +{ 1.281 + a -= b; a -= c; a ^= (c>>13); 1.282 + b -= c; b -= a; b ^= (a<<8); 1.283 + c -= a; c -= b; c ^= (b>>13); 1.284 + a -= b; a -= c; a ^= (c>>12); 1.285 + b -= c; b -= a; b ^= (a<<16); 1.286 + c -= a; c -= b; c ^= (b>>5); 1.287 + a -= b; a -= c; a ^= (c>>3); 1.288 + b -= c; b -= a; b ^= (a<<10); 1.289 + c -= a; c -= b; c ^= (b>>15); 1.290 +} 1.291 + 1.292 +PLDHashNumber 1.293 +nsDiskCache::Hash(const char * key, PLDHashNumber initval) 1.294 +{ 1.295 + const uint8_t *k = reinterpret_cast<const uint8_t*>(key); 1.296 + uint32_t a, b, c, len, length; 1.297 + 1.298 + length = strlen(key); 1.299 + /* Set up the internal state */ 1.300 + len = length; 1.301 + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ 1.302 + c = initval; /* variable initialization of internal state */ 1.303 + 1.304 + /*---------------------------------------- handle most of the key */ 1.305 + while (len >= 12) 1.306 + { 1.307 + a += k[0] + (uint32_t(k[1])<<8) + (uint32_t(k[2])<<16) + (uint32_t(k[3])<<24); 1.308 + b += k[4] + (uint32_t(k[5])<<8) + (uint32_t(k[6])<<16) + (uint32_t(k[7])<<24); 1.309 + c += k[8] + (uint32_t(k[9])<<8) + (uint32_t(k[10])<<16) + (uint32_t(k[11])<<24); 1.310 + hashmix(a, b, c); 1.311 + k += 12; len -= 12; 1.312 + } 1.313 + 1.314 + /*------------------------------------- handle the last 11 bytes */ 1.315 + c += length; 1.316 + switch(len) { /* all the case statements fall through */ 1.317 + case 11: c += (uint32_t(k[10])<<24); 1.318 + case 10: c += (uint32_t(k[9])<<16); 1.319 + case 9 : c += (uint32_t(k[8])<<8); 1.320 + /* the low-order byte of c is reserved for the length */ 1.321 + case 8 : b += (uint32_t(k[7])<<24); 1.322 + case 7 : b += (uint32_t(k[6])<<16); 1.323 + case 6 : b += (uint32_t(k[5])<<8); 1.324 + case 5 : b += k[4]; 1.325 + case 4 : a += (uint32_t(k[3])<<24); 1.326 + case 3 : a += (uint32_t(k[2])<<16); 1.327 + case 2 : a += (uint32_t(k[1])<<8); 1.328 + case 1 : a += k[0]; 1.329 + /* case 0: nothing left to add */ 1.330 + } 1.331 + hashmix(a, b, c); 1.332 + 1.333 + return c; 1.334 +} 1.335 + 1.336 +nsresult 1.337 +nsDiskCache::Truncate(PRFileDesc * fd, uint32_t newEOF) 1.338 +{ 1.339 + // use modified SetEOF from nsFileStreams::SetEOF() 1.340 + 1.341 +#if defined(XP_UNIX) 1.342 + if (ftruncate(PR_FileDesc2NativeHandle(fd), newEOF) != 0) { 1.343 + NS_ERROR("ftruncate failed"); 1.344 + return NS_ERROR_FAILURE; 1.345 + } 1.346 + 1.347 +#elif defined(XP_WIN) 1.348 + int32_t cnt = PR_Seek(fd, newEOF, PR_SEEK_SET); 1.349 + if (cnt == -1) return NS_ERROR_FAILURE; 1.350 + if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(fd))) { 1.351 + NS_ERROR("SetEndOfFile failed"); 1.352 + return NS_ERROR_FAILURE; 1.353 + } 1.354 + 1.355 +#else 1.356 + // add implementations for other platforms here 1.357 +#endif 1.358 + return NS_OK; 1.359 +} 1.360 + 1.361 + 1.362 +/****************************************************************************** 1.363 + * nsDiskCacheDevice 1.364 + *****************************************************************************/ 1.365 + 1.366 +nsDiskCacheDevice::nsDiskCacheDevice() 1.367 + : mCacheCapacity(0) 1.368 + , mMaxEntrySize(-1) // -1 means "no limit" 1.369 + , mInitialized(false) 1.370 + , mClearingDiskCache(false) 1.371 +{ 1.372 +} 1.373 + 1.374 +nsDiskCacheDevice::~nsDiskCacheDevice() 1.375 +{ 1.376 + Shutdown(); 1.377 +} 1.378 + 1.379 + 1.380 +/** 1.381 + * methods of nsCacheDevice 1.382 + */ 1.383 +nsresult 1.384 +nsDiskCacheDevice::Init() 1.385 +{ 1.386 + nsresult rv; 1.387 + 1.388 + if (Initialized()) { 1.389 + NS_ERROR("Disk cache already initialized!"); 1.390 + return NS_ERROR_UNEXPECTED; 1.391 + } 1.392 + 1.393 + if (!mCacheDirectory) 1.394 + return NS_ERROR_FAILURE; 1.395 + 1.396 + rv = mBindery.Init(); 1.397 + if (NS_FAILED(rv)) 1.398 + return rv; 1.399 + 1.400 + nsDeleteDir::RemoveOldTrashes(mCacheDirectory); 1.401 + 1.402 + // Open Disk Cache 1.403 + rv = OpenDiskCache(); 1.404 + if (NS_FAILED(rv)) { 1.405 + (void) mCacheMap.Close(false); 1.406 + return rv; 1.407 + } 1.408 + 1.409 + mInitialized = true; 1.410 + return NS_OK; 1.411 +} 1.412 + 1.413 + 1.414 +/** 1.415 + * NOTE: called while holding the cache service lock 1.416 + */ 1.417 +nsresult 1.418 +nsDiskCacheDevice::Shutdown() 1.419 +{ 1.420 + nsCacheService::AssertOwnsLock(); 1.421 + 1.422 + nsresult rv = Shutdown_Private(true); 1.423 + if (NS_FAILED(rv)) 1.424 + return rv; 1.425 + 1.426 + return NS_OK; 1.427 +} 1.428 + 1.429 + 1.430 +nsresult 1.431 +nsDiskCacheDevice::Shutdown_Private(bool flush) 1.432 +{ 1.433 + CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush)); 1.434 + 1.435 + if (Initialized()) { 1.436 + // check cache limits in case we need to evict. 1.437 + EvictDiskCacheEntries(mCacheCapacity); 1.438 + 1.439 + // At this point there may be a number of pending cache-requests on the 1.440 + // cache-io thread. Wait for all these to run before we wipe out our 1.441 + // datastructures (see bug #620660) 1.442 + (void) nsCacheService::SyncWithCacheIOThread(); 1.443 + 1.444 + // write out persistent information about the cache. 1.445 + (void) mCacheMap.Close(flush); 1.446 + 1.447 + mBindery.Reset(); 1.448 + 1.449 + mInitialized = false; 1.450 + } 1.451 + 1.452 + return NS_OK; 1.453 +} 1.454 + 1.455 + 1.456 +const char * 1.457 +nsDiskCacheDevice::GetDeviceID() 1.458 +{ 1.459 + return DISK_CACHE_DEVICE_ID; 1.460 +} 1.461 + 1.462 +/** 1.463 + * FindEntry - 1.464 + * 1.465 + * cases: key not in disk cache, hash number free 1.466 + * key not in disk cache, hash number used 1.467 + * key in disk cache 1.468 + * 1.469 + * NOTE: called while holding the cache service lock 1.470 + */ 1.471 +nsCacheEntry * 1.472 +nsDiskCacheDevice::FindEntry(nsCString * key, bool *collision) 1.473 +{ 1.474 + Telemetry::AutoTimer<Telemetry::CACHE_DISK_SEARCH_2> timer; 1.475 + if (!Initialized()) return nullptr; // NS_ERROR_NOT_INITIALIZED 1.476 + if (mClearingDiskCache) return nullptr; 1.477 + nsDiskCacheRecord record; 1.478 + nsDiskCacheBinding * binding = nullptr; 1.479 + PLDHashNumber hashNumber = nsDiskCache::Hash(key->get()); 1.480 + 1.481 + *collision = false; 1.482 + 1.483 + binding = mBindery.FindActiveBinding(hashNumber); 1.484 + if (binding && !binding->mCacheEntry->Key()->Equals(*key)) { 1.485 + *collision = true; 1.486 + return nullptr; 1.487 + } else if (binding && binding->mDeactivateEvent) { 1.488 + binding->mDeactivateEvent->CancelEvent(); 1.489 + binding->mDeactivateEvent = nullptr; 1.490 + CACHE_LOG_DEBUG(("CACHE: reusing deactivated entry %p " \ 1.491 + "req-key=%s entry-key=%s\n", 1.492 + binding->mCacheEntry, key, binding->mCacheEntry->Key())); 1.493 + 1.494 + return binding->mCacheEntry; // just return this one, observing that 1.495 + // FindActiveBinding() does not return 1.496 + // bindings to doomed entries 1.497 + } 1.498 + binding = nullptr; 1.499 + 1.500 + // lookup hash number in cache map 1.501 + nsresult rv = mCacheMap.FindRecord(hashNumber, &record); 1.502 + if (NS_FAILED(rv)) return nullptr; // XXX log error? 1.503 + 1.504 + nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record); 1.505 + if (!diskEntry) return nullptr; 1.506 + 1.507 + // compare key to be sure 1.508 + if (!key->Equals(diskEntry->Key())) { 1.509 + *collision = true; 1.510 + return nullptr; 1.511 + } 1.512 + 1.513 + nsCacheEntry * entry = diskEntry->CreateCacheEntry(this); 1.514 + if (entry) { 1.515 + binding = mBindery.CreateBinding(entry, &record); 1.516 + if (!binding) { 1.517 + delete entry; 1.518 + entry = nullptr; 1.519 + } 1.520 + } 1.521 + 1.522 + if (!entry) { 1.523 + (void) mCacheMap.DeleteStorage(&record); 1.524 + (void) mCacheMap.DeleteRecord(&record); 1.525 + } 1.526 + 1.527 + return entry; 1.528 +} 1.529 + 1.530 + 1.531 +/** 1.532 + * NOTE: called while holding the cache service lock 1.533 + */ 1.534 +nsresult 1.535 +nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) 1.536 +{ 1.537 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.538 + if (!IsValidBinding(binding)) 1.539 + return NS_ERROR_UNEXPECTED; 1.540 + 1.541 + CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n", 1.542 + entry, binding->mRecord.HashNumber())); 1.543 + 1.544 + nsDiskCacheDeviceDeactivateEntryEvent *event = 1.545 + new nsDiskCacheDeviceDeactivateEntryEvent(this, entry, binding); 1.546 + 1.547 + // ensure we can cancel the event via the binding later if necessary 1.548 + binding->mDeactivateEvent = event; 1.549 + 1.550 + DebugOnly<nsresult> rv = nsCacheService::DispatchToCacheIOThread(event); 1.551 + NS_ASSERTION(NS_SUCCEEDED(rv), "DeactivateEntry: Failed dispatching " 1.552 + "deactivation event"); 1.553 + return NS_OK; 1.554 +} 1.555 + 1.556 +/** 1.557 + * NOTE: called while holding the cache service lock 1.558 + */ 1.559 +nsresult 1.560 +nsDiskCacheDevice::DeactivateEntry_Private(nsCacheEntry * entry, 1.561 + nsDiskCacheBinding * binding) 1.562 +{ 1.563 + nsresult rv = NS_OK; 1.564 + if (entry->IsDoomed()) { 1.565 + // delete data, entry, record from disk for entry 1.566 + rv = mCacheMap.DeleteStorage(&binding->mRecord); 1.567 + 1.568 + } else { 1.569 + // save stuff to disk for entry 1.570 + rv = mCacheMap.WriteDiskCacheEntry(binding); 1.571 + if (NS_FAILED(rv)) { 1.572 + // clean up as best we can 1.573 + (void) mCacheMap.DeleteStorage(&binding->mRecord); 1.574 + (void) mCacheMap.DeleteRecord(&binding->mRecord); 1.575 + binding->mDoomed = true; // record is no longer in cache map 1.576 + } 1.577 + } 1.578 + 1.579 + mBindery.RemoveBinding(binding); // extract binding from collision detection stuff 1.580 + delete entry; // which will release binding 1.581 + return rv; 1.582 +} 1.583 + 1.584 + 1.585 +/** 1.586 + * BindEntry() 1.587 + * no hash number collision -> no problem 1.588 + * collision 1.589 + * record not active -> evict, no problem 1.590 + * record is active 1.591 + * record is already doomed -> record shouldn't have been in map, no problem 1.592 + * record is not doomed -> doom, and replace record in map 1.593 + * 1.594 + * walk matching hashnumber list to find lowest generation number 1.595 + * take generation number from other (data/meta) location, 1.596 + * or walk active list 1.597 + * 1.598 + * NOTE: called while holding the cache service lock 1.599 + */ 1.600 +nsresult 1.601 +nsDiskCacheDevice::BindEntry(nsCacheEntry * entry) 1.602 +{ 1.603 + if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; 1.604 + if (mClearingDiskCache) return NS_ERROR_NOT_AVAILABLE; 1.605 + nsresult rv = NS_OK; 1.606 + nsDiskCacheRecord record, oldRecord; 1.607 + nsDiskCacheBinding *binding; 1.608 + PLDHashNumber hashNumber = nsDiskCache::Hash(entry->Key()->get()); 1.609 + 1.610 + // Find out if there is already an active binding for this hash. If yes it 1.611 + // should have another key since BindEntry() shouldn't be called twice for 1.612 + // the same entry. Doom the old entry, the new one will get another 1.613 + // generation number so files won't collide. 1.614 + binding = mBindery.FindActiveBinding(hashNumber); 1.615 + if (binding) { 1.616 + NS_ASSERTION(!binding->mCacheEntry->Key()->Equals(*entry->Key()), 1.617 + "BindEntry called for already bound entry!"); 1.618 + // If the entry is pending deactivation, cancel deactivation 1.619 + if (binding->mDeactivateEvent) { 1.620 + binding->mDeactivateEvent->CancelEvent(); 1.621 + binding->mDeactivateEvent = nullptr; 1.622 + } 1.623 + nsCacheService::DoomEntry(binding->mCacheEntry); 1.624 + binding = nullptr; 1.625 + } 1.626 + 1.627 + // Lookup hash number in cache map. There can be a colliding inactive entry. 1.628 + // See bug #321361 comment 21 for the scenario. If there is such entry, 1.629 + // delete it. 1.630 + rv = mCacheMap.FindRecord(hashNumber, &record); 1.631 + if (NS_SUCCEEDED(rv)) { 1.632 + nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record); 1.633 + if (diskEntry) { 1.634 + // compare key to be sure 1.635 + if (!entry->Key()->Equals(diskEntry->Key())) { 1.636 + mCacheMap.DeleteStorage(&record); 1.637 + rv = mCacheMap.DeleteRecord(&record); 1.638 + if (NS_FAILED(rv)) return rv; 1.639 + } 1.640 + } 1.641 + record = nsDiskCacheRecord(); 1.642 + } 1.643 + 1.644 + // create a new record for this entry 1.645 + record.SetHashNumber(nsDiskCache::Hash(entry->Key()->get())); 1.646 + record.SetEvictionRank(ULONG_MAX - SecondsFromPRTime(PR_Now())); 1.647 + 1.648 + CACHE_LOG_DEBUG(("CACHE: disk BindEntry [%p %x]\n", 1.649 + entry, record.HashNumber())); 1.650 + 1.651 + if (!entry->IsDoomed()) { 1.652 + // if entry isn't doomed, add it to the cache map 1.653 + rv = mCacheMap.AddRecord(&record, &oldRecord); // deletes old record, if any 1.654 + if (NS_FAILED(rv)) return rv; 1.655 + 1.656 + uint32_t oldHashNumber = oldRecord.HashNumber(); 1.657 + if (oldHashNumber) { 1.658 + // gotta evict this one first 1.659 + nsDiskCacheBinding * oldBinding = mBindery.FindActiveBinding(oldHashNumber); 1.660 + if (oldBinding) { 1.661 + // XXX if debug : compare keys for hashNumber collision 1.662 + 1.663 + if (!oldBinding->mCacheEntry->IsDoomed()) { 1.664 + // If the old entry is pending deactivation, cancel deactivation 1.665 + if (oldBinding->mDeactivateEvent) { 1.666 + oldBinding->mDeactivateEvent->CancelEvent(); 1.667 + oldBinding->mDeactivateEvent = nullptr; 1.668 + } 1.669 + // we've got a live one! 1.670 + nsCacheService::DoomEntry(oldBinding->mCacheEntry); 1.671 + // storage will be delete when oldBinding->mCacheEntry is Deactivated 1.672 + } 1.673 + } else { 1.674 + // delete storage 1.675 + // XXX if debug : compare keys for hashNumber collision 1.676 + rv = mCacheMap.DeleteStorage(&oldRecord); 1.677 + if (NS_FAILED(rv)) return rv; // XXX delete record we just added? 1.678 + } 1.679 + } 1.680 + } 1.681 + 1.682 + // Make sure this entry has its associated nsDiskCacheBinding attached. 1.683 + binding = mBindery.CreateBinding(entry, &record); 1.684 + NS_ASSERTION(binding, "nsDiskCacheDevice::BindEntry"); 1.685 + if (!binding) return NS_ERROR_OUT_OF_MEMORY; 1.686 + NS_ASSERTION(binding->mRecord.ValidRecord(), "bad cache map record"); 1.687 + 1.688 + return NS_OK; 1.689 +} 1.690 + 1.691 + 1.692 +/** 1.693 + * NOTE: called while holding the cache service lock 1.694 + */ 1.695 +void 1.696 +nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry) 1.697 +{ 1.698 + CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry)); 1.699 + 1.700 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.701 + NS_ASSERTION(binding, "DoomEntry: binding == nullptr"); 1.702 + if (!binding) 1.703 + return; 1.704 + 1.705 + if (!binding->mDoomed) { 1.706 + // so it can't be seen by FindEntry() ever again. 1.707 +#ifdef DEBUG 1.708 + nsresult rv = 1.709 +#endif 1.710 + mCacheMap.DeleteRecord(&binding->mRecord); 1.711 + NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed."); 1.712 + binding->mDoomed = true; // record in no longer in cache map 1.713 + } 1.714 +} 1.715 + 1.716 + 1.717 +/** 1.718 + * NOTE: called while holding the cache service lock 1.719 + */ 1.720 +nsresult 1.721 +nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry * entry, 1.722 + nsCacheAccessMode mode, 1.723 + uint32_t offset, 1.724 + nsIInputStream ** result) 1.725 +{ 1.726 + CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n", 1.727 + entry, mode, offset)); 1.728 + 1.729 + NS_ENSURE_ARG_POINTER(entry); 1.730 + NS_ENSURE_ARG_POINTER(result); 1.731 + 1.732 + nsresult rv; 1.733 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.734 + if (!IsValidBinding(binding)) 1.735 + return NS_ERROR_UNEXPECTED; 1.736 + 1.737 + NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other"); 1.738 + 1.739 + rv = binding->EnsureStreamIO(); 1.740 + if (NS_FAILED(rv)) return rv; 1.741 + 1.742 + return binding->mStreamIO->GetInputStream(offset, result); 1.743 +} 1.744 + 1.745 + 1.746 +/** 1.747 + * NOTE: called while holding the cache service lock 1.748 + */ 1.749 +nsresult 1.750 +nsDiskCacheDevice::OpenOutputStreamForEntry(nsCacheEntry * entry, 1.751 + nsCacheAccessMode mode, 1.752 + uint32_t offset, 1.753 + nsIOutputStream ** result) 1.754 +{ 1.755 + CACHE_LOG_DEBUG(("CACHE: disk OpenOutputStreamForEntry [%p %x %u]\n", 1.756 + entry, mode, offset)); 1.757 + 1.758 + NS_ENSURE_ARG_POINTER(entry); 1.759 + NS_ENSURE_ARG_POINTER(result); 1.760 + 1.761 + nsresult rv; 1.762 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.763 + if (!IsValidBinding(binding)) 1.764 + return NS_ERROR_UNEXPECTED; 1.765 + 1.766 + NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other"); 1.767 + 1.768 + rv = binding->EnsureStreamIO(); 1.769 + if (NS_FAILED(rv)) return rv; 1.770 + 1.771 + return binding->mStreamIO->GetOutputStream(offset, result); 1.772 +} 1.773 + 1.774 + 1.775 +/** 1.776 + * NOTE: called while holding the cache service lock 1.777 + */ 1.778 +nsresult 1.779 +nsDiskCacheDevice::GetFileForEntry(nsCacheEntry * entry, 1.780 + nsIFile ** result) 1.781 +{ 1.782 + NS_ENSURE_ARG_POINTER(result); 1.783 + *result = nullptr; 1.784 + 1.785 + nsresult rv; 1.786 + 1.787 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.788 + if (!IsValidBinding(binding)) 1.789 + return NS_ERROR_UNEXPECTED; 1.790 + 1.791 + // check/set binding->mRecord for separate file, sync w/mCacheMap 1.792 + if (binding->mRecord.DataLocationInitialized()) { 1.793 + if (binding->mRecord.DataFile() != 0) 1.794 + return NS_ERROR_NOT_AVAILABLE; // data not stored as separate file 1.795 + 1.796 + NS_ASSERTION(binding->mRecord.DataFileGeneration() == binding->mGeneration, "error generations out of sync"); 1.797 + } else { 1.798 + binding->mRecord.SetDataFileGeneration(binding->mGeneration); 1.799 + binding->mRecord.SetDataFileSize(0); // 1k minimum 1.800 + if (!binding->mDoomed) { 1.801 + // record stored in cache map, so update it 1.802 + rv = mCacheMap.UpdateRecord(&binding->mRecord); 1.803 + if (NS_FAILED(rv)) return rv; 1.804 + } 1.805 + } 1.806 + 1.807 + nsCOMPtr<nsIFile> file; 1.808 + rv = mCacheMap.GetFileForDiskCacheRecord(&binding->mRecord, 1.809 + nsDiskCache::kData, 1.810 + false, 1.811 + getter_AddRefs(file)); 1.812 + if (NS_FAILED(rv)) return rv; 1.813 + 1.814 + NS_IF_ADDREF(*result = file); 1.815 + return NS_OK; 1.816 +} 1.817 + 1.818 + 1.819 +/** 1.820 + * This routine will get called every time an open descriptor is written to. 1.821 + * 1.822 + * NOTE: called while holding the cache service lock 1.823 + */ 1.824 +nsresult 1.825 +nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize) 1.826 +{ 1.827 + CACHE_LOG_DEBUG(("CACHE: disk OnDataSizeChange [%p %d]\n", 1.828 + entry, deltaSize)); 1.829 + 1.830 + // If passed a negative value, then there's nothing to do. 1.831 + if (deltaSize < 0) 1.832 + return NS_OK; 1.833 + 1.834 + nsDiskCacheBinding * binding = GetCacheEntryBinding(entry); 1.835 + if (!IsValidBinding(binding)) 1.836 + return NS_ERROR_UNEXPECTED; 1.837 + 1.838 + NS_ASSERTION(binding->mRecord.ValidRecord(), "bad record"); 1.839 + 1.840 + uint32_t newSize = entry->DataSize() + deltaSize; 1.841 + uint32_t newSizeK = ((newSize + 0x3FF) >> 10); 1.842 + 1.843 + // If the new size is larger than max. file size or larger than 1.844 + // 1/8 the cache capacity (which is in KiB's), doom the entry and abort. 1.845 + if (EntryIsTooBig(newSize)) { 1.846 +#ifdef DEBUG 1.847 + nsresult rv = 1.848 +#endif 1.849 + nsCacheService::DoomEntry(entry); 1.850 + NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); 1.851 + return NS_ERROR_ABORT; 1.852 + } 1.853 + 1.854 + uint32_t sizeK = ((entry->DataSize() + 0x03FF) >> 10); // round up to next 1k 1.855 + 1.856 + // In total count we ignore anything over kMaxDataSizeK (bug #651100), so 1.857 + // the target capacity should be calculated the same way. 1.858 + if (sizeK > kMaxDataSizeK) sizeK = kMaxDataSizeK; 1.859 + if (newSizeK > kMaxDataSizeK) newSizeK = kMaxDataSizeK; 1.860 + 1.861 + // pre-evict entries to make space for new data 1.862 + uint32_t targetCapacity = mCacheCapacity > (newSizeK - sizeK) 1.863 + ? mCacheCapacity - (newSizeK - sizeK) 1.864 + : 0; 1.865 + EvictDiskCacheEntries(targetCapacity); 1.866 + 1.867 + return NS_OK; 1.868 +} 1.869 + 1.870 + 1.871 +/****************************************************************************** 1.872 + * EntryInfoVisitor 1.873 + *****************************************************************************/ 1.874 +class EntryInfoVisitor : public nsDiskCacheRecordVisitor 1.875 +{ 1.876 +public: 1.877 + EntryInfoVisitor(nsDiskCacheMap * cacheMap, 1.878 + nsICacheVisitor * visitor) 1.879 + : mCacheMap(cacheMap) 1.880 + , mVisitor(visitor) 1.881 + {} 1.882 + 1.883 + virtual int32_t VisitRecord(nsDiskCacheRecord * mapRecord) 1.884 + { 1.885 + // XXX optimization: do we have this record in memory? 1.886 + 1.887 + // read in the entry (metadata) 1.888 + nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord); 1.889 + if (!diskEntry) { 1.890 + return kVisitNextRecord; 1.891 + } 1.892 + 1.893 + // create nsICacheEntryInfo 1.894 + nsDiskCacheEntryInfo * entryInfo = new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID, diskEntry); 1.895 + if (!entryInfo) { 1.896 + return kStopVisitingRecords; 1.897 + } 1.898 + nsCOMPtr<nsICacheEntryInfo> ref(entryInfo); 1.899 + 1.900 + bool keepGoing; 1.901 + (void)mVisitor->VisitEntry(DISK_CACHE_DEVICE_ID, entryInfo, &keepGoing); 1.902 + return keepGoing ? kVisitNextRecord : kStopVisitingRecords; 1.903 + } 1.904 + 1.905 +private: 1.906 + nsDiskCacheMap * mCacheMap; 1.907 + nsICacheVisitor * mVisitor; 1.908 +}; 1.909 + 1.910 + 1.911 +nsresult 1.912 +nsDiskCacheDevice::Visit(nsICacheVisitor * visitor) 1.913 +{ 1.914 + if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; 1.915 + nsDiskCacheDeviceInfo* deviceInfo = new nsDiskCacheDeviceInfo(this); 1.916 + nsCOMPtr<nsICacheDeviceInfo> ref(deviceInfo); 1.917 + 1.918 + bool keepGoing; 1.919 + nsresult rv = visitor->VisitDevice(DISK_CACHE_DEVICE_ID, deviceInfo, &keepGoing); 1.920 + if (NS_FAILED(rv)) return rv; 1.921 + 1.922 + if (keepGoing) { 1.923 + EntryInfoVisitor infoVisitor(&mCacheMap, visitor); 1.924 + return mCacheMap.VisitRecords(&infoVisitor); 1.925 + } 1.926 + 1.927 + return NS_OK; 1.928 +} 1.929 + 1.930 +// Max allowed size for an entry is currently MIN(mMaxEntrySize, 1/8 CacheCapacity) 1.931 +bool 1.932 +nsDiskCacheDevice::EntryIsTooBig(int64_t entrySize) 1.933 +{ 1.934 + if (mMaxEntrySize == -1) // no limit 1.935 + return entrySize > (static_cast<int64_t>(mCacheCapacity) * 1024 / 8); 1.936 + else 1.937 + return entrySize > mMaxEntrySize || 1.938 + entrySize > (static_cast<int64_t>(mCacheCapacity) * 1024 / 8); 1.939 +} 1.940 + 1.941 +nsresult 1.942 +nsDiskCacheDevice::EvictEntries(const char * clientID) 1.943 +{ 1.944 + CACHE_LOG_DEBUG(("CACHE: disk EvictEntries [%s]\n", clientID)); 1.945 + 1.946 + if (!Initialized()) return NS_ERROR_NOT_INITIALIZED; 1.947 + nsresult rv; 1.948 + 1.949 + if (clientID == nullptr) { 1.950 + // we're clearing the entire disk cache 1.951 + rv = ClearDiskCache(); 1.952 + if (rv != NS_ERROR_CACHE_IN_USE) 1.953 + return rv; 1.954 + } 1.955 + 1.956 + nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, 0, clientID); 1.957 + rv = mCacheMap.VisitRecords(&evictor); 1.958 + 1.959 + if (clientID == nullptr) // we tried to clear the entire cache 1.960 + rv = mCacheMap.Trim(); // so trim cache block files (if possible) 1.961 + return rv; 1.962 +} 1.963 + 1.964 + 1.965 +/** 1.966 + * private methods 1.967 + */ 1.968 + 1.969 +nsresult 1.970 +nsDiskCacheDevice::OpenDiskCache() 1.971 +{ 1.972 + Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_OPEN> timer; 1.973 + // if we don't have a cache directory, create one and open it 1.974 + bool exists; 1.975 + nsresult rv = mCacheDirectory->Exists(&exists); 1.976 + if (NS_FAILED(rv)) 1.977 + return rv; 1.978 + 1.979 + if (exists) { 1.980 + // Try opening cache map file. 1.981 + nsDiskCache::CorruptCacheInfo corruptInfo; 1.982 + rv = mCacheMap.Open(mCacheDirectory, &corruptInfo, true); 1.983 + 1.984 + if (NS_SUCCEEDED(rv)) { 1.985 + Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS, 1.986 + corruptInfo); 1.987 + } else if (rv == NS_ERROR_ALREADY_INITIALIZED) { 1.988 + NS_WARNING("nsDiskCacheDevice::OpenDiskCache: already open!"); 1.989 + } else { 1.990 + // Consider cache corrupt: delete it 1.991 + Telemetry::Accumulate(Telemetry::DISK_CACHE_CORRUPT_DETAILS, 1.992 + corruptInfo); 1.993 + // delay delete by 1 minute to avoid IO thrash at startup 1.994 + rv = nsDeleteDir::DeleteDir(mCacheDirectory, true, 60000); 1.995 + if (NS_FAILED(rv)) 1.996 + return rv; 1.997 + exists = false; 1.998 + } 1.999 + } 1.1000 + 1.1001 + // if we don't have a cache directory, create one and open it 1.1002 + if (!exists) { 1.1003 + nsCacheService::MarkStartingFresh(); 1.1004 + rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777); 1.1005 + CACHE_LOG_PATH(PR_LOG_ALWAYS, "\ncreate cache directory: %s\n", mCacheDirectory); 1.1006 + CACHE_LOG_ALWAYS(("mCacheDirectory->Create() = %x\n", rv)); 1.1007 + if (NS_FAILED(rv)) 1.1008 + return rv; 1.1009 + 1.1010 + // reopen the cache map 1.1011 + nsDiskCache::CorruptCacheInfo corruptInfo; 1.1012 + rv = mCacheMap.Open(mCacheDirectory, &corruptInfo, false); 1.1013 + if (NS_FAILED(rv)) 1.1014 + return rv; 1.1015 + } 1.1016 + 1.1017 + return NS_OK; 1.1018 +} 1.1019 + 1.1020 + 1.1021 +nsresult 1.1022 +nsDiskCacheDevice::ClearDiskCache() 1.1023 +{ 1.1024 + if (mBindery.ActiveBindings()) 1.1025 + return NS_ERROR_CACHE_IN_USE; 1.1026 + 1.1027 + mClearingDiskCache = true; 1.1028 + 1.1029 + nsresult rv = Shutdown_Private(false); // false: don't bother flushing 1.1030 + if (NS_FAILED(rv)) 1.1031 + return rv; 1.1032 + 1.1033 + mClearingDiskCache = false; 1.1034 + 1.1035 + // If the disk cache directory is already gone, then it's not an error if 1.1036 + // we fail to delete it ;-) 1.1037 + rv = nsDeleteDir::DeleteDir(mCacheDirectory, true); 1.1038 + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) 1.1039 + return rv; 1.1040 + 1.1041 + return Init(); 1.1042 +} 1.1043 + 1.1044 + 1.1045 +nsresult 1.1046 +nsDiskCacheDevice::EvictDiskCacheEntries(uint32_t targetCapacity) 1.1047 +{ 1.1048 + CACHE_LOG_DEBUG(("CACHE: disk EvictDiskCacheEntries [%u]\n", 1.1049 + targetCapacity)); 1.1050 + 1.1051 + NS_ASSERTION(targetCapacity > 0, "oops"); 1.1052 + 1.1053 + if (mCacheMap.TotalSize() < targetCapacity) 1.1054 + return NS_OK; 1.1055 + 1.1056 + // targetCapacity is in KiB's 1.1057 + nsDiskCacheEvictor evictor(&mCacheMap, &mBindery, targetCapacity, nullptr); 1.1058 + return mCacheMap.EvictRecords(&evictor); 1.1059 +} 1.1060 + 1.1061 + 1.1062 +/** 1.1063 + * methods for prefs 1.1064 + */ 1.1065 + 1.1066 +void 1.1067 +nsDiskCacheDevice::SetCacheParentDirectory(nsIFile * parentDir) 1.1068 +{ 1.1069 + nsresult rv; 1.1070 + bool exists; 1.1071 + 1.1072 + if (Initialized()) { 1.1073 + NS_ASSERTION(false, "Cannot switch cache directory when initialized"); 1.1074 + return; 1.1075 + } 1.1076 + 1.1077 + if (!parentDir) { 1.1078 + mCacheDirectory = nullptr; 1.1079 + return; 1.1080 + } 1.1081 + 1.1082 + // ensure parent directory exists 1.1083 + rv = parentDir->Exists(&exists); 1.1084 + if (NS_SUCCEEDED(rv) && !exists) 1.1085 + rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700); 1.1086 + if (NS_FAILED(rv)) return; 1.1087 + 1.1088 + // ensure cache directory exists 1.1089 + nsCOMPtr<nsIFile> directory; 1.1090 + 1.1091 + rv = parentDir->Clone(getter_AddRefs(directory)); 1.1092 + if (NS_FAILED(rv)) return; 1.1093 + rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache")); 1.1094 + if (NS_FAILED(rv)) return; 1.1095 + 1.1096 + mCacheDirectory = do_QueryInterface(directory); 1.1097 +} 1.1098 + 1.1099 + 1.1100 +void 1.1101 +nsDiskCacheDevice::getCacheDirectory(nsIFile ** result) 1.1102 +{ 1.1103 + *result = mCacheDirectory; 1.1104 + NS_IF_ADDREF(*result); 1.1105 +} 1.1106 + 1.1107 + 1.1108 +/** 1.1109 + * NOTE: called while holding the cache service lock 1.1110 + */ 1.1111 +void 1.1112 +nsDiskCacheDevice::SetCapacity(uint32_t capacity) 1.1113 +{ 1.1114 + // Units are KiB's 1.1115 + mCacheCapacity = capacity; 1.1116 + if (Initialized()) { 1.1117 + if (NS_IsMainThread()) { 1.1118 + // Do not evict entries on the main thread 1.1119 + nsCacheService::DispatchToCacheIOThread( 1.1120 + new nsEvictDiskCacheEntriesEvent(this)); 1.1121 + } else { 1.1122 + // start evicting entries if the new size is smaller! 1.1123 + EvictDiskCacheEntries(mCacheCapacity); 1.1124 + } 1.1125 + } 1.1126 + // Let cache map know of the new capacity 1.1127 + mCacheMap.NotifyCapacityChange(capacity); 1.1128 +} 1.1129 + 1.1130 + 1.1131 +uint32_t nsDiskCacheDevice::getCacheCapacity() 1.1132 +{ 1.1133 + return mCacheCapacity; 1.1134 +} 1.1135 + 1.1136 + 1.1137 +uint32_t nsDiskCacheDevice::getCacheSize() 1.1138 +{ 1.1139 + return mCacheMap.TotalSize(); 1.1140 +} 1.1141 + 1.1142 + 1.1143 +uint32_t nsDiskCacheDevice::getEntryCount() 1.1144 +{ 1.1145 + return mCacheMap.EntryCount(); 1.1146 +} 1.1147 + 1.1148 +void 1.1149 +nsDiskCacheDevice::SetMaxEntrySize(int32_t maxSizeInKilobytes) 1.1150 +{ 1.1151 + // Internal units are bytes. Changing this only takes effect *after* the 1.1152 + // change and has no consequences for existing cache-entries 1.1153 + if (maxSizeInKilobytes >= 0) 1.1154 + mMaxEntrySize = maxSizeInKilobytes * 1024; 1.1155 + else 1.1156 + mMaxEntrySize = -1; 1.1157 +} 1.1158 + 1.1159 +size_t 1.1160 +nsDiskCacheDevice::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) 1.1161 +{ 1.1162 + size_t usage = aMallocSizeOf(this); 1.1163 + 1.1164 + usage += mCacheMap.SizeOfExcludingThis(aMallocSizeOf); 1.1165 + usage += mBindery.SizeOfExcludingThis(aMallocSizeOf); 1.1166 + 1.1167 + return usage; 1.1168 +} 1.1169 +