Source/WebCore/ChangeLog

 12015-02-16 Chris Dumez <cdumez@apple.com>
 2
 3 Keep all memory cache resources in ListHashSets
 4 https://bugs.webkit.org/show_bug.cgi?id=141667
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Keep all memory cache resources in ListHashSets instead of manual linked
 9 lists. This simplifies the code a lot and is also more efficient for
 10 retrieving / removing particular CachedResources.
 11
 12 * loader/cache/CachedResource.cpp:
 13 (WebCore::CachedResource::CachedResource):
 14 * loader/cache/CachedResource.h:
 15 * loader/cache/MemoryCache.cpp:
 16 (WebCore::MemoryCache::pruneDeadResourcesToSize):
 17 (WebCore::MemoryCache::removeFromLRUList):
 18 (WebCore::MemoryCache::insertInLRUList):
 19 (WebCore::MemoryCache::dumpLRULists):
 20 (WebCore::MemoryCache::lruListFor): Deleted.
 21 * loader/cache/MemoryCache.h:
 22
1232015-02-13 Chris Dumez <cdumez@apple.com>
224
325 RenderListItem resets its marker's style on style change even if the diff is StyleDifferenceEqual

Source/WebCore/loader/cache/CachedResource.cpp

@@CachedResource::CachedResource(const ResourceRequest& request, Type type, Sessio
135135 , m_deleted(false)
136136 , m_lruIndex(0)
137137#endif
138  , m_nextInAllResourcesList(0)
139  , m_prevInAllResourcesList(0)
140138 , m_owningCachedResourceLoader(0)
141139 , m_resourceToRevalidate(0)
142140 , m_proxyResource(0)

Source/WebCore/loader/cache/CachedResource.h

@@private:
319319 unsigned m_lruIndex;
320320#endif
321321
322  CachedResource* m_nextInAllResourcesList;
323  CachedResource* m_prevInAllResourcesList;
324 
325322 CachedResourceLoader* m_owningCachedResourceLoader; // only non-null for resources that are not in the cache
326323
327324 // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date

Source/WebCore/loader/cache/MemoryCache.cpp

@@void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize)
347347 if (m_inPruneResources)
348348 return;
349349 TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true);
350 
351  int size = m_allResources.size();
352350
353351 if (targetSize && m_deadSize <= targetSize)
354352 return;
355353
356354 bool canShrinkLRULists = true;
357  for (int i = size - 1; i >= 0; i--) {
358  // Remove from the tail, since this is the least frequently accessed of the objects.
359  CachedResource* current = m_allResources[i].m_tail;
360 
 355 for (int i = m_allResources.size() - 1; i >= 0; i--) {
 356 LRUList& list = *m_allResources[i];
 357
361358 // First flush all the decoded data in this queue.
362  while (current) {
363  // Protect 'previous' so it can't get deleted during destroyDecodedData().
364  CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList;
365  ASSERT(!previous || previous->inCache());
366  if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) {
 359 // Remove from the head, since this is the least frequently accessed of the objects.
 360 auto it = list.begin();
 361 while (it != list.end()) {
 362 CachedResource& current = **it;
 363
 364 // Increment the iterator now as the call to destroyDecodedData() below may
 365 // invalidate the current iterator.
 366 ++it;
 367
 368 // Protect 'next' so it can't get deleted during destroyDecodedData().
 369 CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr;
 370 ASSERT(!next || next->inCache());
 371 if (!current.hasClients() && !current.isPreloaded() && current.isLoaded()) {
367372 // Destroy our decoded data. This will remove us from
368373 // m_liveDecodedResources, and possibly move us to a different
369374 // LRU list in m_allResources.
370  current->destroyDecodedData();
 375 current.destroyDecodedData();
371376
372377 if (targetSize && m_deadSize <= targetSize)
373378 return;
374379 }
375  // Decoded data may reference other resources. Stop iterating if 'previous' somehow got
 380 // Decoded data may reference other resources. Stop iterating if 'next' somehow got
376381 // kicked out of cache during destroyDecodedData().
377  if (previous && !previous->inCache())
 382 if (next && !next->inCache())
378383 break;
379  current = previous.get();
380384 }
381385
382  // Now evict objects from this queue.
383  current = m_allResources[i].m_tail;
384  while (current) {
385  CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList;
386  ASSERT(!previous || previous->inCache());
387  if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) {
388  remove(*current);
 386 // Now evict objects from this list.
 387 // Remove from the head, since this is the least frequently accessed of the objects.
 388 it = list.begin();
 389 while (it != list.end()) {
 390 CachedResource& current = **it;
 391
 392 // Increment the iterator now as the call to remove() below will
 393 // invalidate the current iterator.
 394 ++it;
 395
 396 CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr;
 397 ASSERT(!next || next->inCache());
 398 if (!current.hasClients() && !current.isPreloaded() && !current.isCacheValidator()) {
 399 remove(current);
389400 if (targetSize && m_deadSize <= targetSize)
390401 return;
391402 }
392  if (previous && !previous->inCache())
 403 if (next && !next->inCache())
393404 break;
394  current = previous.get();
395405 }
396406
397407 // Shrink the vector back down so we don't waste time inspecting
398408 // empty LRU lists on future prunes.
399  if (m_allResources[i].m_head)
 409 if (!m_allResources[i]->isEmpty())
400410 canShrinkLRULists = false;
401411 else if (canShrinkLRULists)
402  m_allResources.resize(i);
 412 m_allResources.shrink(i);
403413 }
404414}
405415

@@void MemoryCache::remove(CachedResource& resource)
445455 resource.deleteIfPossible();
446456}
447457
448 MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource& resource)
 458auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList&
