netwerk/cache/nsMemoryCacheDevice.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsCache.h"
michael@0 8 #include "nsMemoryCacheDevice.h"
michael@0 9 #include "nsCacheService.h"
michael@0 10 #include "nsICacheService.h"
michael@0 11 #include "nsICacheVisitor.h"
michael@0 12 #include "nsIStorageStream.h"
michael@0 13 #include "nsCRT.h"
michael@0 14 #include "nsReadableUtils.h"
michael@0 15 #include "mozilla/MathAlgorithms.h"
michael@0 16 #include "mozilla/Telemetry.h"
michael@0 17 #include <algorithm>
michael@0 18
michael@0 19 // The memory cache implements the "LRU-SP" caching algorithm
michael@0 20 // described in "LRU-SP: A Size-Adjusted and Popularity-Aware LRU Replacement
michael@0 21 // Algorithm for Web Caching" by Kai Cheng and Yahiko Kambayashi.
michael@0 22
michael@0 23 // We keep kQueueCount LRU queues, which should be about ceil(log2(mHardLimit))
michael@0 24 // The queues hold exponentially increasing ranges of floor(log2((size/nref)))
michael@0 25 // values for entries.
michael@0 26 // Entries larger than 2^(kQueueCount-1) go in the last queue.
michael@0 27 // Entries with no expiration go in the first queue.
michael@0 28
michael@0 29 const char *gMemoryDeviceID = "memory";
michael@0 30
michael@0 31
michael@0 32 nsMemoryCacheDevice::nsMemoryCacheDevice()
michael@0 33 : mInitialized(false),
michael@0 34 mHardLimit(4 * 1024 * 1024), // default, if no pref
michael@0 35 mSoftLimit((mHardLimit * 9) / 10), // default, if no pref
michael@0 36 mTotalSize(0),
michael@0 37 mInactiveSize(0),
michael@0 38 mEntryCount(0),
michael@0 39 mMaxEntryCount(0),
michael@0 40 mMaxEntrySize(-1) // -1 means "no limit"
michael@0 41 {
michael@0 42 for (int i=0; i<kQueueCount; ++i)
michael@0 43 PR_INIT_CLIST(&mEvictionList[i]);
michael@0 44 }
michael@0 45
michael@0 46
michael@0 47 nsMemoryCacheDevice::~nsMemoryCacheDevice()
michael@0 48 {
michael@0 49 Shutdown();
michael@0 50 }
michael@0 51
michael@0 52
michael@0 53 nsresult
michael@0 54 nsMemoryCacheDevice::Init()
michael@0 55 {
michael@0 56 if (mInitialized) return NS_ERROR_ALREADY_INITIALIZED;
michael@0 57
michael@0 58 nsresult rv = mMemCacheEntries.Init();
michael@0 59 mInitialized = NS_SUCCEEDED(rv);
michael@0 60 return rv;
michael@0 61 }
michael@0 62
michael@0 63
michael@0 64 nsresult
michael@0 65 nsMemoryCacheDevice::Shutdown()
michael@0 66 {
michael@0 67 NS_ASSERTION(mInitialized, "### attempting shutdown while not initialized");
michael@0 68 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
michael@0 69
michael@0 70 mMemCacheEntries.Shutdown();
michael@0 71
michael@0 72 // evict all entries
michael@0 73 nsCacheEntry * entry, * next;
michael@0 74
michael@0 75 for (int i = kQueueCount - 1; i >= 0; --i) {
michael@0 76 entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
michael@0 77 while (entry != &mEvictionList[i]) {
michael@0 78 NS_ASSERTION(!entry->IsInUse(), "### shutting down with active entries");
michael@0 79 next = (nsCacheEntry *)PR_NEXT_LINK(entry);
michael@0 80 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 81
michael@0 82 // update statistics
michael@0 83 int32_t memoryRecovered = (int32_t)entry->DataSize();
michael@0 84 mTotalSize -= memoryRecovered;
michael@0 85 mInactiveSize -= memoryRecovered;
michael@0 86 --mEntryCount;
michael@0 87
michael@0 88 delete entry;
michael@0 89 entry = next;
michael@0 90 }
michael@0 91 }
michael@0 92
michael@0 93 /*
michael@0 94 * we're not factoring in changes to meta data yet...
michael@0 95 * NS_ASSERTION(mTotalSize == 0, "### mem cache leaking entries?");
michael@0 96 */
michael@0 97 NS_ASSERTION(mInactiveSize == 0, "### mem cache leaking entries?");
michael@0 98 NS_ASSERTION(mEntryCount == 0, "### mem cache leaking entries?");
michael@0 99
michael@0 100 mInitialized = false;
michael@0 101
michael@0 102 return NS_OK;
michael@0 103 }
michael@0 104
michael@0 105
michael@0 106 const char *
michael@0 107 nsMemoryCacheDevice::GetDeviceID()
michael@0 108 {
michael@0 109 return gMemoryDeviceID;
michael@0 110 }
michael@0 111
michael@0 112
michael@0 113 nsCacheEntry *
michael@0 114 nsMemoryCacheDevice::FindEntry(nsCString * key, bool *collision)
michael@0 115 {
michael@0 116 mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_MEMORY_SEARCH_2> timer;
michael@0 117 nsCacheEntry * entry = mMemCacheEntries.GetEntry(key);
michael@0 118 if (!entry) return nullptr;
michael@0 119
michael@0 120 // move entry to the tail of an eviction list
michael@0 121 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 122 PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
michael@0 123
michael@0 124 mInactiveSize -= entry->DataSize();
michael@0 125
michael@0 126 return entry;
michael@0 127 }
michael@0 128
michael@0 129
michael@0 130 nsresult
michael@0 131 nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry)
michael@0 132 {
michael@0 133 CACHE_LOG_DEBUG(("nsMemoryCacheDevice::DeactivateEntry for entry 0x%p\n",
michael@0 134 entry));
michael@0 135 if (entry->IsDoomed()) {
michael@0 136 #ifdef DEBUG
michael@0 137 // XXX verify we've removed it from mMemCacheEntries & eviction list
michael@0 138 #endif
michael@0 139 delete entry;
michael@0 140 CACHE_LOG_DEBUG(("deleted doomed entry 0x%p\n", entry));
michael@0 141 return NS_OK;
michael@0 142 }
michael@0 143
michael@0 144 #ifdef DEBUG
michael@0 145 nsCacheEntry * ourEntry = mMemCacheEntries.GetEntry(entry->Key());
michael@0 146 NS_ASSERTION(ourEntry, "DeactivateEntry called for an entry we don't have!");
michael@0 147 NS_ASSERTION(entry == ourEntry, "entry doesn't match ourEntry");
michael@0 148 if (ourEntry != entry)
michael@0 149 return NS_ERROR_INVALID_POINTER;
michael@0 150 #endif
michael@0 151
michael@0 152 mInactiveSize += entry->DataSize();
michael@0 153 EvictEntriesIfNecessary();
michael@0 154
michael@0 155 return NS_OK;
michael@0 156 }
michael@0 157
michael@0 158
michael@0 159 nsresult
michael@0 160 nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry)
michael@0 161 {
michael@0 162 if (!entry->IsDoomed()) {
michael@0 163 NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!");
michael@0 164
michael@0 165 // append entry to the eviction list
michael@0 166 PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
michael@0 167
michael@0 168 // add entry to hashtable of mem cache entries
michael@0 169 nsresult rv = mMemCacheEntries.AddEntry(entry);
michael@0 170 if (NS_FAILED(rv)) {
michael@0 171 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 172 return rv;
michael@0 173 }
michael@0 174
michael@0 175 // add size of entry to memory totals
michael@0 176 ++mEntryCount;
michael@0 177 if (mMaxEntryCount < mEntryCount) mMaxEntryCount = mEntryCount;
michael@0 178
michael@0 179 mTotalSize += entry->DataSize();
michael@0 180 EvictEntriesIfNecessary();
michael@0 181 }
michael@0 182
michael@0 183 return NS_OK;
michael@0 184 }
michael@0 185
michael@0 186
michael@0 187 void
michael@0 188 nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry)
michael@0 189 {
michael@0 190 #ifdef DEBUG
michael@0 191 // debug code to verify we have entry
michael@0 192 nsCacheEntry * hashEntry = mMemCacheEntries.GetEntry(entry->Key());
michael@0 193 if (!hashEntry) NS_WARNING("no entry for key");
michael@0 194 else if (entry != hashEntry) NS_WARNING("entry != hashEntry");
michael@0 195 #endif
michael@0 196 CACHE_LOG_DEBUG(("Dooming entry 0x%p in memory cache\n", entry));
michael@0 197 EvictEntry(entry, DO_NOT_DELETE_ENTRY);
michael@0 198 }
michael@0 199
michael@0 200
michael@0 201 nsresult
michael@0 202 nsMemoryCacheDevice::OpenInputStreamForEntry( nsCacheEntry * entry,
michael@0 203 nsCacheAccessMode mode,
michael@0 204 uint32_t offset,
michael@0 205 nsIInputStream ** result)
michael@0 206 {
michael@0 207 NS_ENSURE_ARG_POINTER(entry);
michael@0 208 NS_ENSURE_ARG_POINTER(result);
michael@0 209
michael@0 210 nsCOMPtr<nsIStorageStream> storage;
michael@0 211 nsresult rv;
michael@0 212
michael@0 213 nsISupports *data = entry->Data();
michael@0 214 if (data) {
michael@0 215 storage = do_QueryInterface(data, &rv);
michael@0 216 if (NS_FAILED(rv))
michael@0 217 return rv;
michael@0 218 }
michael@0 219 else {
michael@0 220 rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage));
michael@0 221 if (NS_FAILED(rv))
michael@0 222 return rv;
michael@0 223 entry->SetData(storage);
michael@0 224 }
michael@0 225
michael@0 226 return storage->NewInputStream(offset, result);
michael@0 227 }
michael@0 228
michael@0 229
michael@0 230 nsresult
michael@0 231 nsMemoryCacheDevice::OpenOutputStreamForEntry( nsCacheEntry * entry,
michael@0 232 nsCacheAccessMode mode,
michael@0 233 uint32_t offset,
michael@0 234 nsIOutputStream ** result)
michael@0 235 {
michael@0 236 NS_ENSURE_ARG_POINTER(entry);
michael@0 237 NS_ENSURE_ARG_POINTER(result);
michael@0 238
michael@0 239 nsCOMPtr<nsIStorageStream> storage;
michael@0 240 nsresult rv;
michael@0 241
michael@0 242 nsISupports *data = entry->Data();
michael@0 243 if (data) {
michael@0 244 storage = do_QueryInterface(data, &rv);
michael@0 245 if (NS_FAILED(rv))
michael@0 246 return rv;
michael@0 247 }
michael@0 248 else {
michael@0 249 rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage));
michael@0 250 if (NS_FAILED(rv))
michael@0 251 return rv;
michael@0 252 entry->SetData(storage);
michael@0 253 }
michael@0 254
michael@0 255 return storage->GetOutputStream(offset, result);
michael@0 256 }
michael@0 257
michael@0 258
michael@0 259 nsresult
michael@0 260 nsMemoryCacheDevice::GetFileForEntry( nsCacheEntry * entry,
michael@0 261 nsIFile ** result )
michael@0 262 {
michael@0 263 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 264 }
michael@0 265
michael@0 266 bool
michael@0 267 nsMemoryCacheDevice::EntryIsTooBig(int64_t entrySize)
michael@0 268 {
michael@0 269 CACHE_LOG_DEBUG(("nsMemoryCacheDevice::EntryIsTooBig "
michael@0 270 "[size=%d max=%d soft=%d]\n",
michael@0 271 entrySize, mMaxEntrySize, mSoftLimit));
michael@0 272 if (mMaxEntrySize == -1)
michael@0 273 return entrySize > mSoftLimit;
michael@0 274 else
michael@0 275 return (entrySize > mSoftLimit || entrySize > mMaxEntrySize);
michael@0 276 }
michael@0 277
michael@0 278 size_t
michael@0 279 nsMemoryCacheDevice::TotalSize()
michael@0 280 {
michael@0 281 return mTotalSize;
michael@0 282 }
michael@0 283
michael@0 284 nsresult
michael@0 285 nsMemoryCacheDevice::OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize)
michael@0 286 {
michael@0 287 if (entry->IsStreamData()) {
michael@0 288 // we have the right to refuse or pre-evict
michael@0 289 uint32_t newSize = entry->DataSize() + deltaSize;
michael@0 290 if (EntryIsTooBig(newSize)) {
michael@0 291 #ifdef DEBUG
michael@0 292 nsresult rv =
michael@0 293 #endif
michael@0 294 nsCacheService::DoomEntry(entry);
michael@0 295 NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
michael@0 296 return NS_ERROR_ABORT;
michael@0 297 }
michael@0 298 }
michael@0 299
michael@0 300 // adjust our totals
michael@0 301 mTotalSize += deltaSize;
michael@0 302
michael@0 303 if (!entry->IsDoomed()) {
michael@0 304 // move entry to the tail of the appropriate eviction list
michael@0 305 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 306 PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, deltaSize)]);
michael@0 307 }
michael@0 308
michael@0 309 EvictEntriesIfNecessary();
michael@0 310 return NS_OK;
michael@0 311 }
michael@0 312
michael@0 313
michael@0 314 void
michael@0 315 nsMemoryCacheDevice::AdjustMemoryLimits(int32_t softLimit, int32_t hardLimit)
michael@0 316 {
michael@0 317 mSoftLimit = softLimit;
michael@0 318 mHardLimit = hardLimit;
michael@0 319
michael@0 320 // First, evict entries that won't fit into the new cache size.
michael@0 321 EvictEntriesIfNecessary();
michael@0 322 }
michael@0 323
michael@0 324
michael@0 325 void
michael@0 326 nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry)
michael@0 327 {
michael@0 328 CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n",
michael@0 329 entry, deleteEntry));
michael@0 330 // remove entry from our hashtable
michael@0 331 mMemCacheEntries.RemoveEntry(entry);
michael@0 332
michael@0 333 // remove entry from the eviction list
michael@0 334 PR_REMOVE_AND_INIT_LINK(entry);
michael@0 335
michael@0 336 // update statistics
michael@0 337 int32_t memoryRecovered = (int32_t)entry->DataSize();
michael@0 338 mTotalSize -= memoryRecovered;
michael@0 339 if (!entry->IsDoomed())
michael@0 340 mInactiveSize -= memoryRecovered;
michael@0 341 --mEntryCount;
michael@0 342
michael@0 343 if (deleteEntry) delete entry;
michael@0 344 }
michael@0 345
michael@0 346
michael@0 347 void
michael@0 348 nsMemoryCacheDevice::EvictEntriesIfNecessary(void)
michael@0 349 {
michael@0 350 nsCacheEntry * entry;
michael@0 351 nsCacheEntry * maxEntry;
michael@0 352 CACHE_LOG_DEBUG(("EvictEntriesIfNecessary. mTotalSize: %d, mHardLimit: %d,"
michael@0 353 "mInactiveSize: %d, mSoftLimit: %d\n",
michael@0 354 mTotalSize, mHardLimit, mInactiveSize, mSoftLimit));
michael@0 355
michael@0 356 if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
michael@0 357 return;
michael@0 358
michael@0 359 uint32_t now = SecondsFromPRTime(PR_Now());
michael@0 360 uint64_t entryCost = 0;
michael@0 361 uint64_t maxCost = 0;
michael@0 362 do {
michael@0 363 // LRU-SP eviction selection: Check the head of each segment (each
michael@0 364 // eviction list, kept in LRU order) and select the maximal-cost
michael@0 365 // entry for eviction. Cost is time-since-accessed * size / nref.
michael@0 366 maxEntry = 0;
michael@0 367 for (int i = kQueueCount - 1; i >= 0; --i) {
michael@0 368 entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
michael@0 369
michael@0 370 // If the head of a list is in use, check the next available entry
michael@0 371 while ((entry != &mEvictionList[i]) &&
michael@0 372 (entry->IsInUse())) {
michael@0 373 entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
michael@0 374 }
michael@0 375
michael@0 376 if (entry != &mEvictionList[i]) {
michael@0 377 entryCost = (uint64_t)
michael@0 378 (now - entry->LastFetched()) * entry->DataSize() /
michael@0 379 std::max(1, entry->FetchCount());
michael@0 380 if (!maxEntry || (entryCost > maxCost)) {
michael@0 381 maxEntry = entry;
michael@0 382 maxCost = entryCost;
michael@0 383 }
michael@0 384 }
michael@0 385 }
michael@0 386 if (maxEntry) {
michael@0 387 EvictEntry(maxEntry, DELETE_ENTRY);
michael@0 388 } else {
michael@0 389 break;
michael@0 390 }
michael@0 391 }
michael@0 392 while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit));
michael@0 393 }
michael@0 394
michael@0 395
michael@0 396 int
michael@0 397 nsMemoryCacheDevice::EvictionList(nsCacheEntry * entry, int32_t deltaSize)
michael@0 398 {
michael@0 399 // favor items which never expire by putting them in the lowest-index queue
michael@0 400 if (entry->ExpirationTime() == nsICache::NO_EXPIRATION_TIME)
michael@0 401 return 0;
michael@0 402
michael@0 403 // compute which eviction queue this entry should go into,
michael@0 404 // based on floor(log2(size/nref))
michael@0 405 int32_t size = deltaSize + (int32_t)entry->DataSize();
michael@0 406 int32_t fetchCount = std::max(1, entry->FetchCount());
michael@0 407
michael@0 408 return std::min((int)mozilla::FloorLog2(size / fetchCount), kQueueCount - 1);
michael@0 409 }
michael@0 410
michael@0 411
michael@0 412 nsresult
michael@0 413 nsMemoryCacheDevice::Visit(nsICacheVisitor * visitor)
michael@0 414 {
michael@0 415 nsMemoryCacheDeviceInfo * deviceInfo = new nsMemoryCacheDeviceInfo(this);
michael@0 416 nsCOMPtr<nsICacheDeviceInfo> deviceRef(deviceInfo);
michael@0 417 if (!deviceInfo) return NS_ERROR_OUT_OF_MEMORY;
michael@0 418
michael@0 419 bool keepGoing;
michael@0 420 nsresult rv = visitor->VisitDevice(gMemoryDeviceID, deviceInfo, &keepGoing);
michael@0 421 if (NS_FAILED(rv)) return rv;
michael@0 422
michael@0 423 if (!keepGoing)
michael@0 424 return NS_OK;
michael@0 425
michael@0 426 nsCacheEntry * entry;
michael@0 427 nsCOMPtr<nsICacheEntryInfo> entryRef;
michael@0 428
michael@0 429 for (int i = kQueueCount - 1; i >= 0; --i) {
michael@0 430 entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
michael@0 431 while (entry != &mEvictionList[i]) {
michael@0 432 nsCacheEntryInfo * entryInfo = new nsCacheEntryInfo(entry);
michael@0 433 if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY;
michael@0 434 entryRef = entryInfo;
michael@0 435
michael@0 436 rv = visitor->VisitEntry(gMemoryDeviceID, entryInfo, &keepGoing);
michael@0 437 entryInfo->DetachEntry();
michael@0 438 if (NS_FAILED(rv)) return rv;
michael@0 439 if (!keepGoing) break;
michael@0 440
michael@0 441 entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
michael@0 442 }
michael@0 443 }
michael@0 444 return NS_OK;
michael@0 445 }
michael@0 446
michael@0 447
michael@0 448 static bool
michael@0 449 IsEntryPrivate(nsCacheEntry* entry, void* args)
michael@0 450 {
michael@0 451 return entry->IsPrivate();
michael@0 452 }
michael@0 453
michael@0 454 struct ClientIDArgs {
michael@0 455 const char* clientID;
michael@0 456 uint32_t prefixLength;
michael@0 457 };
michael@0 458
michael@0 459 static bool
michael@0 460 EntryMatchesClientID(nsCacheEntry* entry, void* args)
michael@0 461 {
michael@0 462 const char * clientID = static_cast<ClientIDArgs*>(args)->clientID;
michael@0 463 uint32_t prefixLength = static_cast<ClientIDArgs*>(args)->prefixLength;
michael@0 464 const char * key = entry->Key()->get();
michael@0 465 return !clientID || nsCRT::strncmp(clientID, key, prefixLength) == 0;
michael@0 466 }
michael@0 467
michael@0 468 nsresult
michael@0 469 nsMemoryCacheDevice::DoEvictEntries(bool (*matchFn)(nsCacheEntry* entry, void* args), void* args)
michael@0 470 {
michael@0 471 nsCacheEntry * entry;
michael@0 472
michael@0 473 for (int i = kQueueCount - 1; i >= 0; --i) {
michael@0 474 PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
michael@0 475 while (elem != &mEvictionList[i]) {
michael@0 476 entry = (nsCacheEntry *)elem;
michael@0 477 elem = PR_NEXT_LINK(elem);
michael@0 478
michael@0 479 if (!matchFn(entry, args))
michael@0 480 continue;
michael@0 481
michael@0 482 if (entry->IsInUse()) {
michael@0 483 nsresult rv = nsCacheService::DoomEntry(entry);
michael@0 484 if (NS_FAILED(rv)) {
michael@0 485 CACHE_LOG_WARNING(("memCache->DoEvictEntries() aborted: rv =%x", rv));
michael@0 486 return rv;
michael@0 487 }
michael@0 488 } else {
michael@0 489 EvictEntry(entry, DELETE_ENTRY);
michael@0 490 }
michael@0 491 }
michael@0 492 }
michael@0 493
michael@0 494 return NS_OK;
michael@0 495 }
michael@0 496
michael@0 497 nsresult
michael@0 498 nsMemoryCacheDevice::EvictEntries(const char * clientID)
michael@0 499 {
michael@0 500 ClientIDArgs args = {clientID, clientID ? uint32_t(strlen(clientID)) : 0};
michael@0 501 return DoEvictEntries(&EntryMatchesClientID, &args);
michael@0 502 }
michael@0 503
michael@0 504 nsresult
michael@0 505 nsMemoryCacheDevice::EvictPrivateEntries()
michael@0 506 {
michael@0 507 return DoEvictEntries(&IsEntryPrivate, nullptr);
michael@0 508 }
michael@0 509
michael@0 510
michael@0 511 // WARNING: SetCapacity can get called before Init()
michael@0 512 void
michael@0 513 nsMemoryCacheDevice::SetCapacity(int32_t capacity)
michael@0 514 {
michael@0 515 int32_t hardLimit = capacity * 1024; // convert k into bytes
michael@0 516 int32_t softLimit = (hardLimit * 9) / 10;
michael@0 517 AdjustMemoryLimits(softLimit, hardLimit);
michael@0 518 }
michael@0 519
michael@0 520 void
michael@0 521 nsMemoryCacheDevice::SetMaxEntrySize(int32_t maxSizeInKilobytes)
michael@0 522 {
michael@0 523 // Internal unit is bytes. Changing this only takes effect *after* the
michael@0 524 // change and has no consequences for existing cache-entries
michael@0 525 if (maxSizeInKilobytes >= 0)
michael@0 526 mMaxEntrySize = maxSizeInKilobytes * 1024;
michael@0 527 else
michael@0 528 mMaxEntrySize = -1;
michael@0 529 }
michael@0 530
michael@0 531 #ifdef DEBUG
michael@0 532 static PLDHashOperator
michael@0 533 CountEntry(PLDHashTable * table, PLDHashEntryHdr * hdr, uint32_t number, void * arg)
michael@0 534 {
michael@0 535 int32_t *entryCount = (int32_t *)arg;
michael@0 536 ++(*entryCount);
michael@0 537 return PL_DHASH_NEXT;
michael@0 538 }
michael@0 539
michael@0 540 void
michael@0 541 nsMemoryCacheDevice::CheckEntryCount()
michael@0 542 {
michael@0 543 if (!mInitialized) return;
michael@0 544
michael@0 545 int32_t evictionListCount = 0;
michael@0 546 for (int i=0; i<kQueueCount; ++i) {
michael@0 547 PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
michael@0 548 while (elem != &mEvictionList[i]) {
michael@0 549 elem = PR_NEXT_LINK(elem);
michael@0 550 ++evictionListCount;
michael@0 551 }
michael@0 552 }
michael@0 553 NS_ASSERTION(mEntryCount == evictionListCount, "### mem cache badness");
michael@0 554
michael@0 555 int32_t entryCount = 0;
michael@0 556 mMemCacheEntries.VisitEntries(CountEntry, &entryCount);
michael@0 557 NS_ASSERTION(mEntryCount == entryCount, "### mem cache badness");
michael@0 558 }
michael@0 559 #endif
michael@0 560
michael@0 561 /******************************************************************************
michael@0 562 * nsMemoryCacheDeviceInfo - for implementing about:cache
michael@0 563 *****************************************************************************/
michael@0 564
michael@0 565
michael@0 566 NS_IMPL_ISUPPORTS(nsMemoryCacheDeviceInfo, nsICacheDeviceInfo)
michael@0 567
michael@0 568
michael@0 569 NS_IMETHODIMP
michael@0 570 nsMemoryCacheDeviceInfo::GetDescription(char ** result)
michael@0 571 {
michael@0 572 NS_ENSURE_ARG_POINTER(result);
michael@0 573 *result = NS_strdup("Memory cache device");
michael@0 574 if (!*result) return NS_ERROR_OUT_OF_MEMORY;
michael@0 575 return NS_OK;
michael@0 576 }
michael@0 577
michael@0 578
michael@0 579 NS_IMETHODIMP
michael@0 580 nsMemoryCacheDeviceInfo::GetUsageReport(char ** result)
michael@0 581 {
michael@0 582 NS_ENSURE_ARG_POINTER(result);
michael@0 583 nsCString buffer;
michael@0 584
michael@0 585 buffer.AssignLiteral(" <tr>\n"
michael@0 586 " <th>Inactive storage:</th>\n"
michael@0 587 " <td>");
michael@0 588 buffer.AppendInt(mDevice->mInactiveSize / 1024);
michael@0 589 buffer.AppendLiteral(" KiB</td>\n"
michael@0 590 " </tr>\n");
michael@0 591
michael@0 592 *result = ToNewCString(buffer);
michael@0 593 if (!*result) return NS_ERROR_OUT_OF_MEMORY;
michael@0 594 return NS_OK;
michael@0 595 }
michael@0 596
michael@0 597
michael@0 598 NS_IMETHODIMP
michael@0 599 nsMemoryCacheDeviceInfo::GetEntryCount(uint32_t * result)
michael@0 600 {
michael@0 601 NS_ENSURE_ARG_POINTER(result);
michael@0 602 // XXX compare calculated count vs. mEntryCount
michael@0 603 *result = (uint32_t)mDevice->mEntryCount;
michael@0 604 return NS_OK;
michael@0 605 }
michael@0 606
michael@0 607
michael@0 608 NS_IMETHODIMP
michael@0 609 nsMemoryCacheDeviceInfo::GetTotalSize(uint32_t * result)
michael@0 610 {
michael@0 611 NS_ENSURE_ARG_POINTER(result);
michael@0 612 *result = (uint32_t)mDevice->mTotalSize;
michael@0 613 return NS_OK;
michael@0 614 }
michael@0 615
michael@0 616
michael@0 617 NS_IMETHODIMP
michael@0 618 nsMemoryCacheDeviceInfo::GetMaximumSize(uint32_t * result)
michael@0 619 {
michael@0 620 NS_ENSURE_ARG_POINTER(result);
michael@0 621 *result = (uint32_t)mDevice->mHardLimit;
michael@0 622 return NS_OK;
michael@0 623 }

mercurial