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