| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | |
| 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | |
| 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | |
| 5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
| 6 | |
| 7 This library is free software; you can redistribute it and/or | |
| 8 modify it under the terms of the GNU Library General Public | |
| 9 License as published by the Free Software Foundation; either | |
| 10 version 2 of the License, or (at your option) any later version. | |
| 11 | |
| 12 This library is distributed in the hope that it will be useful, | |
| 13 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 15 Library General Public License for more details. | |
| 16 | |
| 17 You should have received a copy of the GNU Library General Public License | |
| 18 along with this library; see the file COPYING.LIB. If not, write to | |
| 19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| 20 Boston, MA 02110-1301, USA. | |
| 21 */ | |
| 22 | |
| 23 #include "sky/engine/core/fetch/MemoryCache.h" | |
| 24 | |
| 25 #include "base/bind.h" | |
| 26 #include "sky/engine/core/dom/Microtask.h" | |
| 27 #include "sky/engine/core/fetch/ResourcePtr.h" | |
| 28 #include "sky/engine/core/frame/FrameView.h" | |
| 29 #include "sky/engine/platform/Logging.h" | |
| 30 #include "sky/engine/platform/TraceEvent.h" | |
| 31 #include "sky/engine/wtf/Assertions.h" | |
| 32 #include "sky/engine/wtf/CurrentTime.h" | |
| 33 #include "sky/engine/wtf/MainThread.h" | |
| 34 #include "sky/engine/wtf/MathExtras.h" | |
| 35 #include "sky/engine/wtf/TemporaryChange.h" | |
| 36 #include "sky/engine/wtf/text/CString.h" | |
| 37 | |
| 38 namespace blink { | |
| 39 | |
| 40 static OwnPtr<MemoryCache>* gMemoryCache; | |
| 41 | |
| 42 static const unsigned cDefaultCacheCapacity = 8192 * 1024; | |
| 43 static const unsigned cDeferredPruneDeadCapacityFactor = 2; | |
| 44 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. | |
| 45 static const double cMaxPruneDeferralDelay = 0.5; // Seconds. | |
| 46 static const float cTargetPrunePercentage = .95f; // Percentage of capacity towa
rd which we prune, to avoid immediately pruning again. | |
| 47 | |
| 48 MemoryCache* memoryCache() | |
| 49 { | |
| 50 ASSERT(WTF::isMainThread()); | |
| 51 if (!gMemoryCache) | |
| 52 gMemoryCache = new OwnPtr<MemoryCache>(MemoryCache::create()); | |
| 53 return gMemoryCache->get(); | |
| 54 } | |
| 55 | |
| 56 PassOwnPtr<MemoryCache> replaceMemoryCacheForTesting(PassOwnPtr<MemoryCache> cac
he) | |
| 57 { | |
| 58 // Make sure we have non-empty gMemoryCache. | |
| 59 memoryCache(); | |
| 60 OwnPtr<MemoryCache> oldCache = gMemoryCache->release(); | |
| 61 *gMemoryCache = cache; | |
| 62 return oldCache.release(); | |
| 63 } | |
| 64 | |
| 65 inline MemoryCache::MemoryCache() | |
| 66 : m_inPruneResources(false) | |
| 67 , m_maxPruneDeferralDelay(cMaxPruneDeferralDelay) | |
| 68 , m_capacity(cDefaultCacheCapacity) | |
| 69 , m_minDeadCapacity(0) | |
| 70 , m_maxDeadCapacity(cDefaultCacheCapacity) | |
| 71 , m_maxDeferredPruneDeadCapacity(cDeferredPruneDeadCapacityFactor * cDefault
CacheCapacity) | |
| 72 , m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune) | |
| 73 , m_liveSize(0) | |
| 74 , m_deadSize(0) | |
| 75 #ifdef MEMORY_CACHE_STATS | |
| 76 , m_statsTimer(this, &MemoryCache::dumpStats) | |
| 77 #endif | |
| 78 { | |
| 79 #ifdef MEMORY_CACHE_STATS | |
| 80 const double statsIntervalInSeconds = 15; | |
| 81 m_statsTimer.startRepeating(statsIntervalInSeconds, FROM_HERE); | |
| 82 #endif | |
| 83 m_pruneTimeStamp = m_pruneFrameTimeStamp = FrameView::currentFrameTimeStamp(
); | |
| 84 } | |
| 85 | |
| 86 PassOwnPtr<MemoryCache> MemoryCache::create() | |
| 87 { | |
| 88 return adoptPtr(new MemoryCache()); | |
| 89 } | |
| 90 | |
| 91 MemoryCache::~MemoryCache() | |
| 92 { | |
| 93 } | |
| 94 | |
| 95 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) | |
| 96 { | |
| 97 if (!originalURL.hasFragmentIdentifier()) | |
| 98 return originalURL; | |
| 99 // Strip away fragment identifier from HTTP URLs. | |
| 100 // Data URLs must be unmodified. For file and custom URLs clients may expect
resources | |
| 101 // to be unique even when they differ by the fragment identifier only. | |
| 102 if (!originalURL.protocolIsInHTTPFamily()) | |
| 103 return originalURL; | |
| 104 KURL url = originalURL; | |
| 105 url.removeFragmentIdentifier(); | |
| 106 return url; | |
| 107 } | |
| 108 | |
| 109 void MemoryCache::add(Resource* resource) | |
| 110 { | |
| 111 ASSERT(WTF::isMainThread()); | |
| 112 ASSERT(resource->url().isValid()); | |
| 113 RELEASE_ASSERT(!m_resources.contains(resource->url())); | |
| 114 m_resources.set(resource->url().string(), MemoryCacheEntry::create(resource)
); | |
| 115 update(resource, 0, resource->size(), true); | |
| 116 | |
| 117 WTF_LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resou
rce->url().string().latin1().data(), resource); | |
| 118 } | |
| 119 | |
| 120 void MemoryCache::replace(Resource* newResource, Resource* oldResource) | |
| 121 { | |
| 122 if (MemoryCacheEntry* oldEntry = m_resources.get(oldResource->url())) | |
| 123 evict(oldEntry); | |
| 124 add(newResource); | |
| 125 if (newResource->decodedSize() && newResource->hasClients()) | |
| 126 insertInLiveDecodedResourcesList(m_resources.get(newResource->url())); | |
| 127 } | |
| 128 | |
| 129 void MemoryCache::remove(Resource* resource) | |
| 130 { | |
| 131 // The resource may have already been removed by someone other than our call
er, | |
| 132 // who needed a fresh copy for a reload. | |
| 133 if (!contains(resource)) | |
| 134 return; | |
| 135 evict(m_resources.get(resource->url())); | |
| 136 } | |
| 137 | |
| 138 bool MemoryCache::contains(const Resource* resource) const | |
| 139 { | |
| 140 if (resource->url().isNull()) | |
| 141 return false; | |
| 142 const MemoryCacheEntry* entry = m_resources.get(resource->url()); | |
| 143 return entry && entry->m_resource == resource; | |
| 144 } | |
| 145 | |
| 146 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) | |
| 147 { | |
| 148 ASSERT(WTF::isMainThread()); | |
| 149 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); | |
| 150 MemoryCacheEntry* entry = m_resources.get(url); | |
| 151 if (!entry) | |
| 152 return 0; | |
| 153 Resource* resource = entry->m_resource.get(); | |
| 154 if (resource && !resource->lock()) { | |
| 155 ASSERT(!resource->hasClients()); | |
| 156 bool didEvict = evict(entry); | |
| 157 ASSERT_UNUSED(didEvict, didEvict); | |
| 158 return 0; | |
| 159 } | |
| 160 return resource; | |
| 161 } | |
| 162 | |
| 163 size_t MemoryCache::deadCapacity() const | |
| 164 { | |
| 165 // Dead resource capacity is whatever space is not occupied by live resource
s, bounded by an independent minimum and maximum. | |
| 166 size_t capacity = m_capacity - std::min(m_liveSize, m_capacity); // Start wi
th available capacity. | |
| 167 capacity = std::max(capacity, m_minDeadCapacity); // Make sure it's above th
e minimum. | |
| 168 capacity = std::min(capacity, m_maxDeadCapacity); // Make sure it's below th
e maximum. | |
| 169 return capacity; | |
| 170 } | |
| 171 | |
| 172 size_t MemoryCache::liveCapacity() const | |
| 173 { | |
| 174 // Live resource capacity is whatever is left over after calculating dead re
source capacity. | |
| 175 return m_capacity - deadCapacity(); | |
| 176 } | |
| 177 | |
| 178 void MemoryCache::pruneLiveResources() | |
| 179 { | |
| 180 ASSERT(!prunePending()); | |
| 181 size_t capacity = liveCapacity(); | |
| 182 if (!m_liveSize || (capacity && m_liveSize <= capacity)) | |
| 183 return; | |
| 184 | |
| 185 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage);
// Cut by a percentage to avoid immediately pruning again. | |
| 186 | |
| 187 // Destroy any decoded data in live objects that we can. | |
| 188 // Start from the tail, since this is the lowest priority | |
| 189 // and least recently accessed of the objects. | |
| 190 | |
| 191 // The list might not be sorted by the m_lastDecodedFrameTimeStamp. The impa
ct | |
| 192 // of this weaker invariant is minor as the below if statement to check the | |
| 193 // elapsedTime will evaluate to false as the current time will be a lot | |
| 194 // greater than the current->m_lastDecodedFrameTimeStamp. | |
| 195 // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 | |
| 196 | |
| 197 // Start pruning from the lowest priority list. | |
| 198 for (int priority = MemoryCacheLiveResourcePriorityLow; priority <= MemoryCa
cheLiveResourcePriorityHigh; ++priority) { | |
| 199 MemoryCacheEntry* current = m_liveDecodedResources[priority].m_tail; | |
| 200 while (current) { | |
| 201 MemoryCacheEntry* previous = current->m_previousInLiveResourcesList; | |
| 202 ASSERT(current->m_resource->hasClients()); | |
| 203 if (current->m_resource->isLoaded() && current->m_resource->decodedS
ize()) { | |
| 204 // Check to see if the remaining resources are too new to prune. | |
| 205 double elapsedTime = m_pruneFrameTimeStamp - current->m_lastDeco
dedAccessTime; | |
| 206 if (elapsedTime < m_delayBeforeLiveDecodedPrune) | |
| 207 return; | |
| 208 | |
| 209 // Destroy our decoded data if possible. This will remove us | |
| 210 // from m_liveDecodedResources, and possibly move us to a | |
| 211 // different LRU list in m_allResources. | |
| 212 current->m_resource->prune(); | |
| 213 | |
| 214 if (targetSize && m_liveSize <= targetSize) | |
| 215 return; | |
| 216 } | |
| 217 current = previous; | |
| 218 } | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void MemoryCache::pruneDeadResources() | |
| 223 { | |
| 224 size_t capacity = deadCapacity(); | |
| 225 if (!m_deadSize || (capacity && m_deadSize <= capacity)) | |
| 226 return; | |
| 227 | |
| 228 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage);
// Cut by a percentage to avoid immediately pruning again. | |
| 229 | |
| 230 int size = m_allResources.size(); | |
| 231 | |
| 232 // See if we have any purged resources we can evict. | |
| 233 for (int i = 0; i < size; i++) { | |
| 234 MemoryCacheEntry* current = m_allResources[i].m_tail; | |
| 235 while (current) { | |
| 236 MemoryCacheEntry* previous = current->m_previousInAllResourcesList; | |
| 237 // Main Resources in the cache are only substitue data that was | |
| 238 // precached and should not be evicted. | |
| 239 if (current->m_resource->wasPurged() && current->m_resource->canDele
te() | |
| 240 && current->m_resource->type() != Resource::MainResource) { | |
| 241 ASSERT(!current->m_resource->hasClients()); | |
| 242 bool wasEvicted = evict(current); | |
| 243 ASSERT_UNUSED(wasEvicted, wasEvicted); | |
| 244 } | |
| 245 current = previous; | |
| 246 } | |
| 247 } | |
| 248 if (targetSize && m_deadSize <= targetSize) | |
| 249 return; | |
| 250 | |
| 251 bool canShrinkLRULists = true; | |
| 252 for (int i = size - 1; i >= 0; i--) { | |
| 253 // Remove from the tail, since this is the least frequently accessed of
the objects. | |
| 254 MemoryCacheEntry* current = m_allResources[i].m_tail; | |
| 255 | |
| 256 // First flush all the decoded data in this queue. | |
| 257 while (current) { | |
| 258 // Protect 'previous' so it can't get deleted during destroyDecodedD
ata(). | |
| 259 MemoryCacheEntry* previous = current->m_previousInAllResourcesList; | |
| 260 ASSERT(!previous || contains(previous->m_resource.get())); | |
| 261 if (!current->m_resource->hasClients() && current->m_resource->isLoa
ded()) { | |
| 262 // Destroy our decoded data. This will remove us from | |
| 263 // m_liveDecodedResources, and possibly move us to a different | |
| 264 // LRU list in m_allResources. | |
| 265 current->m_resource->prune(); | |
| 266 | |
| 267 if (targetSize && m_deadSize <= targetSize) | |
| 268 return; | |
| 269 } | |
| 270 // Decoded data may reference other resources. Stop iterating if 'pr
evious' somehow got | |
| 271 // kicked out of cache during destroyDecodedData(). | |
| 272 if (previous && !contains(previous->m_resource.get())) | |
| 273 break; | |
| 274 current = previous; | |
| 275 } | |
| 276 | |
| 277 // Now evict objects from this queue. | |
| 278 current = m_allResources[i].m_tail; | |
| 279 while (current) { | |
| 280 MemoryCacheEntry* previous = current->m_previousInAllResourcesList; | |
| 281 ASSERT(!previous || contains(previous->m_resource.get())); | |
| 282 if (!current->m_resource->hasClients() | |
| 283 && !current->m_resource->isCacheValidator() && current->m_resour
ce->canDelete() | |
| 284 && current->m_resource->type() != Resource::MainResource) { | |
| 285 // Main Resources in the cache are only substitue data that was | |
| 286 // precached and should not be evicted. | |
| 287 bool wasEvicted = evict(current); | |
| 288 ASSERT_UNUSED(wasEvicted, wasEvicted); | |
| 289 if (targetSize && m_deadSize <= targetSize) | |
| 290 return; | |
| 291 } | |
| 292 if (previous && !contains(previous->m_resource.get())) | |
| 293 break; | |
| 294 current = previous; | |
| 295 } | |
| 296 | |
| 297 // Shrink the vector back down so we don't waste time inspecting | |
| 298 // empty LRU lists on future prunes. | |
| 299 if (m_allResources[i].m_head) | |
| 300 canShrinkLRULists = false; | |
| 301 else if (canShrinkLRULists) | |
| 302 m_allResources.resize(i); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 void MemoryCache::setCapacities(size_t minDeadBytes, size_t maxDeadBytes, size_t
totalBytes) | |
| 307 { | |
| 308 ASSERT(minDeadBytes <= maxDeadBytes); | |
| 309 ASSERT(maxDeadBytes <= totalBytes); | |
| 310 m_minDeadCapacity = minDeadBytes; | |
| 311 m_maxDeadCapacity = maxDeadBytes; | |
| 312 m_maxDeferredPruneDeadCapacity = cDeferredPruneDeadCapacityFactor * maxDeadB
ytes; | |
| 313 m_capacity = totalBytes; | |
| 314 prune(); | |
| 315 } | |
| 316 | |
| 317 bool MemoryCache::evict(MemoryCacheEntry* entry) | |
| 318 { | |
| 319 ASSERT(WTF::isMainThread()); | |
| 320 | |
| 321 Resource* resource = entry->m_resource.get(); | |
| 322 bool canDelete = resource->canDelete(); | |
| 323 WTF_LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resourc
e, resource->url().string().latin1().data()); | |
| 324 // The resource may have already been removed by someone other than our call
er, | |
| 325 // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bu
g.cgi?id=12479#c6>. | |
| 326 update(resource, resource->size(), 0, false); | |
| 327 removeFromLiveDecodedResourcesList(entry); | |
| 328 | |
| 329 ResourceMap::iterator it = m_resources.find(resource->url()); | |
| 330 ASSERT(it != m_resources.end()); | |
| 331 #if !ENABLE(OILPAN) | |
| 332 OwnPtr<MemoryCacheEntry> entryPtr; | |
| 333 entryPtr.swap(it->value); | |
| 334 #endif | |
| 335 m_resources.remove(it); | |
| 336 return canDelete; | |
| 337 } | |
| 338 | |
| 339 MemoryCacheLRUList* MemoryCache::lruListFor(unsigned accessCount, size_t size) | |
| 340 { | |
| 341 ASSERT(accessCount > 0); | |
| 342 unsigned queueIndex = WTF::fastLog2(size / accessCount); | |
| 343 if (m_allResources.size() <= queueIndex) | |
| 344 m_allResources.grow(queueIndex + 1); | |
| 345 return &m_allResources[queueIndex]; | |
| 346 } | |
| 347 | |
| 348 void MemoryCache::removeFromLRUList(MemoryCacheEntry* entry, MemoryCacheLRUList*
list) | |
| 349 { | |
| 350 #if ENABLE(ASSERT) | |
| 351 // Verify that we are in fact in this list. | |
| 352 bool found = false; | |
| 353 for (MemoryCacheEntry* current = list->m_head; current; current = current->m
_nextInAllResourcesList) { | |
| 354 if (current == entry) { | |
| 355 found = true; | |
| 356 break; | |
| 357 } | |
| 358 } | |
| 359 ASSERT(found); | |
| 360 #endif | |
| 361 | |
| 362 MemoryCacheEntry* next = entry->m_nextInAllResourcesList; | |
| 363 MemoryCacheEntry* previous = entry->m_previousInAllResourcesList; | |
| 364 entry->m_nextInAllResourcesList = nullptr; | |
| 365 entry->m_previousInAllResourcesList = nullptr; | |
| 366 | |
| 367 if (next) | |
| 368 next->m_previousInAllResourcesList = previous; | |
| 369 else | |
| 370 list->m_tail = previous; | |
| 371 | |
| 372 if (previous) | |
| 373 previous->m_nextInAllResourcesList = next; | |
| 374 else | |
| 375 list->m_head = next; | |
| 376 } | |
| 377 | |
| 378 void MemoryCache::insertInLRUList(MemoryCacheEntry* entry, MemoryCacheLRUList* l
ist) | |
| 379 { | |
| 380 ASSERT(!entry->m_nextInAllResourcesList && !entry->m_previousInAllResourcesL
ist); | |
| 381 | |
| 382 entry->m_nextInAllResourcesList = list->m_head; | |
| 383 list->m_head = entry; | |
| 384 | |
| 385 if (entry->m_nextInAllResourcesList) | |
| 386 entry->m_nextInAllResourcesList->m_previousInAllResourcesList = entry; | |
| 387 else | |
| 388 list->m_tail = entry; | |
| 389 | |
| 390 #if ENABLE(ASSERT) | |
| 391 // Verify that we are in now in the list like we should be. | |
| 392 bool found = false; | |
| 393 for (MemoryCacheEntry* current = list->m_head; current; current = current->m
_nextInAllResourcesList) { | |
| 394 if (current == entry) { | |
| 395 found = true; | |
| 396 break; | |
| 397 } | |
| 398 } | |
| 399 ASSERT(found); | |
| 400 #endif | |
| 401 } | |
| 402 | |
| 403 void MemoryCache::removeFromLiveDecodedResourcesList(MemoryCacheEntry* entry) | |
| 404 { | |
| 405 // If we've never been accessed, then we're brand new and not in any list. | |
| 406 if (!entry->m_inLiveDecodedResourcesList) | |
| 407 return; | |
| 408 entry->m_inLiveDecodedResourcesList = false; | |
| 409 | |
| 410 MemoryCacheLRUList* list = &m_liveDecodedResources[entry->m_liveResourcePrio
rity]; | |
| 411 | |
| 412 #if ENABLE(ASSERT) | |
| 413 // Verify that we are in fact in this list. | |
| 414 bool found = false; | |
| 415 for (MemoryCacheEntry* current = list->m_head; current; current = current->m
_nextInLiveResourcesList) { | |
| 416 if (current == entry) { | |
| 417 found = true; | |
| 418 break; | |
| 419 } | |
| 420 } | |
| 421 ASSERT(found); | |
| 422 #endif | |
| 423 | |
| 424 MemoryCacheEntry* next = entry->m_nextInLiveResourcesList; | |
| 425 MemoryCacheEntry* previous = entry->m_previousInLiveResourcesList; | |
| 426 | |
| 427 entry->m_nextInLiveResourcesList = nullptr; | |
| 428 entry->m_previousInLiveResourcesList = nullptr; | |
| 429 | |
| 430 if (next) | |
| 431 next->m_previousInLiveResourcesList = previous; | |
| 432 else | |
| 433 list->m_tail = previous; | |
| 434 | |
| 435 if (previous) | |
| 436 previous->m_nextInLiveResourcesList = next; | |
| 437 else | |
| 438 list->m_head = next; | |
| 439 } | |
| 440 | |
| 441 void MemoryCache::insertInLiveDecodedResourcesList(MemoryCacheEntry* entry) | |
| 442 { | |
| 443 // Make sure we aren't in the list already. | |
| 444 ASSERT(!entry->m_nextInLiveResourcesList && !entry->m_previousInLiveResource
sList && !entry->m_inLiveDecodedResourcesList); | |
| 445 entry->m_inLiveDecodedResourcesList = true; | |
| 446 | |
| 447 MemoryCacheLRUList* list = &m_liveDecodedResources[entry->m_liveResourcePrio
rity]; | |
| 448 entry->m_nextInLiveResourcesList = list->m_head; | |
| 449 if (list->m_head) | |
| 450 list->m_head->m_previousInLiveResourcesList = entry; | |
| 451 list->m_head = entry; | |
| 452 | |
| 453 if (!entry->m_nextInLiveResourcesList) | |
| 454 list->m_tail = entry; | |
| 455 | |
| 456 #if ENABLE(ASSERT) | |
| 457 // Verify that we are in now in the list like we should be. | |
| 458 bool found = false; | |
| 459 for (MemoryCacheEntry* current = list->m_head; current; current = current->m
_nextInLiveResourcesList) { | |
| 460 if (current == entry) { | |
| 461 found = true; | |
| 462 break; | |
| 463 } | |
| 464 } | |
| 465 ASSERT(found); | |
| 466 #endif | |
| 467 } | |
| 468 | |
| 469 void MemoryCache::makeLive(Resource* resource) | |
| 470 { | |
| 471 if (!contains(resource)) | |
| 472 return; | |
| 473 ASSERT(m_deadSize >= resource->size()); | |
| 474 m_liveSize += resource->size(); | |
| 475 m_deadSize -= resource->size(); | |
| 476 } | |
| 477 | |
| 478 void MemoryCache::makeDead(Resource* resource) | |
| 479 { | |
| 480 if (!contains(resource)) | |
| 481 return; | |
| 482 m_liveSize -= resource->size(); | |
| 483 m_deadSize += resource->size(); | |
| 484 removeFromLiveDecodedResourcesList(m_resources.get(resource->url())); | |
| 485 } | |
| 486 | |
| 487 void MemoryCache::update(Resource* resource, size_t oldSize, size_t newSize, boo
l wasAccessed) | |
| 488 { | |
| 489 if (!contains(resource)) | |
| 490 return; | |
| 491 MemoryCacheEntry* entry = m_resources.get(resource->url()); | |
| 492 | |
| 493 // The object must now be moved to a different queue, since either its size
or its accessCount has been changed, | |
| 494 // and both of those are used to determine which LRU queue the resource shou
ld be in. | |
| 495 if (oldSize) | |
| 496 removeFromLRUList(entry, lruListFor(entry->m_accessCount, oldSize)); | |
| 497 if (wasAccessed) | |
| 498 entry->m_accessCount++; | |
| 499 if (newSize) | |
| 500 insertInLRUList(entry, lruListFor(entry->m_accessCount, newSize)); | |
| 501 | |
| 502 ptrdiff_t delta = newSize - oldSize; | |
| 503 if (resource->hasClients()) { | |
| 504 ASSERT(delta >= 0 || m_liveSize >= static_cast<size_t>(-delta) ); | |
| 505 m_liveSize += delta; | |
| 506 } else { | |
| 507 ASSERT(delta >= 0 || m_deadSize >= static_cast<size_t>(-delta) ); | |
| 508 m_deadSize += delta; | |
| 509 } | |
| 510 } | |
| 511 | |
| 512 void MemoryCache::updateDecodedResource(Resource* resource, UpdateReason reason,
MemoryCacheLiveResourcePriority priority) | |
| 513 { | |
| 514 if (!contains(resource)) | |
| 515 return; | |
| 516 MemoryCacheEntry* entry = m_resources.get(resource->url()); | |
| 517 | |
| 518 removeFromLiveDecodedResourcesList(entry); | |
| 519 if (priority != MemoryCacheLiveResourcePriorityUnknown && priority != entry-
>m_liveResourcePriority) | |
| 520 entry->m_liveResourcePriority = priority; | |
| 521 if (resource->decodedSize() && resource->hasClients()) | |
| 522 insertInLiveDecodedResourcesList(entry); | |
| 523 | |
| 524 if (reason != UpdateForAccess) | |
| 525 return; | |
| 526 | |
| 527 double timestamp = resource->isImage() ? FrameView::currentFrameTimeStamp()
: 0.0; | |
| 528 if (!timestamp) | |
| 529 timestamp = currentTime(); | |
| 530 entry->m_lastDecodedAccessTime = timestamp; | |
| 531 } | |
| 532 | |
| 533 MemoryCacheLiveResourcePriority MemoryCache::priority(Resource* resource) const | |
| 534 { | |
| 535 if (!contains(resource)) | |
| 536 return MemoryCacheLiveResourcePriorityUnknown; | |
| 537 MemoryCacheEntry* entry = m_resources.get(resource->url()); | |
| 538 return entry->m_liveResourcePriority; | |
| 539 } | |
| 540 | |
| 541 void MemoryCache::removeURLFromCache(ExecutionContext* context, const KURL& url) | |
| 542 { | |
| 543 removeURLFromCacheInternal(context, url); | |
| 544 } | |
| 545 | |
| 546 void MemoryCache::removeURLFromCacheInternal(ExecutionContext*, const KURL& url) | |
| 547 { | |
| 548 if (Resource* resource = memoryCache()->resourceForURL(url)) | |
| 549 memoryCache()->remove(resource); | |
| 550 } | |
| 551 | |
| 552 void MemoryCache::TypeStatistic::addResource(Resource* o) | |
| 553 { | |
| 554 bool purged = o->wasPurged(); | |
| 555 bool purgeable = o->isPurgeable() && !purged; | |
| 556 size_t pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095; | |
| 557 count++; | |
| 558 size += purged ? 0 : o->size(); | |
| 559 liveSize += o->hasClients() ? o->size() : 0; | |
| 560 decodedSize += o->decodedSize(); | |
| 561 encodedSize += o->encodedSize(); | |
| 562 encodedSizeDuplicatedInDataURLs += o->url().protocolIsData() ? o->encodedSiz
e() : 0; | |
| 563 purgeableSize += purgeable ? pageSize : 0; | |
| 564 purgedSize += purged ? pageSize : 0; | |
| 565 } | |
| 566 | |
| 567 MemoryCache::Statistics MemoryCache::getStatistics() | |
| 568 { | |
| 569 Statistics stats; | |
| 570 ResourceMap::iterator e = m_resources.end(); | |
| 571 for (ResourceMap::iterator i = m_resources.begin(); i != e; ++i) { | |
| 572 Resource* resource = i->value->m_resource.get(); | |
| 573 switch (resource->type()) { | |
| 574 case Resource::Image: | |
| 575 stats.images.addResource(resource); | |
| 576 break; | |
| 577 case Resource::Font: | |
| 578 stats.fonts.addResource(resource); | |
| 579 break; | |
| 580 default: | |
| 581 stats.other.addResource(resource); | |
| 582 break; | |
| 583 } | |
| 584 } | |
| 585 return stats; | |
| 586 } | |
| 587 | |
| 588 void MemoryCache::evictResources() | |
| 589 { | |
| 590 for (;;) { | |
| 591 ResourceMap::iterator i = m_resources.begin(); | |
| 592 if (i == m_resources.end()) | |
| 593 break; | |
| 594 evict(i->value.get()); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 void MemoryCache::prune(Resource* justReleasedResource) | |
| 599 { | |
| 600 TRACE_EVENT0("renderer", "MemoryCache::prune()"); | |
| 601 | |
| 602 if (m_inPruneResources) | |
| 603 return; | |
| 604 if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize
<= m_maxDeadCapacity) // Fast path. | |
| 605 return; | |
| 606 | |
| 607 // To avoid burdening the current thread with repetitive pruning jobs, | |
| 608 // pruning is postponed until the end of the current task. If it has | |
| 609 // been more than m_maxPruneDeferralDelay since the last prune, | |
| 610 // then we prune immediately. | |
| 611 // If the current thread's run loop is not active, then pruning will happen | |
| 612 // immediately only if it has been over m_maxPruneDeferralDelay | |
| 613 // since the last prune. | |
| 614 double currentTime = WTF::currentTime(); | |
| 615 if (prunePending()) { | |
| 616 if (currentTime - m_pruneTimeStamp >= m_maxPruneDeferralDelay) | |
| 617 pruneNow(currentTime); | |
| 618 } else { | |
| 619 if (currentTime - m_pruneTimeStamp >= m_maxPruneDeferralDelay) { | |
| 620 pruneNow(currentTime); // Delay exceeded, prune now. | |
| 621 } else { | |
| 622 m_pendingPrune.Reset(base::Bind(&MemoryCache::pruneMicrotask, base::
Unretained(this))); | |
| 623 Microtask::enqueueMicrotask(m_pendingPrune.callback()); | |
| 624 } | |
| 625 } | |
| 626 | |
| 627 if (prunePending() && m_deadSize > m_maxDeferredPruneDeadCapacity && justRel
easedResource) { | |
| 628 // The following eviction does not respect LRU order, but it can be done | |
| 629 // immediately in constant time, as opposed to pruneDeadResources, which | |
| 630 // we would rather defer because it is O(N), which would make tear-down
of N | |
| 631 // objects O(N^2) if we pruned immediately. This immediate eviction is a | |
| 632 // safeguard against runaway memory consumption by dead resources | |
| 633 // while a prune is pending. | |
| 634 // Main Resources in the cache are only substitue data that was | |
| 635 // precached and should not be evicted. | |
| 636 if (contains(justReleasedResource) && justReleasedResource->type() != Re
source::MainResource) | |
| 637 evict(m_resources.get(justReleasedResource->url())); | |
| 638 | |
| 639 // As a last resort, prune immediately | |
| 640 if (m_deadSize > m_maxDeferredPruneDeadCapacity) | |
| 641 pruneNow(currentTime); | |
| 642 } | |
| 643 } | |
| 644 | |
| 645 void MemoryCache::pruneMicrotask() | |
| 646 { | |
| 647 pruneNow(WTF::currentTime()); | |
| 648 } | |
| 649 | |
| 650 void MemoryCache::pruneNow(double currentTime) | |
| 651 { | |
| 652 if (prunePending()) | |
| 653 m_pendingPrune.Cancel(); | |
| 654 | |
| 655 TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); | |
| 656 pruneDeadResources(); // Prune dead first, in case it was "borrowing" capaci
ty from live. | |
| 657 pruneLiveResources(); | |
| 658 m_pruneFrameTimeStamp = FrameView::currentFrameTimeStamp(); | |
| 659 m_pruneTimeStamp = currentTime; | |
| 660 } | |
| 661 | |
| 662 #if ENABLE(OILPAN) | |
| 663 void MemoryCache::registerLiveResource(Resource& resource) | |
| 664 { | |
| 665 ASSERT(!m_liveResources.contains(&resource)); | |
| 666 m_liveResources.add(&resource); | |
| 667 } | |
| 668 | |
| 669 void MemoryCache::unregisterLiveResource(Resource& resource) | |
| 670 { | |
| 671 ASSERT(m_liveResources.contains(&resource)); | |
| 672 m_liveResources.remove(&resource); | |
| 673 } | |
| 674 | |
| 675 #else | |
| 676 | |
| 677 void MemoryCache::registerLiveResource(Resource&) | |
| 678 { | |
| 679 } | |
| 680 | |
| 681 void MemoryCache::unregisterLiveResource(Resource&) | |
| 682 { | |
| 683 } | |
| 684 #endif | |
| 685 | |
| 686 #ifdef MEMORY_CACHE_STATS | |
| 687 | |
| 688 void MemoryCache::dumpStats(Timer<MemoryCache>*) | |
| 689 { | |
| 690 Statistics s = getStatistics(); | |
| 691 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "
LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); | |
| 692 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "----
---------", "-------------", "-------------", "-------------", "-------------",
"-------------"); | |
| 693 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.
images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.
images.purgedSize); | |
| 694 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.coun
t, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSiz
e, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); | |
| 695 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.coun
t, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeabl
eSize, s.scripts.purgedSize); | |
| 696 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fo
nts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.
purgedSize); | |
| 697 printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Other", s.other.count, s.ot
her.size, s.other.liveSize, s.other.decodedSize, s.other.purgeableSize, s.other.
purgedSize); | |
| 698 printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "--
-----------", "-------------", "-------------", "-------------", "-------------"
, "-------------"); | |
| 699 | |
| 700 printf("Duplication of encoded data from data URLs\n"); | |
| 701 printf("%-13s %13d of %13d\n", "Images", s.images.encodedSizeDuplicatedI
nDataURLs, s.images.encodedSize); | |
| 702 printf("%-13s %13d of %13d\n", "CSS", s.cssStyleSheets.encodedSizeDup
licatedInDataURLs, s.cssStyleSheets.encodedSize); | |
| 703 printf("%-13s %13d of %13d\n", "JavaScript", s.scripts.encodedSizeDuplicated
InDataURLs, s.scripts.encodedSize); | |
| 704 printf("%-13s %13d of %13d\n", "Fonts", s.fonts.encodedSizeDuplicatedIn
DataURLs, s.fonts.encodedSize); | |
| 705 printf("%-13s %13d of %13d\n", "Other", s.other.encodedSizeDuplicatedIn
DataURLs, s.other.encodedSize); | |
| 706 } | |
| 707 | |
| 708 void MemoryCache::dumpLRULists(bool includeLive) const | |
| 709 { | |
| 710 printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded
, Access count, Referenced, isPurgeable, wasPurged):\n"); | |
| 711 | |
| 712 int size = m_allResources.size(); | |
| 713 for (int i = size - 1; i >= 0; i--) { | |
| 714 printf("\n\nList %d: ", i); | |
| 715 Resource* current = m_allResources[i].m_tail; | |
| 716 while (current) { | |
| 717 Resource* prev = current->m_prevInAllResourcesList; | |
| 718 if (includeLive || !current->hasClients()) | |
| 719 printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSiz
e() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, cur
rent->accessCount(), current->hasClients(), current->isPurgeable(), current->was
Purged()); | |
| 720 | |
| 721 current = prev; | |
| 722 } | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 #endif // MEMORY_CACHE_STATS | |
| 727 | |
| 728 } // namespace blink | |
| OLD | NEW |