Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(700)

Side by Side Diff: third_party/WebKit/Source/core/fetch/MemoryCache.cpp

Issue 2411243004: [WeakMemoryCache] Remove LRU lists, prune order control and live/dead distinction (Closed)
Patch Set: Reflect yhirano's comment Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 6
7 This library is free software; you can redistribute it and/or 7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public 8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either 9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version. 10 version 2 of the License, or (at your option) any later version.
(...skipping 20 matching lines...) Expand all
31 #include "wtf/AutoReset.h" 31 #include "wtf/AutoReset.h"
32 #include "wtf/CurrentTime.h" 32 #include "wtf/CurrentTime.h"
33 #include "wtf/MathExtras.h" 33 #include "wtf/MathExtras.h"
34 #include "wtf/text/CString.h" 34 #include "wtf/text/CString.h"
35 35
36 namespace blink { 36 namespace blink {
37 37
38 static Persistent<MemoryCache>* gMemoryCache; 38 static Persistent<MemoryCache>* gMemoryCache;
39 39
40 static const unsigned cDefaultCacheCapacity = 8192 * 1024; 40 static const unsigned cDefaultCacheCapacity = 8192 * 1024;
41 static const unsigned cDeferredPruneDeadCapacityFactor = 2;
42 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. 41 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
43 static const double cMaxPruneDeferralDelay = 0.5; // Seconds. 42 static const double cMaxPruneDeferralDelay = 0.5; // Seconds.
44 43
45 // Percentage of capacity toward which we prune, to avoid immediately pruning 44 // Percentage of capacity toward which we prune, to avoid immediately pruning
46 // again. 45 // again.
47 static const float cTargetPrunePercentage = .95f; 46 static const float cTargetPrunePercentage = .95f;
48 47
49 MemoryCache* memoryCache() { 48 MemoryCache* memoryCache() {
50 DCHECK(WTF::isMainThread()); 49 DCHECK(WTF::isMainThread());
51 if (!gMemoryCache) 50 if (!gMemoryCache)
52 gMemoryCache = new Persistent<MemoryCache>(MemoryCache::create()); 51 gMemoryCache = new Persistent<MemoryCache>(MemoryCache::create());
53 return gMemoryCache->get(); 52 return gMemoryCache->get();
54 } 53 }
55 54
56 MemoryCache* replaceMemoryCacheForTesting(MemoryCache* cache) { 55 MemoryCache* replaceMemoryCacheForTesting(MemoryCache* cache) {
57 memoryCache(); 56 memoryCache();
58 MemoryCache* oldCache = gMemoryCache->release(); 57 MemoryCache* oldCache = gMemoryCache->release();
59 *gMemoryCache = cache; 58 *gMemoryCache = cache;
60 MemoryCacheDumpProvider::instance()->setMemoryCache(cache); 59 MemoryCacheDumpProvider::instance()->setMemoryCache(cache);
61 return oldCache; 60 return oldCache;
62 } 61 }
63 62
64 DEFINE_TRACE(MemoryCacheEntry) { 63 DEFINE_TRACE(MemoryCacheEntry) {
65 visitor->template registerWeakMembers<MemoryCacheEntry, 64 visitor->template registerWeakMembers<MemoryCacheEntry,
66 &MemoryCacheEntry::clearResourceWeak>( 65 &MemoryCacheEntry::clearResourceWeak>(
67 this); 66 this);
68 visitor->trace(m_previousInLiveResourcesList);
69 visitor->trace(m_nextInLiveResourcesList);
70 visitor->trace(m_previousInAllResourcesList);
71 visitor->trace(m_nextInAllResourcesList);
72 } 67 }
73 68
74 void MemoryCacheEntry::clearResourceWeak(Visitor* visitor) { 69 void MemoryCacheEntry::clearResourceWeak(Visitor* visitor) {
75 if (!m_resource || ThreadHeap::isHeapObjectAlive(m_resource)) 70 if (!m_resource || ThreadHeap::isHeapObjectAlive(m_resource))
76 return; 71 return;
77 memoryCache()->remove(m_resource.get()); 72 memoryCache()->remove(m_resource.get());
78 m_resource.clear(); 73 m_resource.clear();
79 } 74 }
80 75
81 void MemoryCacheEntry::dispose() {
82 m_resource.clear();
83 }
84
85 Resource* MemoryCacheEntry::resource() {
86 return m_resource.get();
87 }
88
89 DEFINE_TRACE(MemoryCacheLRUList) {
90 visitor->trace(m_head);
91 visitor->trace(m_tail);
92 }
93
94 inline MemoryCache::MemoryCache() 76 inline MemoryCache::MemoryCache()
95 : m_inPruneResources(false), 77 : m_inPruneResources(false),
96 m_prunePending(false), 78 m_prunePending(false),
97 m_maxPruneDeferralDelay(cMaxPruneDeferralDelay), 79 m_maxPruneDeferralDelay(cMaxPruneDeferralDelay),
98 m_pruneTimeStamp(0.0), 80 m_pruneTimeStamp(0.0),
99 m_pruneFrameTimeStamp(0.0), 81 m_pruneFrameTimeStamp(0.0),
100 m_lastFramePaintTimeStamp(0.0), 82 m_lastFramePaintTimeStamp(0.0),
101 m_capacity(cDefaultCacheCapacity), 83 m_capacity(cDefaultCacheCapacity),
102 m_minDeadCapacity(0),
103 m_maxDeadCapacity(cDefaultCacheCapacity),
104 m_maxDeferredPruneDeadCapacity(cDeferredPruneDeadCapacityFactor *
105 cDefaultCacheCapacity),
106 m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune), 84 m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune),
107 m_liveSize(0), 85 m_size(0) {
108 m_deadSize(0) {
109 MemoryCacheDumpProvider::instance()->setMemoryCache(this); 86 MemoryCacheDumpProvider::instance()->setMemoryCache(this);
110 if (ProcessHeap::isLowEndDevice()) 87 if (ProcessHeap::isLowEndDevice())
111 MemoryCoordinator::instance().registerClient(this); 88 MemoryCoordinator::instance().registerClient(this);
112 } 89 }
113 90
114 MemoryCache* MemoryCache::create() { 91 MemoryCache* MemoryCache::create() {
115 return new MemoryCache; 92 return new MemoryCache;
116 } 93 }
117 94
118 MemoryCache::~MemoryCache() { 95 MemoryCache::~MemoryCache() {
119 if (m_prunePending) 96 if (m_prunePending)
120 Platform::current()->currentThread()->removeTaskObserver(this); 97 Platform::current()->currentThread()->removeTaskObserver(this);
121 } 98 }
122 99
123 DEFINE_TRACE(MemoryCache) { 100 DEFINE_TRACE(MemoryCache) {
124 visitor->trace(m_allResources);
125 visitor->trace(m_liveDecodedResources);
126 visitor->trace(m_resourceMaps); 101 visitor->trace(m_resourceMaps);
127 MemoryCacheDumpClient::trace(visitor); 102 MemoryCacheDumpClient::trace(visitor);
128 MemoryCoordinatorClient::trace(visitor); 103 MemoryCoordinatorClient::trace(visitor);
129 } 104 }
130 105
131 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) { 106 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) {
132 if (!originalURL.hasFragmentIdentifier()) 107 if (!originalURL.hasFragmentIdentifier())
133 return originalURL; 108 return originalURL;
134 // Strip away fragment identifier from HTTP URLs. Data URLs must be 109 // Strip away fragment identifier from HTTP URLs. Data URLs must be
135 // unmodified. For file and custom URLs clients may expect resources to be 110 // unmodified. For file and custom URLs clients may expect resources to be
(...skipping 13 matching lines...) Expand all
149 const String& cacheIdentifier) { 124 const String& cacheIdentifier) {
150 if (!m_resourceMaps.contains(cacheIdentifier)) { 125 if (!m_resourceMaps.contains(cacheIdentifier)) {
151 ResourceMapIndex::AddResult result = 126 ResourceMapIndex::AddResult result =
152 m_resourceMaps.add(cacheIdentifier, new ResourceMap); 127 m_resourceMaps.add(cacheIdentifier, new ResourceMap);
153 CHECK(result.isNewEntry); 128 CHECK(result.isNewEntry);
154 } 129 }
155 return m_resourceMaps.get(cacheIdentifier); 130 return m_resourceMaps.get(cacheIdentifier);
156 } 131 }
157 132
158 void MemoryCache::add(Resource* resource) { 133 void MemoryCache::add(Resource* resource) {
159 DCHECK(WTF::isMainThread()); 134 DCHECK(resource);
160 DCHECK(resource->url().isValid());
161 ResourceMap* resources = ensureResourceMap(resource->cacheIdentifier()); 135 ResourceMap* resources = ensureResourceMap(resource->cacheIdentifier());
162 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); 136 addInternal(resources, MemoryCacheEntry::create(resource));
163 CHECK(!contains(resource));
164 resources->set(url, MemoryCacheEntry::create(resource));
165 update(resource, 0, resource->size(), true);
166
167 RESOURCE_LOADING_DVLOG(1) << "MemoryCache::add Added " 137 RESOURCE_LOADING_DVLOG(1) << "MemoryCache::add Added "
168 << resource->url().getString() << ", resource " 138 << resource->url().getString() << ", resource "
169 << resource; 139 << resource;
170 } 140 }
171 141
142 void MemoryCache::addInternal(ResourceMap* resourceMap,
143 MemoryCacheEntry* entry) {
144 DCHECK(WTF::isMainThread());
145 DCHECK(resourceMap);
146
147 Resource* resource = entry->resource();
148 if (!resource)
149 return;
150 DCHECK(resource->url().isValid());
151
152 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
153 ResourceMap::iterator it = resourceMap->find(url);
154 if (it != resourceMap->end()) {
155 Resource* oldResource = it->value->resource();
156 CHECK_NE(oldResource, resource);
157 update(oldResource, oldResource->size(), 0);
158 }
159 resourceMap->set(url, entry);
160 update(resource, 0, resource->size());
161 }
162
172 void MemoryCache::remove(Resource* resource) { 163 void MemoryCache::remove(Resource* resource) {
173 // The resource may have already been removed by someone other than our 164 DCHECK(WTF::isMainThread());
174 // caller, who needed a fresh copy for a reload. 165 DCHECK(resource);
175 if (MemoryCacheEntry* entry = getEntryForResource(resource)) 166 RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for "
176 evict(entry); 167 << resource->url().getString() << " from cache";
168 TRACE_EVENT1("blink", "MemoryCache::evict", "resource",
169 resource->url().getString().utf8());
170
171 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier());
172 if (!resources)
173 return;
174
175 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
176 ResourceMap::iterator it = resources->find(url);
177 if (it == resources->end() || it->value->resource() != resource)
178 return;
179 removeInternal(resources, it);
180 }
181
182 void MemoryCache::removeInternal(ResourceMap* resourceMap,
183 const ResourceMap::iterator& it) {
184 DCHECK(WTF::isMainThread());
185 DCHECK(resourceMap);
186
187 Resource* resource = it->value->resource();
188 DCHECK(resource);
189
190 update(resource, resource->size(), 0);
191 resourceMap->remove(it);
177 } 192 }
178 193
179 bool MemoryCache::contains(const Resource* resource) const { 194 bool MemoryCache::contains(const Resource* resource) const {
180 return getEntryForResource(resource); 195 if (!resource || resource->url().isEmpty())
196 return false;
197 const ResourceMap* resources =
198 m_resourceMaps.get(resource->cacheIdentifier());
199 if (!resources)
200 return false;
201 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
202 MemoryCacheEntry* entry = resources->get(url);
203 return entry && resource == entry->resource();
181 } 204 }
182 205
183 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) { 206 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) const {
184 return resourceForURL(resourceURL, defaultCacheIdentifier()); 207 return resourceForURL(resourceURL, defaultCacheIdentifier());
185 } 208 }
186 209
187 Resource* MemoryCache::resourceForURL(const KURL& resourceURL, 210 Resource* MemoryCache::resourceForURL(const KURL& resourceURL,
188 const String& cacheIdentifier) { 211 const String& cacheIdentifier) const {
189 DCHECK(WTF::isMainThread()); 212 DCHECK(WTF::isMainThread());
190 if (!resourceURL.isValid() || resourceURL.isNull()) 213 if (!resourceURL.isValid() || resourceURL.isNull())
191 return nullptr; 214 return nullptr;
192 DCHECK(!cacheIdentifier.isNull()); 215 DCHECK(!cacheIdentifier.isNull());
193 ResourceMap* resources = m_resourceMaps.get(cacheIdentifier); 216 const ResourceMap* resources = m_resourceMaps.get(cacheIdentifier);
194 if (!resources) 217 if (!resources)
195 return nullptr; 218 return nullptr;
196 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); 219 MemoryCacheEntry* entry =
197 MemoryCacheEntry* entry = resources->get(url); 220 resources->get(removeFragmentIdentifierIfNeeded(resourceURL));
198 if (!entry) 221 if (!entry)
199 return nullptr; 222 return nullptr;
200 return entry->resource(); 223 return entry->resource();
201 } 224 }
202 225
203 HeapVector<Member<Resource>> MemoryCache::resourcesForURL( 226 HeapVector<Member<Resource>> MemoryCache::resourcesForURL(
204 const KURL& resourceURL) { 227 const KURL& resourceURL) const {
205 DCHECK(WTF::isMainThread()); 228 DCHECK(WTF::isMainThread());
206 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); 229 KURL url = removeFragmentIdentifierIfNeeded(resourceURL);
207 HeapVector<Member<Resource>> results; 230 HeapVector<Member<Resource>> results;
208 for (const auto& resourceMapIter : m_resourceMaps) { 231 for (const auto& resourceMapIter : m_resourceMaps) {
209 if (MemoryCacheEntry* entry = resourceMapIter.value->get(url)) { 232 if (MemoryCacheEntry* entry = resourceMapIter.value->get(url)) {
210 Resource* resource = entry->resource(); 233 Resource* resource = entry->resource();
211 DCHECK(resource); 234 DCHECK(resource);
212 results.append(resource); 235 results.append(resource);
213 } 236 }
214 } 237 }
215 return results; 238 return results;
216 } 239 }
217 240
218 size_t MemoryCache::deadCapacity() const { 241 void MemoryCache::pruneResources(PruneStrategy strategy) {
219 // Dead resource capacity is whatever space is not occupied by live resources,
220 // bounded by an independent minimum and maximum.
221 size_t capacity =
222 m_capacity -
223 std::min(m_liveSize, m_capacity); // Start with available capacity.
224 capacity = std::max(capacity,
225 m_minDeadCapacity); // Make sure it's above the minimum.
226 capacity = std::min(capacity,
227 m_maxDeadCapacity); // Make sure it's below the maximum.
228 return capacity;
229 }
230
231 size_t MemoryCache::liveCapacity() const {
232 // Live resource capacity is whatever is left over after calculating dead
233 // resource capacity.
234 return m_capacity - deadCapacity();
235 }
236
237 void MemoryCache::pruneLiveResources(PruneStrategy strategy) {
238 DCHECK(!m_prunePending); 242 DCHECK(!m_prunePending);
239 size_t capacity = liveCapacity(); 243 const size_t sizeLimit = (strategy == MaximalPrune) ? 0 : capacity();
240 if (strategy == MaximalPrune) 244 if (m_size <= sizeLimit)
241 capacity = 0;
242 if (!m_liveSize || (capacity && m_liveSize <= capacity))
243 return; 245 return;
244 246
245 // Cut by a percentage to avoid immediately pruning again. 247 // Cut by a percentage to avoid immediately pruning again.
246 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage); 248 size_t targetSize = static_cast<size_t>(sizeLimit * cTargetPrunePercentage);
247 249
248 // Destroy any decoded data in live objects that we can. Start from the tail, 250 for (const auto& resourceMapIter : m_resourceMaps) {
249 // since this is the lowest priority and least recently accessed of the 251 for (const auto& resourceIter : *resourceMapIter.value) {
250 // objects. 252 Resource* resource = resourceIter.value->resource();
251 253 DCHECK(resource);
252 // The list might not be sorted by the m_lastDecodedFrameTimeStamp. The impact 254 if (resource->isLoaded() && resource->decodedSize()) {
253 // of this weaker invariant is minor as the below if statement to check the 255 // Check to see if the remaining resources are too new to prune.
254 // elapsedTime will evaluate to false as the current time will be a lot 256 double elapsedTime =
255 // greater than the current->m_lastDecodedFrameTimeStamp. For more details 257 m_pruneFrameTimeStamp - resourceIter.value->m_lastDecodedAccessTime;
256 // see: https://bugs.webkit.org/show_bug.cgi?id=30209 258 if (strategy == AutomaticPrune &&
257 259 elapsedTime < m_delayBeforeLiveDecodedPrune)
258 MemoryCacheEntry* current = m_liveDecodedResources.m_tail; 260 continue;
259 while (current) { 261 resource->prune();
260 Resource* resource = current->resource(); 262 if (m_size <= targetSize)
261 MemoryCacheEntry* previous = current->m_previousInLiveResourcesList; 263 return;
262 DCHECK(resource->isAlive()); 264 }
263
264 if (resource->isLoaded() && resource->decodedSize()) {
265 // Check to see if the remaining resources are too new to prune.
266 double elapsedTime =
267 m_pruneFrameTimeStamp - current->m_lastDecodedAccessTime;
268 if (strategy == AutomaticPrune &&
269 elapsedTime < m_delayBeforeLiveDecodedPrune)
270 return;
271
272 // Destroy our decoded data if possible. This will remove us from
273 // m_liveDecodedResources, and possibly move us to a different LRU list in
274 // m_allResources.
275 resource->prune();
276
277 if (targetSize && m_liveSize <= targetSize)
278 return;
279 } 265 }
280 current = previous;
281 } 266 }
282 } 267 }
283 268
284 void MemoryCache::pruneDeadResources(PruneStrategy strategy) { 269 void MemoryCache::setCapacity(size_t totalBytes) {
285 size_t capacity = deadCapacity();
286 if (strategy == MaximalPrune)
287 capacity = 0;
288 if (!m_deadSize || (capacity && m_deadSize <= capacity))
289 return;
290
291 // Cut by a percentage to avoid immediately pruning again.
292 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage);
293
294 int size = m_allResources.size();
295 if (targetSize && m_deadSize <= targetSize)
296 return;
297
298 bool canShrinkLRULists = true;
299 for (int i = size - 1; i >= 0; i--) {
300 // Remove from the tail, since this is the least frequently accessed of the
301 // objects.
302 MemoryCacheEntry* current = m_allResources[i].m_tail;
303
304 // First flush all the decoded data in this queue.
305 while (current) {
306 Resource* resource = current->resource();
307 MemoryCacheEntry* previous = current->m_previousInAllResourcesList;
308
309 // Decoded data may reference other resources. Skip |current| if |current|
310 // somehow got kicked out of cache during destroyDecodedData().
311 if (!resource || !contains(resource)) {
312 current = previous;
313 continue;
314 }
315
316 if (!resource->isAlive() && !resource->isPreloaded() &&
317 resource->isLoaded()) {
318 // Destroy our decoded data. This will remove us from
319 // m_liveDecodedResources, and possibly move us to a different LRU list
320 // in m_allResources.
321 resource->prune();
322
323 if (targetSize && m_deadSize <= targetSize)
324 return;
325 }
326 current = previous;
327 }
328
329 // Now evict objects from this queue.
330 current = m_allResources[i].m_tail;
331 while (current) {
332 Resource* resource = current->resource();
333 MemoryCacheEntry* previous = current->m_previousInAllResourcesList;
334 if (!resource || !contains(resource)) {
335 current = previous;
336 continue;
337 }
338 if (!resource->isAlive() && !resource->isPreloaded()) {
339 evict(current);
340 if (targetSize && m_deadSize <= targetSize)
341 return;
342 }
343 current = previous;
344 }
345
346 // Shrink the vector back down so we don't waste time inspecting empty LRU
347 // lists on future prunes.
348 if (m_allResources[i].m_head)
349 canShrinkLRULists = false;
350 else if (canShrinkLRULists)
351 m_allResources.resize(i);
352 }
353 }
354
355 void MemoryCache::setCapacities(size_t minDeadBytes,
356 size_t maxDeadBytes,
357 size_t totalBytes) {
358 DCHECK_LE(minDeadBytes, maxDeadBytes);
359 DCHECK_LE(maxDeadBytes, totalBytes);
360 m_minDeadCapacity = minDeadBytes;
361 m_maxDeadCapacity = maxDeadBytes;
362 m_maxDeferredPruneDeadCapacity =
363 cDeferredPruneDeadCapacityFactor * maxDeadBytes;
364 m_capacity = totalBytes; 270 m_capacity = totalBytes;
365 prune(); 271 prune();
366 } 272 }
367 273
368 void MemoryCache::evict(MemoryCacheEntry* entry) { 274 void MemoryCache::update(Resource* resource, size_t oldSize, size_t newSize) {
369 DCHECK(WTF::isMainThread());
370
371 Resource* resource = entry->resource();
372 DCHECK(resource);
373 RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for "
374 << resource->url().getString() << " from cache";
375 TRACE_EVENT1("blink", "MemoryCache::evict", "resource",
376 resource->url().getString().utf8());
377 // The resource may have already been removed by someone other than our
378 // caller, who needed a fresh copy for a reload. See
379 // <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
380 update(resource, resource->size(), 0, false);
381 removeFromLiveDecodedResourcesList(entry);
382
383 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier());
384 DCHECK(resources);
385 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
386 ResourceMap::iterator it = resources->find(url);
387 DCHECK_NE(it, resources->end());
388
389 MemoryCacheEntry* entryPtr = it->value;
390 resources->remove(it);
391 if (entryPtr)
392 entryPtr->dispose();
393 }
394
395 MemoryCacheEntry* MemoryCache::getEntryForResource(
396 const Resource* resource) const {
397 if (!resource || resource->url().isNull() || resource->url().isEmpty())
398 return nullptr;
399 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier());
400 if (!resources)
401 return nullptr;
402 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
403 MemoryCacheEntry* entry = resources->get(url);
404 if (!entry || entry->resource() != resource)
405 return nullptr;
406 return entry;
407 }
408
409 MemoryCacheLRUList* MemoryCache::lruListFor(unsigned accessCount, size_t size) {
410 DCHECK_GT(accessCount, 0u);
411 unsigned queueIndex = WTF::fastLog2(size / accessCount);
412 if (m_allResources.size() <= queueIndex)
413 m_allResources.grow(queueIndex + 1);
414 return &m_allResources[queueIndex];
415 }
416
417 void MemoryCache::removeFromLRUList(MemoryCacheEntry* entry,
418 MemoryCacheLRUList* list) {
419 DCHECK(containedInLRUList(entry, list));
420
421 MemoryCacheEntry* next = entry->m_nextInAllResourcesList;
422 MemoryCacheEntry* previous = entry->m_previousInAllResourcesList;
423 entry->m_nextInAllResourcesList = nullptr;
424 entry->m_previousInAllResourcesList = nullptr;
425
426 if (next)
427 next->m_previousInAllResourcesList = previous;
428 else
429 list->m_tail = previous;
430
431 if (previous)
432 previous->m_nextInAllResourcesList = next;
433 else
434 list->m_head = next;
435
436 DCHECK(!containedInLRUList(entry, list));
437 }
438
439 void MemoryCache::insertInLRUList(MemoryCacheEntry* entry,
440 MemoryCacheLRUList* list) {
441 DCHECK(!containedInLRUList(entry, list));
442
443 entry->m_nextInAllResourcesList = list->m_head;
444 list->m_head = entry;
445
446 if (entry->m_nextInAllResourcesList)
447 entry->m_nextInAllResourcesList->m_previousInAllResourcesList = entry;
448 else
449 list->m_tail = entry;
450
451 DCHECK(containedInLRUList(entry, list));
452 }
453
454 bool MemoryCache::containedInLRUList(MemoryCacheEntry* entry,
455 MemoryCacheLRUList* list) {
456 for (MemoryCacheEntry* current = list->m_head; current;
457 current = current->m_nextInAllResourcesList) {
458 if (current == entry)
459 return true;
460 }
461 DCHECK(!entry->m_nextInAllResourcesList);
462 DCHECK(!entry->m_previousInAllResourcesList);
463 return false;
464 }
465
466 void MemoryCache::removeFromLiveDecodedResourcesList(MemoryCacheEntry* entry) {
467 // If we've never been accessed, then we're brand new and not in any list.
468 if (!entry->m_inLiveDecodedResourcesList)
469 return;
470 DCHECK(containedInLiveDecodedResourcesList(entry));
471
472 entry->m_inLiveDecodedResourcesList = false;
473
474 MemoryCacheEntry* next = entry->m_nextInLiveResourcesList;
475 MemoryCacheEntry* previous = entry->m_previousInLiveResourcesList;
476
477 entry->m_nextInLiveResourcesList = nullptr;
478 entry->m_previousInLiveResourcesList = nullptr;
479
480 if (next)
481 next->m_previousInLiveResourcesList = previous;
482 else
483 m_liveDecodedResources.m_tail = previous;
484
485 if (previous)
486 previous->m_nextInLiveResourcesList = next;
487 else
488 m_liveDecodedResources.m_head = next;
489
490 DCHECK(!containedInLiveDecodedResourcesList(entry));
491 }
492
493 void MemoryCache::insertInLiveDecodedResourcesList(MemoryCacheEntry* entry) {
494 DCHECK(!containedInLiveDecodedResourcesList(entry));
495
496 entry->m_inLiveDecodedResourcesList = true;
497
498 entry->m_nextInLiveResourcesList = m_liveDecodedResources.m_head;
499 if (m_liveDecodedResources.m_head)
500 m_liveDecodedResources.m_head->m_previousInLiveResourcesList = entry;
501 m_liveDecodedResources.m_head = entry;
502
503 if (!entry->m_nextInLiveResourcesList)
504 m_liveDecodedResources.m_tail = entry;
505
506 DCHECK(containedInLiveDecodedResourcesList(entry));
507 }
508
509 bool MemoryCache::containedInLiveDecodedResourcesList(MemoryCacheEntry* entry) {
510 for (MemoryCacheEntry* current = m_liveDecodedResources.m_head; current;
511 current = current->m_nextInLiveResourcesList) {
512 if (current == entry) {
513 DCHECK(entry->m_inLiveDecodedResourcesList);
514 return true;
515 }
516 }
517 DCHECK(!entry->m_nextInLiveResourcesList);
518 DCHECK(!entry->m_previousInLiveResourcesList);
519 DCHECK(!entry->m_inLiveDecodedResourcesList);
520 return false;
521 }
522
523 void MemoryCache::makeLive(Resource* resource) {
524 if (!contains(resource)) 275 if (!contains(resource))
525 return; 276 return;
526 DCHECK_GE(m_deadSize, resource->size());
527 m_liveSize += resource->size();
528 m_deadSize -= resource->size();
529 }
530
531 void MemoryCache::makeDead(Resource* resource) {
532 if (!contains(resource))
533 return;
534 m_liveSize -= resource->size();
535 m_deadSize += resource->size();
536 removeFromLiveDecodedResourcesList(getEntryForResource(resource));
537 }
538
539 void MemoryCache::update(Resource* resource,
540 size_t oldSize,
541 size_t newSize,
542 bool wasAccessed) {
543 MemoryCacheEntry* entry = getEntryForResource(resource);
544 if (!entry)
545 return;
546
547 // The object must now be moved to a different queue, since either its size or
548 // its accessCount has been changed, and both of those are used to determine
549 // which LRU queue the resource should be in.
550 if (oldSize)
551 removeFromLRUList(entry, lruListFor(entry->m_accessCount, oldSize));
552 if (wasAccessed)
553 entry->m_accessCount++;
554 if (newSize)
555 insertInLRUList(entry, lruListFor(entry->m_accessCount, newSize));
556
557 ptrdiff_t delta = newSize - oldSize; 277 ptrdiff_t delta = newSize - oldSize;
558 if (resource->isAlive()) { 278 DCHECK(delta >= 0 || m_size >= static_cast<size_t>(-delta));
559 DCHECK(delta >= 0 || m_liveSize >= static_cast<size_t>(-delta)); 279 m_size += delta;
560 m_liveSize += delta;
561 } else {
562 DCHECK(delta >= 0 || m_deadSize >= static_cast<size_t>(-delta));
563 m_deadSize += delta;
564 }
565 }
566
567 void MemoryCache::updateDecodedResource(Resource* resource,
568 UpdateReason reason) {
569 MemoryCacheEntry* entry = getEntryForResource(resource);
570 if (!entry)
571 return;
572
573 removeFromLiveDecodedResourcesList(entry);
574 if (resource->decodedSize() && resource->isAlive())
575 insertInLiveDecodedResourcesList(entry);
576
577 if (reason != UpdateForAccess)
578 return;
579
580 double timestamp = resource->isImage() ? m_lastFramePaintTimeStamp : 0.0;
581 if (!timestamp)
582 timestamp = currentTime();
583 entry->m_lastDecodedAccessTime = timestamp;
584 } 280 }
585 281
586 void MemoryCache::removeURLFromCache(const KURL& url) { 282 void MemoryCache::removeURLFromCache(const KURL& url) {
587 HeapVector<Member<Resource>> resources = resourcesForURL(url); 283 HeapVector<Member<Resource>> resources = resourcesForURL(url);
588 for (Resource* resource : resources) 284 for (Resource* resource : resources)
589 memoryCache()->remove(resource); 285 remove(resource);
590 } 286 }
591 287
592 void MemoryCache::TypeStatistic::addResource(Resource* o) { 288 void MemoryCache::TypeStatistic::addResource(Resource* o) {
593 count++; 289 count++;
594 size += o->size(); 290 size += o->size();
595 liveSize += o->isAlive() ? o->size() : 0;
596 decodedSize += o->decodedSize(); 291 decodedSize += o->decodedSize();
597 encodedSize += o->encodedSize(); 292 encodedSize += o->encodedSize();
598 overheadSize += o->overheadSize(); 293 overheadSize += o->overheadSize();
599 encodedSizeDuplicatedInDataURLs += 294 encodedSizeDuplicatedInDataURLs +=
600 o->url().protocolIsData() ? o->encodedSize() : 0; 295 o->url().protocolIsData() ? o->encodedSize() : 0;
601 } 296 }
602 297
603 MemoryCache::Statistics MemoryCache::getStatistics() { 298 MemoryCache::Statistics MemoryCache::getStatistics() const {
604 Statistics stats; 299 Statistics stats;
605 for (const auto& resourceMapIter : m_resourceMaps) { 300 for (const auto& resourceMapIter : m_resourceMaps) {
606 for (const auto& resourceIter : *resourceMapIter.value) { 301 for (const auto& resourceIter : *resourceMapIter.value) {
607 Resource* resource = resourceIter.value->resource(); 302 Resource* resource = resourceIter.value->resource();
608 DCHECK(resource); 303 DCHECK(resource);
609 switch (resource->getType()) { 304 switch (resource->getType()) {
610 case Resource::Image: 305 case Resource::Image:
611 stats.images.addResource(resource); 306 stats.images.addResource(resource);
612 break; 307 break;
613 case Resource::CSSStyleSheet: 308 case Resource::CSSStyleSheet:
(...skipping 20 matching lines...) Expand all
634 void MemoryCache::evictResources(EvictResourcePolicy policy) { 329 void MemoryCache::evictResources(EvictResourcePolicy policy) {
635 for (auto resourceMapIter = m_resourceMaps.begin(); 330 for (auto resourceMapIter = m_resourceMaps.begin();
636 resourceMapIter != m_resourceMaps.end();) { 331 resourceMapIter != m_resourceMaps.end();) {
637 ResourceMap* resources = resourceMapIter->value.get(); 332 ResourceMap* resources = resourceMapIter->value.get();
638 HeapVector<Member<MemoryCacheEntry>> unusedPreloads; 333 HeapVector<Member<MemoryCacheEntry>> unusedPreloads;
639 for (auto resourceIter = resources->begin(); 334 for (auto resourceIter = resources->begin();
640 resourceIter != resources->end(); resourceIter = resources->begin()) { 335 resourceIter != resources->end(); resourceIter = resources->begin()) {
641 DCHECK(resourceIter.get()); 336 DCHECK(resourceIter.get());
642 DCHECK(resourceIter->value.get()); 337 DCHECK(resourceIter->value.get());
643 DCHECK(resourceIter->value->resource()); 338 DCHECK(resourceIter->value->resource());
644 if (policy == EvictAllResources || 339 Resource* resource = resourceIter->value->resource();
645 !(resourceIter->value->resource() && 340 DCHECK(resource);
646 resourceIter->value->resource()->isUnusedPreload())) { 341 if (policy != EvictAllResources && resource->isUnusedPreload()) {
647 evict(resourceIter->value.get());
648 } else {
649 // Store unused preloads aside, so they could be added back later. 342 // Store unused preloads aside, so they could be added back later.
650 // That is in order to avoid the performance impact of iterating over 343 // That is in order to avoid the performance impact of iterating over
651 // the same resource multiple times. 344 // the same resource multiple times.
652 unusedPreloads.append(resourceIter->value.get()); 345 unusedPreloads.append(resourceIter->value.get());
653 resources->remove(resourceIter);
654 } 346 }
347 removeInternal(resources, resourceIter);
655 } 348 }
656 for (const auto& unusedPreload : unusedPreloads) { 349 for (const auto& unusedPreload : unusedPreloads) {
657 KURL url = 350 addInternal(resources, unusedPreload);
658 removeFragmentIdentifierIfNeeded(unusedPreload->resource()->url());
659 resources->set(url, unusedPreload);
660 } 351 }
661 // We may iterate multiple times over resourceMaps with unused preloads. 352 // We may iterate multiple times over resourceMaps with unused preloads.
662 // That's extremely unlikely to have any real-life performance impact. 353 // That's extremely unlikely to have any real-life performance impact.
663 if (!resources->size()) { 354 if (!resources->size()) {
664 m_resourceMaps.remove(resourceMapIter); 355 m_resourceMaps.remove(resourceMapIter);
665 resourceMapIter = m_resourceMaps.begin(); 356 resourceMapIter = m_resourceMaps.begin();
666 } else { 357 } else {
667 ++resourceMapIter; 358 ++resourceMapIter;
668 } 359 }
669 } 360 }
670 } 361 }
671 362
672 void MemoryCache::prune() { 363 void MemoryCache::prune() {
673 TRACE_EVENT0("renderer", "MemoryCache::prune()"); 364 TRACE_EVENT0("renderer", "MemoryCache::prune()");
674 365
675 if (m_inPruneResources) 366 if (m_inPruneResources)
676 return; 367 return;
677 if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && 368 if (m_size <= m_capacity) // Fast path.
678 m_deadSize <= m_maxDeadCapacity) // Fast path.
679 return; 369 return;
680 370
681 // To avoid burdening the current thread with repetitive pruning jobs, pruning 371 // To avoid burdening the current thread with repetitive pruning jobs, pruning
682 // is postponed until the end of the current task. If it has been more than 372 // is postponed until the end of the current task. If it has been more than
683 // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If 373 // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If
684 // the current thread's run loop is not active, then pruning will happen 374 // the current thread's run loop is not active, then pruning will happen
685 // immediately only if it has been over m_maxPruneDeferralDelay since the last 375 // immediately only if it has been over m_maxPruneDeferralDelay since the last
686 // prune. 376 // prune.
687 double currentTime = WTF::currentTime(); 377 double currentTime = WTF::currentTime();
688 if (m_prunePending) { 378 if (m_prunePending) {
(...skipping 25 matching lines...) Expand all
714 } 404 }
715 405
716 void MemoryCache::pruneNow(double currentTime, PruneStrategy strategy) { 406 void MemoryCache::pruneNow(double currentTime, PruneStrategy strategy) {
717 if (m_prunePending) { 407 if (m_prunePending) {
718 m_prunePending = false; 408 m_prunePending = false;
719 Platform::current()->currentThread()->removeTaskObserver(this); 409 Platform::current()->currentThread()->removeTaskObserver(this);
720 } 410 }
721 411
722 AutoReset<bool> reentrancyProtector(&m_inPruneResources, true); 412 AutoReset<bool> reentrancyProtector(&m_inPruneResources, true);
723 413
724 // Prune dead first, in case it was "borrowing" capacity from live. 414 pruneResources(strategy);
725 pruneDeadResources(strategy);
726 pruneLiveResources(strategy);
727 m_pruneFrameTimeStamp = m_lastFramePaintTimeStamp; 415 m_pruneFrameTimeStamp = m_lastFramePaintTimeStamp;
728 m_pruneTimeStamp = currentTime; 416 m_pruneTimeStamp = currentTime;
729 } 417 }
730 418
731 void MemoryCache::updateFramePaintTimestamp() { 419 void MemoryCache::updateFramePaintTimestamp() {
732 m_lastFramePaintTimeStamp = currentTime(); 420 m_lastFramePaintTimeStamp = currentTime();
733 } 421 }
734 422
735 bool MemoryCache::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, 423 bool MemoryCache::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail,
736 WebProcessMemoryDump* memoryDump) { 424 WebProcessMemoryDump* memoryDump) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
769 resource->onMemoryDump(levelOfDetail, memoryDump); 457 resource->onMemoryDump(levelOfDetail, memoryDump);
770 } 458 }
771 } 459 }
772 return true; 460 return true;
773 } 461 }
774 462
775 void MemoryCache::onMemoryPressure(WebMemoryPressureLevel level) { 463 void MemoryCache::onMemoryPressure(WebMemoryPressureLevel level) {
776 pruneAll(); 464 pruneAll();
777 } 465 }
778 466
779 bool MemoryCache::isInSameLRUListForTest(const Resource* x, const Resource* y) {
780 MemoryCacheEntry* ex = getEntryForResource(x);
781 MemoryCacheEntry* ey = getEntryForResource(y);
782 DCHECK(ex);
783 DCHECK(ey);
784 return lruListFor(ex->m_accessCount, x->size()) ==
785 lruListFor(ey->m_accessCount, y->size());
786 }
787
788 } // namespace blink 467 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/fetch/MemoryCache.h ('k') | third_party/WebKit/Source/core/fetch/MemoryCacheTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698