1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache/nsMemoryCacheDevice.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,623 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=80: */ 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 "nsCache.h" 1.11 +#include "nsMemoryCacheDevice.h" 1.12 +#include "nsCacheService.h" 1.13 +#include "nsICacheService.h" 1.14 +#include "nsICacheVisitor.h" 1.15 +#include "nsIStorageStream.h" 1.16 +#include "nsCRT.h" 1.17 +#include "nsReadableUtils.h" 1.18 +#include "mozilla/MathAlgorithms.h" 1.19 +#include "mozilla/Telemetry.h" 1.20 +#include <algorithm> 1.21 + 1.22 +// The memory cache implements the "LRU-SP" caching algorithm 1.23 +// described in "LRU-SP: A Size-Adjusted and Popularity-Aware LRU Replacement 1.24 +// Algorithm for Web Caching" by Kai Cheng and Yahiko Kambayashi. 1.25 + 1.26 +// We keep kQueueCount LRU queues, which should be about ceil(log2(mHardLimit)) 1.27 +// The queues hold exponentially increasing ranges of floor(log2((size/nref))) 1.28 +// values for entries. 1.29 +// Entries larger than 2^(kQueueCount-1) go in the last queue. 1.30 +// Entries with no expiration go in the first queue. 1.31 + 1.32 +const char *gMemoryDeviceID = "memory"; 1.33 + 1.34 + 1.35 +nsMemoryCacheDevice::nsMemoryCacheDevice() 1.36 + : mInitialized(false), 1.37 + mHardLimit(4 * 1024 * 1024), // default, if no pref 1.38 + mSoftLimit((mHardLimit * 9) / 10), // default, if no pref 1.39 + mTotalSize(0), 1.40 + mInactiveSize(0), 1.41 + mEntryCount(0), 1.42 + mMaxEntryCount(0), 1.43 + mMaxEntrySize(-1) // -1 means "no limit" 1.44 +{ 1.45 + for (int i=0; i<kQueueCount; ++i) 1.46 + PR_INIT_CLIST(&mEvictionList[i]); 1.47 +} 1.48 + 1.49 + 1.50 +nsMemoryCacheDevice::~nsMemoryCacheDevice() 1.51 +{ 1.52 + Shutdown(); 1.53 +} 1.54 + 1.55 + 1.56 +nsresult 1.57 +nsMemoryCacheDevice::Init() 1.58 +{ 1.59 + if (mInitialized) return NS_ERROR_ALREADY_INITIALIZED; 1.60 + 1.61 + nsresult rv = mMemCacheEntries.Init(); 1.62 + mInitialized = NS_SUCCEEDED(rv); 1.63 + return rv; 1.64 +} 1.65 + 1.66 + 1.67 +nsresult 1.68 +nsMemoryCacheDevice::Shutdown() 1.69 +{ 1.70 + NS_ASSERTION(mInitialized, "### attempting shutdown while not initialized"); 1.71 + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); 1.72 + 1.73 + mMemCacheEntries.Shutdown(); 1.74 + 1.75 + // evict all entries 1.76 + nsCacheEntry * entry, * next; 1.77 + 1.78 + for (int i = kQueueCount - 1; i >= 0; --i) { 1.79 + entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]); 1.80 + while (entry != &mEvictionList[i]) { 1.81 + NS_ASSERTION(!entry->IsInUse(), "### shutting down with active entries"); 1.82 + next = (nsCacheEntry *)PR_NEXT_LINK(entry); 1.83 + PR_REMOVE_AND_INIT_LINK(entry); 1.84 + 1.85 + // update statistics 1.86 + int32_t memoryRecovered = (int32_t)entry->DataSize(); 1.87 + mTotalSize -= memoryRecovered; 1.88 + mInactiveSize -= memoryRecovered; 1.89 + --mEntryCount; 1.90 + 1.91 + delete entry; 1.92 + entry = next; 1.93 + } 1.94 + } 1.95 + 1.96 +/* 1.97 + * we're not factoring in changes to meta data yet... 1.98 + * NS_ASSERTION(mTotalSize == 0, "### mem cache leaking entries?"); 1.99 + */ 1.100 + NS_ASSERTION(mInactiveSize == 0, "### mem cache leaking entries?"); 1.101 + NS_ASSERTION(mEntryCount == 0, "### mem cache leaking entries?"); 1.102 + 1.103 + mInitialized = false; 1.104 + 1.105 + return NS_OK; 1.106 +} 1.107 + 1.108 + 1.109 +const char * 1.110 +nsMemoryCacheDevice::GetDeviceID() 1.111 +{ 1.112 + return gMemoryDeviceID; 1.113 +} 1.114 + 1.115 + 1.116 +nsCacheEntry * 1.117 +nsMemoryCacheDevice::FindEntry(nsCString * key, bool *collision) 1.118 +{ 1.119 + mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_MEMORY_SEARCH_2> timer; 1.120 + nsCacheEntry * entry = mMemCacheEntries.GetEntry(key); 1.121 + if (!entry) return nullptr; 1.122 + 1.123 + // move entry to the tail of an eviction list 1.124 + PR_REMOVE_AND_INIT_LINK(entry); 1.125 + PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]); 1.126 + 1.127 + mInactiveSize -= entry->DataSize(); 1.128 + 1.129 + return entry; 1.130 +} 1.131 + 1.132 + 1.133 +nsresult 1.134 +nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry) 1.135 +{ 1.136 + CACHE_LOG_DEBUG(("nsMemoryCacheDevice::DeactivateEntry for entry 0x%p\n", 1.137 + entry)); 1.138 + if (entry->IsDoomed()) { 1.139 +#ifdef DEBUG 1.140 + // XXX verify we've removed it from mMemCacheEntries & eviction list 1.141 +#endif 1.142 + delete entry; 1.143 + CACHE_LOG_DEBUG(("deleted doomed entry 0x%p\n", entry)); 1.144 + return NS_OK; 1.145 + } 1.146 + 1.147 +#ifdef DEBUG 1.148 + nsCacheEntry * ourEntry = mMemCacheEntries.GetEntry(entry->Key()); 1.149 + NS_ASSERTION(ourEntry, "DeactivateEntry called for an entry we don't have!"); 1.150 + NS_ASSERTION(entry == ourEntry, "entry doesn't match ourEntry"); 1.151 + if (ourEntry != entry) 1.152 + return NS_ERROR_INVALID_POINTER; 1.153 +#endif 1.154 + 1.155 + mInactiveSize += entry->DataSize(); 1.156 + EvictEntriesIfNecessary(); 1.157 + 1.158 + return NS_OK; 1.159 +} 1.160 + 1.161 + 1.162 +nsresult 1.163 +nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry) 1.164 +{ 1.165 + if (!entry->IsDoomed()) { 1.166 + NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!"); 1.167 + 1.168 + // append entry to the eviction list 1.169 + PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]); 1.170 + 1.171 + // add entry to hashtable of mem cache entries 1.172 + nsresult rv = mMemCacheEntries.AddEntry(entry); 1.173 + if (NS_FAILED(rv)) { 1.174 + PR_REMOVE_AND_INIT_LINK(entry); 1.175 + return rv; 1.176 + } 1.177 + 1.178 + // add size of entry to memory totals 1.179 + ++mEntryCount; 1.180 + if (mMaxEntryCount < mEntryCount) mMaxEntryCount = mEntryCount; 1.181 + 1.182 + mTotalSize += entry->DataSize(); 1.183 + EvictEntriesIfNecessary(); 1.184 + } 1.185 + 1.186 + return NS_OK; 1.187 +} 1.188 + 1.189 + 1.190 +void 1.191 +nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry) 1.192 +{ 1.193 +#ifdef DEBUG 1.194 + // debug code to verify we have entry 1.195 + nsCacheEntry * hashEntry = mMemCacheEntries.GetEntry(entry->Key()); 1.196 + if (!hashEntry) NS_WARNING("no entry for key"); 1.197 + else if (entry != hashEntry) NS_WARNING("entry != hashEntry"); 1.198 +#endif 1.199 + CACHE_LOG_DEBUG(("Dooming entry 0x%p in memory cache\n", entry)); 1.200 + EvictEntry(entry, DO_NOT_DELETE_ENTRY); 1.201 +} 1.202 + 1.203 + 1.204 +nsresult 1.205 +nsMemoryCacheDevice::OpenInputStreamForEntry( nsCacheEntry * entry, 1.206 + nsCacheAccessMode mode, 1.207 + uint32_t offset, 1.208 + nsIInputStream ** result) 1.209 +{ 1.210 + NS_ENSURE_ARG_POINTER(entry); 1.211 + NS_ENSURE_ARG_POINTER(result); 1.212 + 1.213 + nsCOMPtr<nsIStorageStream> storage; 1.214 + nsresult rv; 1.215 + 1.216 + nsISupports *data = entry->Data(); 1.217 + if (data) { 1.218 + storage = do_QueryInterface(data, &rv); 1.219 + if (NS_FAILED(rv)) 1.220 + return rv; 1.221 + } 1.222 + else { 1.223 + rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage)); 1.224 + if (NS_FAILED(rv)) 1.225 + return rv; 1.226 + entry->SetData(storage); 1.227 + } 1.228 + 1.229 + return storage->NewInputStream(offset, result); 1.230 +} 1.231 + 1.232 + 1.233 +nsresult 1.234 +nsMemoryCacheDevice::OpenOutputStreamForEntry( nsCacheEntry * entry, 1.235 + nsCacheAccessMode mode, 1.236 + uint32_t offset, 1.237 + nsIOutputStream ** result) 1.238 +{ 1.239 + NS_ENSURE_ARG_POINTER(entry); 1.240 + NS_ENSURE_ARG_POINTER(result); 1.241 + 1.242 + nsCOMPtr<nsIStorageStream> storage; 1.243 + nsresult rv; 1.244 + 1.245 + nsISupports *data = entry->Data(); 1.246 + if (data) { 1.247 + storage = do_QueryInterface(data, &rv); 1.248 + if (NS_FAILED(rv)) 1.249 + return rv; 1.250 + } 1.251 + else { 1.252 + rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage)); 1.253 + if (NS_FAILED(rv)) 1.254 + return rv; 1.255 + entry->SetData(storage); 1.256 + } 1.257 + 1.258 + return storage->GetOutputStream(offset, result); 1.259 +} 1.260 + 1.261 + 1.262 +nsresult 1.263 +nsMemoryCacheDevice::GetFileForEntry( nsCacheEntry * entry, 1.264 + nsIFile ** result ) 1.265 +{ 1.266 + return NS_ERROR_NOT_IMPLEMENTED; 1.267 +} 1.268 + 1.269 +bool 1.270 +nsMemoryCacheDevice::EntryIsTooBig(int64_t entrySize) 1.271 +{ 1.272 + CACHE_LOG_DEBUG(("nsMemoryCacheDevice::EntryIsTooBig " 1.273 + "[size=%d max=%d soft=%d]\n", 1.274 + entrySize, mMaxEntrySize, mSoftLimit)); 1.275 + if (mMaxEntrySize == -1) 1.276 + return entrySize > mSoftLimit; 1.277 + else 1.278 + return (entrySize > mSoftLimit || entrySize > mMaxEntrySize); 1.279 +} 1.280 + 1.281 +size_t 1.282 +nsMemoryCacheDevice::TotalSize() 1.283 +{ 1.284 + return mTotalSize; 1.285 +} 1.286 + 1.287 +nsresult 1.288 +nsMemoryCacheDevice::OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize) 1.289 +{ 1.290 + if (entry->IsStreamData()) { 1.291 + // we have the right to refuse or pre-evict 1.292 + uint32_t newSize = entry->DataSize() + deltaSize; 1.293 + if (EntryIsTooBig(newSize)) { 1.294 +#ifdef DEBUG 1.295 + nsresult rv = 1.296 +#endif 1.297 + nsCacheService::DoomEntry(entry); 1.298 + NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); 1.299 + return NS_ERROR_ABORT; 1.300 + } 1.301 + } 1.302 + 1.303 + // adjust our totals 1.304 + mTotalSize += deltaSize; 1.305 + 1.306 + if (!entry->IsDoomed()) { 1.307 + // move entry to the tail of the appropriate eviction list 1.308 + PR_REMOVE_AND_INIT_LINK(entry); 1.309 + PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, deltaSize)]); 1.310 + } 1.311 + 1.312 + EvictEntriesIfNecessary(); 1.313 + return NS_OK; 1.314 +} 1.315 + 1.316 + 1.317 +void 1.318 +nsMemoryCacheDevice::AdjustMemoryLimits(int32_t softLimit, int32_t hardLimit) 1.319 +{ 1.320 + mSoftLimit = softLimit; 1.321 + mHardLimit = hardLimit; 1.322 + 1.323 + // First, evict entries that won't fit into the new cache size. 1.324 + EvictEntriesIfNecessary(); 1.325 +} 1.326 + 1.327 + 1.328 +void 1.329 +nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry) 1.330 +{ 1.331 + CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n", 1.332 + entry, deleteEntry)); 1.333 + // remove entry from our hashtable 1.334 + mMemCacheEntries.RemoveEntry(entry); 1.335 + 1.336 + // remove entry from the eviction list 1.337 + PR_REMOVE_AND_INIT_LINK(entry); 1.338 + 1.339 + // update statistics 1.340 + int32_t memoryRecovered = (int32_t)entry->DataSize(); 1.341 + mTotalSize -= memoryRecovered; 1.342 + if (!entry->IsDoomed()) 1.343 + mInactiveSize -= memoryRecovered; 1.344 + --mEntryCount; 1.345 + 1.346 + if (deleteEntry) delete entry; 1.347 +} 1.348 + 1.349 + 1.350 +void 1.351 +nsMemoryCacheDevice::EvictEntriesIfNecessary(void) 1.352 +{ 1.353 + nsCacheEntry * entry; 1.354 + nsCacheEntry * maxEntry; 1.355 + CACHE_LOG_DEBUG(("EvictEntriesIfNecessary. mTotalSize: %d, mHardLimit: %d," 1.356 + "mInactiveSize: %d, mSoftLimit: %d\n", 1.357 + mTotalSize, mHardLimit, mInactiveSize, mSoftLimit)); 1.358 + 1.359 + if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit)) 1.360 + return; 1.361 + 1.362 + uint32_t now = SecondsFromPRTime(PR_Now()); 1.363 + uint64_t entryCost = 0; 1.364 + uint64_t maxCost = 0; 1.365 + do { 1.366 + // LRU-SP eviction selection: Check the head of each segment (each 1.367 + // eviction list, kept in LRU order) and select the maximal-cost 1.368 + // entry for eviction. Cost is time-since-accessed * size / nref. 1.369 + maxEntry = 0; 1.370 + for (int i = kQueueCount - 1; i >= 0; --i) { 1.371 + entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]); 1.372 + 1.373 + // If the head of a list is in use, check the next available entry 1.374 + while ((entry != &mEvictionList[i]) && 1.375 + (entry->IsInUse())) { 1.376 + entry = (nsCacheEntry *)PR_NEXT_LINK(entry); 1.377 + } 1.378 + 1.379 + if (entry != &mEvictionList[i]) { 1.380 + entryCost = (uint64_t) 1.381 + (now - entry->LastFetched()) * entry->DataSize() / 1.382 + std::max(1, entry->FetchCount()); 1.383 + if (!maxEntry || (entryCost > maxCost)) { 1.384 + maxEntry = entry; 1.385 + maxCost = entryCost; 1.386 + } 1.387 + } 1.388 + } 1.389 + if (maxEntry) { 1.390 + EvictEntry(maxEntry, DELETE_ENTRY); 1.391 + } else { 1.392 + break; 1.393 + } 1.394 + } 1.395 + while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit)); 1.396 +} 1.397 + 1.398 + 1.399 +int 1.400 +nsMemoryCacheDevice::EvictionList(nsCacheEntry * entry, int32_t deltaSize) 1.401 +{ 1.402 + // favor items which never expire by putting them in the lowest-index queue 1.403 + if (entry->ExpirationTime() == nsICache::NO_EXPIRATION_TIME) 1.404 + return 0; 1.405 + 1.406 + // compute which eviction queue this entry should go into, 1.407 + // based on floor(log2(size/nref)) 1.408 + int32_t size = deltaSize + (int32_t)entry->DataSize(); 1.409 + int32_t fetchCount = std::max(1, entry->FetchCount()); 1.410 + 1.411 + return std::min((int)mozilla::FloorLog2(size / fetchCount), kQueueCount - 1); 1.412 +} 1.413 + 1.414 + 1.415 +nsresult 1.416 +nsMemoryCacheDevice::Visit(nsICacheVisitor * visitor) 1.417 +{ 1.418 + nsMemoryCacheDeviceInfo * deviceInfo = new nsMemoryCacheDeviceInfo(this); 1.419 + nsCOMPtr<nsICacheDeviceInfo> deviceRef(deviceInfo); 1.420 + if (!deviceInfo) return NS_ERROR_OUT_OF_MEMORY; 1.421 + 1.422 + bool keepGoing; 1.423 + nsresult rv = visitor->VisitDevice(gMemoryDeviceID, deviceInfo, &keepGoing); 1.424 + if (NS_FAILED(rv)) return rv; 1.425 + 1.426 + if (!keepGoing) 1.427 + return NS_OK; 1.428 + 1.429 + nsCacheEntry * entry; 1.430 + nsCOMPtr<nsICacheEntryInfo> entryRef; 1.431 + 1.432 + for (int i = kQueueCount - 1; i >= 0; --i) { 1.433 + entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]); 1.434 + while (entry != &mEvictionList[i]) { 1.435 + nsCacheEntryInfo * entryInfo = new nsCacheEntryInfo(entry); 1.436 + if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY; 1.437 + entryRef = entryInfo; 1.438 + 1.439 + rv = visitor->VisitEntry(gMemoryDeviceID, entryInfo, &keepGoing); 1.440 + entryInfo->DetachEntry(); 1.441 + if (NS_FAILED(rv)) return rv; 1.442 + if (!keepGoing) break; 1.443 + 1.444 + entry = (nsCacheEntry *)PR_NEXT_LINK(entry); 1.445 + } 1.446 + } 1.447 + return NS_OK; 1.448 +} 1.449 + 1.450 + 1.451 +static bool 1.452 +IsEntryPrivate(nsCacheEntry* entry, void* args) 1.453 +{ 1.454 + return entry->IsPrivate(); 1.455 +} 1.456 + 1.457 +struct ClientIDArgs { 1.458 + const char* clientID; 1.459 + uint32_t prefixLength; 1.460 +}; 1.461 + 1.462 +static bool 1.463 +EntryMatchesClientID(nsCacheEntry* entry, void* args) 1.464 +{ 1.465 + const char * clientID = static_cast<ClientIDArgs*>(args)->clientID; 1.466 + uint32_t prefixLength = static_cast<ClientIDArgs*>(args)->prefixLength; 1.467 + const char * key = entry->Key()->get(); 1.468 + return !clientID || nsCRT::strncmp(clientID, key, prefixLength) == 0; 1.469 +} 1.470 + 1.471 +nsresult 1.472 +nsMemoryCacheDevice::DoEvictEntries(bool (*matchFn)(nsCacheEntry* entry, void* args), void* args) 1.473 +{ 1.474 + nsCacheEntry * entry; 1.475 + 1.476 + for (int i = kQueueCount - 1; i >= 0; --i) { 1.477 + PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]); 1.478 + while (elem != &mEvictionList[i]) { 1.479 + entry = (nsCacheEntry *)elem; 1.480 + elem = PR_NEXT_LINK(elem); 1.481 + 1.482 + if (!matchFn(entry, args)) 1.483 + continue; 1.484 + 1.485 + if (entry->IsInUse()) { 1.486 + nsresult rv = nsCacheService::DoomEntry(entry); 1.487 + if (NS_FAILED(rv)) { 1.488 + CACHE_LOG_WARNING(("memCache->DoEvictEntries() aborted: rv =%x", rv)); 1.489 + return rv; 1.490 + } 1.491 + } else { 1.492 + EvictEntry(entry, DELETE_ENTRY); 1.493 + } 1.494 + } 1.495 + } 1.496 + 1.497 + return NS_OK; 1.498 +} 1.499 + 1.500 +nsresult 1.501 +nsMemoryCacheDevice::EvictEntries(const char * clientID) 1.502 +{ 1.503 + ClientIDArgs args = {clientID, clientID ? uint32_t(strlen(clientID)) : 0}; 1.504 + return DoEvictEntries(&EntryMatchesClientID, &args); 1.505 +} 1.506 + 1.507 +nsresult 1.508 +nsMemoryCacheDevice::EvictPrivateEntries() 1.509 +{ 1.510 + return DoEvictEntries(&IsEntryPrivate, nullptr); 1.511 +} 1.512 + 1.513 + 1.514 +// WARNING: SetCapacity can get called before Init() 1.515 +void 1.516 +nsMemoryCacheDevice::SetCapacity(int32_t capacity) 1.517 +{ 1.518 + int32_t hardLimit = capacity * 1024; // convert k into bytes 1.519 + int32_t softLimit = (hardLimit * 9) / 10; 1.520 + AdjustMemoryLimits(softLimit, hardLimit); 1.521 +} 1.522 + 1.523 +void 1.524 +nsMemoryCacheDevice::SetMaxEntrySize(int32_t maxSizeInKilobytes) 1.525 +{ 1.526 + // Internal unit is bytes. Changing this only takes effect *after* the 1.527 + // change and has no consequences for existing cache-entries 1.528 + if (maxSizeInKilobytes >= 0) 1.529 + mMaxEntrySize = maxSizeInKilobytes * 1024; 1.530 + else 1.531 + mMaxEntrySize = -1; 1.532 +} 1.533 + 1.534 +#ifdef DEBUG 1.535 +static PLDHashOperator 1.536 +CountEntry(PLDHashTable * table, PLDHashEntryHdr * hdr, uint32_t number, void * arg) 1.537 +{ 1.538 + int32_t *entryCount = (int32_t *)arg; 1.539 + ++(*entryCount); 1.540 + return PL_DHASH_NEXT; 1.541 +} 1.542 + 1.543 +void 1.544 +nsMemoryCacheDevice::CheckEntryCount() 1.545 +{ 1.546 + if (!mInitialized) return; 1.547 + 1.548 + int32_t evictionListCount = 0; 1.549 + for (int i=0; i<kQueueCount; ++i) { 1.550 + PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]); 1.551 + while (elem != &mEvictionList[i]) { 1.552 + elem = PR_NEXT_LINK(elem); 1.553 + ++evictionListCount; 1.554 + } 1.555 + } 1.556 + NS_ASSERTION(mEntryCount == evictionListCount, "### mem cache badness"); 1.557 + 1.558 + int32_t entryCount = 0; 1.559 + mMemCacheEntries.VisitEntries(CountEntry, &entryCount); 1.560 + NS_ASSERTION(mEntryCount == entryCount, "### mem cache badness"); 1.561 +} 1.562 +#endif 1.563 + 1.564 +/****************************************************************************** 1.565 + * nsMemoryCacheDeviceInfo - for implementing about:cache 1.566 + *****************************************************************************/ 1.567 + 1.568 + 1.569 +NS_IMPL_ISUPPORTS(nsMemoryCacheDeviceInfo, nsICacheDeviceInfo) 1.570 + 1.571 + 1.572 +NS_IMETHODIMP 1.573 +nsMemoryCacheDeviceInfo::GetDescription(char ** result) 1.574 +{ 1.575 + NS_ENSURE_ARG_POINTER(result); 1.576 + *result = NS_strdup("Memory cache device"); 1.577 + if (!*result) return NS_ERROR_OUT_OF_MEMORY; 1.578 + return NS_OK; 1.579 +} 1.580 + 1.581 + 1.582 +NS_IMETHODIMP 1.583 +nsMemoryCacheDeviceInfo::GetUsageReport(char ** result) 1.584 +{ 1.585 + NS_ENSURE_ARG_POINTER(result); 1.586 + nsCString buffer; 1.587 + 1.588 + buffer.AssignLiteral(" <tr>\n" 1.589 + " <th>Inactive storage:</th>\n" 1.590 + " <td>"); 1.591 + buffer.AppendInt(mDevice->mInactiveSize / 1024); 1.592 + buffer.AppendLiteral(" KiB</td>\n" 1.593 + " </tr>\n"); 1.594 + 1.595 + *result = ToNewCString(buffer); 1.596 + if (!*result) return NS_ERROR_OUT_OF_MEMORY; 1.597 + return NS_OK; 1.598 +} 1.599 + 1.600 + 1.601 +NS_IMETHODIMP 1.602 +nsMemoryCacheDeviceInfo::GetEntryCount(uint32_t * result) 1.603 +{ 1.604 + NS_ENSURE_ARG_POINTER(result); 1.605 + // XXX compare calculated count vs. mEntryCount 1.606 + *result = (uint32_t)mDevice->mEntryCount; 1.607 + return NS_OK; 1.608 +} 1.609 + 1.610 + 1.611 +NS_IMETHODIMP 1.612 +nsMemoryCacheDeviceInfo::GetTotalSize(uint32_t * result) 1.613 +{ 1.614 + NS_ENSURE_ARG_POINTER(result); 1.615 + *result = (uint32_t)mDevice->mTotalSize; 1.616 + return NS_OK; 1.617 +} 1.618 + 1.619 + 1.620 +NS_IMETHODIMP 1.621 +nsMemoryCacheDeviceInfo::GetMaximumSize(uint32_t * result) 1.622 +{ 1.623 + NS_ENSURE_ARG_POINTER(result); 1.624 + *result = (uint32_t)mDevice->mHardLimit; 1.625 + return NS_OK; 1.626 +}