449459{
450460 unsigned accessCount = std::max(resource.accessCount(), 1U);
451461 unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount);
452462#ifndef NDEBUG
453463 resource.m_lruIndex = queueIndex;
454464#endif
455  if (m_allResources.size() <= queueIndex)
456  m_allResources.grow(queueIndex + 1);
457  return &m_allResources[queueIndex];
 465
 466 m_allResources.reserveCapacity(queueIndex + 1);
 467 while (m_allResources.size() <= queueIndex)
 468 m_allResources.uncheckedAppend(std::make_unique<LRUList>());
 469 return *m_allResources[queueIndex];
458470}
459471
460472void MemoryCache::removeFromLRUList(CachedResource& resource)

@@void MemoryCache::removeFromLRUList(CachedResource& resource)
467479 unsigned oldListIndex = resource.m_lruIndex;
468480#endif
469481
470  LRUList* list = lruListFor(resource);
 482 LRUList& list = lruListFor(resource);
471483
472 #if !ASSERT_DISABLED
473484 // Verify that the list we got is the list we want.
474485 ASSERT(resource.m_lruIndex == oldListIndex);
475486
476  // Verify that we are in fact in this list.
477  bool found = false;
478  for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
479  if (current == &resource) {
480  found = true;
481  break;
482  }
483  }
484  ASSERT(found);
485 #endif
486 
487  CachedResource* next = resource.m_nextInAllResourcesList;
488  CachedResource* prev = resource.m_prevInAllResourcesList;
489 
490  if (!next && !prev && list->m_head != &resource)
491  return;
492 
493  resource.m_nextInAllResourcesList = nullptr;
494  resource.m_prevInAllResourcesList = nullptr;
495 
496  if (next)
497  next->m_prevInAllResourcesList = prev;
498  else if (list->m_tail == &resource)
499  list->m_tail = prev;
500 
501  if (prev)
502  prev->m_nextInAllResourcesList = next;
503  else if (list->m_head == &resource)
504  list->m_head = next;
 487 bool removed = list.remove(&resource);
 488 ASSERT_UNUSED(removed, removed);
505489}
506490
507491void MemoryCache::insertInLRUList(CachedResource& resource)
508492{
509  // Make sure we aren't in some list already.
510  ASSERT(!resource.m_nextInAllResourcesList && !resource.m_prevInAllResourcesList);
511493 ASSERT(resource.inCache());
512494 ASSERT(resource.accessCount() > 0);
513495
514  LRUList* list = lruListFor(resource);
515 
516  resource.m_nextInAllResourcesList = list->m_head;
517  if (list->m_head)
518  list->m_head->m_prevInAllResourcesList = &resource;
519  list->m_head = &resource;
520 
521  if (!resource.m_nextInAllResourcesList)
522  list->m_tail = &resource;
523 
524 #if !ASSERT_DISABLED
525  // Verify that we are in now in the list like we should be.
526  list = lruListFor(resource);
527  bool found = false;
528  for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) {
529  if (current == &resource) {
530  found = true;
531  break;
532  }
533  }
534  ASSERT(found);
535 #endif
536 
 496 auto addResult = lruListFor(resource).add(&resource);
 497 ASSERT_UNUSED(addResult, addResult.isNewEntry);
537498}
538499
539500void MemoryCache::resourceAccessed(CachedResource& resource)

@@void MemoryCache::dumpLRULists(bool includeLive) const
751712 int size = m_allResources.size();
752713 for (int i = size - 1; i >= 0; i--) {
753714 printf("\n\nList %d: ", i);
754  CachedResource* current = m_allResources[i].m_tail;
755  while (current) {
756  CachedResource* prev = current->m_prevInAllResourcesList;
757  if (includeLive || !current->hasClients())
758  printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients());
759 
760  current = prev;
 715 for (auto* resource : *m_allResources[i]) {
 716 if (includeLive || !resource->hasClients())
 717 printf("(%.1fK, %.1fK, %uA, %dR); ", resource->decodedSize() / 1024.0f, (resource->encodedSize() + resource->overheadSize()) / 1024.0f, resource->accessCount(), resource->hasClients());
761718 }
762719 }
763720}

Source/WebCore/loader/cache/MemoryCache.h

@@private:
166166#else
167167 typedef HashMap<URL, CachedResource*> CachedResourceMap;
168168#endif
169 
170  struct LRUList {
171  CachedResource* m_head {nullptr};
172  CachedResource* m_tail {nullptr};
173  };
 169 typedef ListHashSet<CachedResource*> LRUList;
174170
175171 WEBCORE_EXPORT void pruneDeadResourcesToSize(unsigned targetSize);
176172 WEBCORE_EXPORT void pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources = false);

@@private:
178174 MemoryCache();
179175 ~MemoryCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons.
180176
181  LRUList* lruListFor(CachedResource&);
 177 LRUList& lruListFor(CachedResource&);
182178#ifndef NDEBUG
183179 void dumpStats();
184180 void dumpLRULists(bool includeLive) const;

@@private:
206202 // Size-adjusted and popularity-aware LRU list collection for cache objects. This collection can hold
207203 // more resources than the cached resource map, since it can also hold "stale" multiple versions of objects that are
208204 // waiting to die when the clients referencing them go away.
209  Vector<LRUList, 32> m_allResources;
 205 Vector<std::unique_ptr<LRUList>, 32> m_allResources;
210206
211207 // List just for live resources with decoded data. Access to this list is based off of painting the resource.
212  ListHashSet<CachedResource*> m_liveDecodedResources;
 208 LRUList m_liveDecodedResources;
213209
214210 // A URL-based map of all resources that are in the cache (including the freshest version of objects that are currently being
215211 // referenced by a Web page).