diff options
Diffstat (limited to 'Source/WebCore/loader/cache/MemoryCache.cpp')
-rw-r--r-- | Source/WebCore/loader/cache/MemoryCache.cpp | 922 |
1 files changed, 356 insertions, 566 deletions
diff --git a/Source/WebCore/loader/cache/MemoryCache.cpp b/Source/WebCore/loader/cache/MemoryCache.cpp index 652422cf6..75ee84bb7 100644 --- a/Source/WebCore/loader/cache/MemoryCache.cpp +++ b/Source/WebCore/loader/cache/MemoryCache.cpp @@ -25,9 +25,9 @@ #include "BitmapImage.h" #include "CachedImage.h" +#include "CachedImageClient.h" #include "CachedResource.h" #include "CachedResourceHandle.h" -#include "CrossThreadTask.h" #include "Document.h" #include "FrameLoader.h" #include "FrameLoaderTypes.h" @@ -35,166 +35,156 @@ #include "Image.h" #include "Logging.h" #include "PublicSuffix.h" -#include "SecurityOrigin.h" -#include "SecurityOriginHash.h" +#include "SharedBuffer.h" #include "WorkerGlobalScope.h" #include "WorkerLoaderProxy.h" #include "WorkerThread.h" #include <stdio.h> #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> -#include <wtf/TemporaryChange.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/SetForScope.h> #include <wtf/text/CString.h> -#if ENABLE(DISK_IMAGE_CACHE) -#include "DiskImageCacheIOS.h" -#include "ResourceBuffer.h" -#endif - namespace WebCore { static const int cDefaultCacheCapacity = 8192 * 1024; static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again. -static const double cDefaultDecodedDataDeletionInterval = 0; +static const auto defaultDecodedDataDeletionInterval = std::chrono::seconds { 0 }; -MemoryCache* memoryCache() +MemoryCache& MemoryCache::singleton() { - static MemoryCache* staticCache = new MemoryCache; ASSERT(WTF::isMainThread()); - - return staticCache; + static NeverDestroyed<MemoryCache> memoryCache; + return memoryCache; } MemoryCache::MemoryCache() : m_disabled(false) - , m_pruneEnabled(true) , m_inPruneResources(false) , m_capacity(cDefaultCacheCapacity) , m_minDeadCapacity(0) , m_maxDeadCapacity(cDefaultCacheCapacity) - , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval) + , m_deadDecodedDataDeletionInterval(defaultDecodedDataDeletionInterval) , m_liveSize(0) , m_deadSize(0) + , m_pruneTimer(*this, &MemoryCache::prune) { + static_assert(sizeof(long long) > sizeof(unsigned), "Numerical overflow can happen when adjusting the size of the cached memory."); } -URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) +auto MemoryCache::sessionResourceMap(SessionID sessionID) const -> CachedResourceMap* +{ + ASSERT(sessionID.isValid()); + return m_sessionResources.get(sessionID); +} + +auto MemoryCache::ensureSessionResourceMap(SessionID sessionID) -> CachedResourceMap& +{ + ASSERT(sessionID.isValid()); + auto& map = m_sessionResources.add(sessionID, nullptr).iterator->value; + if (!map) + map = std::make_unique<CachedResourceMap>(); + return *map; +} + +bool MemoryCache::shouldRemoveFragmentIdentifier(const URL& originalURL) { if (!originalURL.hasFragmentIdentifier()) - return originalURL; + return false; // Strip away fragment identifier from HTTP URLs. - // Data URLs must be unmodified. For file and custom URLs clients may expect resources + // Data URLs must be unmodified. For file and custom URLs clients may expect resources // to be unique even when they differ by the fragment identifier only. - if (!originalURL.protocolIsInHTTPFamily()) + return originalURL.protocolIsInHTTPFamily(); +} + +URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) +{ + if (!shouldRemoveFragmentIdentifier(originalURL)) return originalURL; URL url = originalURL; url.removeFragmentIdentifier(); return url; } -bool MemoryCache::add(CachedResource* resource) +bool MemoryCache::add(CachedResource& resource) { if (disabled()) return false; ASSERT(WTF::isMainThread()); -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* originMap = m_resources.get(resource->url()); - if (!originMap) { - originMap = new CachedResourceItem; - m_resources.set(resource->url(), adoptPtr(originMap)); - } - originMap->set(resource->cachePartition(), resource); -#else - m_resources.set(resource->url(), resource); -#endif - resource->setInCache(true); + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + ensureSessionResourceMap(resource.sessionID()).set(key, &resource); + resource.setInCache(true); resourceAccessed(resource); - LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource->url().string().latin1().data(), resource); + LOG(ResourceLoading, "MemoryCache::add Added '%s', resource %p\n", resource.url().string().latin1().data(), &resource); return true; } -void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response) +void MemoryCache::revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse& response) { - CachedResource* resource = revalidatingResource->resourceToRevalidate(); - ASSERT(resource); - ASSERT(!resource->inCache()); - ASSERT(resource->isLoaded()); - ASSERT(revalidatingResource->inCache()); + ASSERT(revalidatingResource.resourceToRevalidate()); + CachedResource& resource = *revalidatingResource.resourceToRevalidate(); + ASSERT(!resource.inCache()); + ASSERT(resource.isLoaded()); - // Calling evict() can potentially delete revalidatingResource, which we use + // Calling remove() can potentially delete revalidatingResource, which we use // below. This mustn't be the case since revalidation means it is loaded // and so canDelete() is false. - ASSERT(!revalidatingResource->canDelete()); + ASSERT(!revalidatingResource.canDelete()); - evict(revalidatingResource); + remove(revalidatingResource); -#if ENABLE(CACHE_PARTITIONING) - ASSERT(!m_resources.get(resource->url()) || !m_resources.get(resource->url())->get(resource->cachePartition())); - CachedResourceItem* originMap = m_resources.get(resource->url()); - if (!originMap) { - originMap = new CachedResourceItem; - m_resources.set(resource->url(), adoptPtr(originMap)); - } - originMap->set(resource->cachePartition(), resource); -#else - ASSERT(!m_resources.get(resource->url())); - m_resources.set(resource->url(), resource); -#endif - resource->setInCache(true); - resource->updateResponseAfterRevalidation(response); + auto& resources = ensureSessionResourceMap(resource.sessionID()); + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + ASSERT(!resources.get(key)); + resources.set(key, &resource); + resource.setInCache(true); + resource.updateResponseAfterRevalidation(response); insertInLRUList(resource); - int delta = resource->size(); - if (resource->decodedSize() && resource->hasClients()) + long long delta = resource.size(); + if (resource.decodedSize() && resource.hasClients()) insertInLiveDecodedResourcesList(resource); if (delta) - adjustSize(resource->hasClients(), delta); - - revalidatingResource->switchClientsToRevalidatedResource(); - ASSERT(!revalidatingResource->m_deleted); + adjustSize(resource.hasClients(), delta); + + revalidatingResource.switchClientsToRevalidatedResource(); + ASSERT(!revalidatingResource.m_deleted); // this deletes the revalidating resource - revalidatingResource->clearResourceToRevalidate(); + revalidatingResource.clearResourceToRevalidate(); } -void MemoryCache::revalidationFailed(CachedResource* revalidatingResource) +void MemoryCache::revalidationFailed(CachedResource& revalidatingResource) { ASSERT(WTF::isMainThread()); - LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource); - ASSERT(revalidatingResource->resourceToRevalidate()); - revalidatingResource->clearResourceToRevalidate(); + LOG(ResourceLoading, "Revalidation failed for %p", &revalidatingResource); + ASSERT(revalidatingResource.resourceToRevalidate()); + revalidatingResource.clearResourceToRevalidate(); } -CachedResource* MemoryCache::resourceForURL(const URL& resourceURL) +CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request, SessionID sessionID) { - return resourceForRequest(ResourceRequest(resourceURL)); + // FIXME: Change all clients to make sure HTTP(s) URLs have no fragment identifiers before calling here. + // CachedResourceLoader is now doing this. Add an assertion once all other clients are doing it too. + auto* resources = sessionResourceMap(sessionID); + if (!resources) + return nullptr; + return resourceForRequestImpl(request, *resources); } -CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request) +CachedResource* MemoryCache::resourceForRequestImpl(const ResourceRequest& request, CachedResourceMap& resources) { ASSERT(WTF::isMainThread()); URL url = removeFragmentIdentifierIfNeeded(request.url()); -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* item = m_resources.get(url); - CachedResource* resource = 0; - if (item) - resource = item->get(request.cachePartition()); -#else - CachedResource* resource = m_resources.get(url); -#endif - bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable(); - if (resource && !resource->makePurgeable(false)) { - ASSERT(!resource->hasClients()); - evict(resource); - return 0; - } - // Add the size back since we had subtracted it when we marked the memory as purgeable. - if (wasPurgeable) - adjustSize(resource->hasClients(), resource->size()); - return resource; + + auto key = std::make_pair(url, request.cachePartition()); + return resources.get(key); } unsigned MemoryCache::deadCapacity() const @@ -212,51 +202,46 @@ unsigned MemoryCache::liveCapacity() const return m_capacity - deadCapacity(); } -#if USE(CG) -// FIXME: Remove the USE(CG) once we either make NativeImagePtr a smart pointer on all platforms or -// remove the usage of CFRetain() in MemoryCache::addImageToCache() so as to make the code platform-independent. -bool MemoryCache::addImageToCache(NativeImagePtr image, const URL& url, const String& cachePartition) +static CachedImageClient& dummyCachedImageClient() +{ + static NeverDestroyed<CachedImageClient> client; + return client; +} + +bool MemoryCache::addImageToCache(NativeImagePtr&& image, const URL& url, const String& domainForCachePartition) { ASSERT(image); - removeImageFromCache(url, cachePartition); // Remove cache entry if it already exists. + SessionID sessionID = SessionID::defaultSessionID(); + removeImageFromCache(url, domainForCachePartition); // Remove cache entry if it already exists. - RefPtr<BitmapImage> bitmapImage = BitmapImage::create(image, nullptr); + RefPtr<BitmapImage> bitmapImage = BitmapImage::create(WTFMove(image), nullptr); if (!bitmapImage) return false; - CachedImageManual* cachedImage = new CachedImageManual(url, bitmapImage.get()); - if (!cachedImage) - return false; + auto cachedImage = std::make_unique<CachedImage>(url, bitmapImage.get(), sessionID); - // Actual release of the CGImageRef is done in BitmapImage. - CFRetain(image); - cachedImage->addFakeClient(); + cachedImage->addClient(dummyCachedImageClient()); cachedImage->setDecodedSize(bitmapImage->decodedSize()); -#if ENABLE(CACHE_PARTITIONING) - cachedImage->resourceRequest().setCachePartition(cachePartition); -#endif - add(cachedImage); - return true; + cachedImage->resourceRequest().setDomainForCachePartition(domainForCachePartition); + + return add(*cachedImage.release()); } -void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartition) +void MemoryCache::removeImageFromCache(const URL& url, const String& domainForCachePartition) { -#if ENABLE(CACHE_PARTITIONING) - CachedResource* resource; - if (CachedResourceItem* item = m_resources.get(url)) - resource = item->get(ResourceRequest::partitionName(cachePartition)); - else - resource = nullptr; -#else - UNUSED_PARAM(cachePartition); - CachedResource* resource = m_resources.get(url); -#endif + auto* resources = sessionResourceMap(SessionID::defaultSessionID()); + if (!resources) + return; + + auto key = std::make_pair(url, ResourceRequest::partitionName(domainForCachePartition)); + + CachedResource* resource = resources->get(key); if (!resource) return; // A resource exists and is not a manually cached image, so just remove it. - if (!resource->isImage() || !static_cast<CachedImage*>(resource)->isManual()) { - evict(resource); + if (!is<CachedImage>(*resource) || !downcast<CachedImage>(*resource).isManuallyCached()) { + remove(*resource); return; } @@ -265,15 +250,11 @@ void MemoryCache::removeImageFromCache(const URL& url, const String& cachePartit // dead resources are pruned. That might be immediately since // removing the last client triggers a MemoryCache::prune, so the // resource may be deleted after this call. - static_cast<CachedImageManual*>(resource)->removeFakeClient(); + downcast<CachedImage>(*resource).removeClient(dummyCachedImageClient()); } -#endif void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources) { - if (!m_pruneEnabled) - return; - unsigned capacity = shouldDestroyDecodedDataForAllLiveResources ? 0 : liveCapacity(); if (capacity && m_liveSize <= capacity) return; @@ -283,41 +264,67 @@ void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveReso pruneLiveResourcesToSize(targetSize, shouldDestroyDecodedDataForAllLiveResources); } -void MemoryCache::pruneLiveResourcesToPercentage(float prunePercentage) +void MemoryCache::forEachResource(const std::function<void(CachedResource&)>& function) { - if (!m_pruneEnabled) - return; + for (auto& unprotectedLRUList : m_allResources) { + Vector<CachedResourceHandle<CachedResource>> lruList; + copyToVector(*unprotectedLRUList, lruList); + for (auto& resource : lruList) + function(*resource); + } +} - if (prunePercentage < 0.0f || prunePercentage > 0.95f) +void MemoryCache::forEachSessionResource(SessionID sessionID, const std::function<void (CachedResource&)>& function) +{ + auto it = m_sessionResources.find(sessionID); + if (it == m_sessionResources.end()) return; - unsigned currentSize = m_liveSize + m_deadSize; - unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage); + Vector<CachedResourceHandle<CachedResource>> resourcesForSession; + copyValuesToVector(*it->value, resourcesForSession); + + for (auto& resource : resourcesForSession) + function(*resource); +} - pruneLiveResourcesToSize(targetSize); +void MemoryCache::destroyDecodedDataForAllImages() +{ + MemoryCache::singleton().forEachResource([](CachedResource& resource) { + if (resource.isImage()) + resource.destroyDecodedData(); + }); } void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources) { if (m_inPruneResources) return; - TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); + SetForScope<bool> reentrancyProtector(m_inPruneResources, true); double currentTime = FrameView::currentPaintTimeStamp(); if (!currentTime) // In case prune is called directly, outside of a Frame paint. currentTime = monotonicallyIncreasingTime(); // Destroy any decoded data in live objects that we can. - // Start from the tail, since this is the least recently accessed of the objects. + // Start from the head, since this is the least recently accessed of the objects. // The list might not be sorted by the m_lastDecodedAccessTime. The impact // of this weaker invariant is minor as the below if statement to check the // elapsedTime will evaluate to false as the currentTime will be a lot // greater than the current->m_lastDecodedAccessTime. // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 - CachedResource* current = m_liveDecodedResources.m_tail; - while (current) { - CachedResource* prev = current->m_prevInLiveResourcesList; + auto it = m_liveDecodedResources.begin(); + while (it != m_liveDecodedResources.end()) { + auto* current = *it; + + // Increment the iterator now because the call to destroyDecodedData() below + // may cause a call to ListHashSet::remove() and invalidate the current + // iterator. Note that this is safe because unlike iteration of most + // WTF Hash data structures, iteration is guaranteed safe against mutation + // of the ListHashSet, except for removal of the item currently pointed to + // by a given iterator. + ++it; + ASSERT(current->hasClients()); if (current->isLoaded() && current->decodedSize()) { // Check to see if the remaining resources are too new to prune. @@ -325,23 +332,18 @@ void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestr if (!shouldDestroyDecodedDataForAllLiveResources && elapsedTime < cMinDelayBeforeLiveDecodedPrune) return; - // Destroy our decoded data. This will remove us from - // m_liveDecodedResources, and possibly move us to a different LRU - // list in m_allResources. + // Destroy our decoded data. This will remove us from m_liveDecodedResources, and possibly move us + // to a different LRU list in m_allResources. current->destroyDecodedData(); if (targetSize && m_liveSize <= targetSize) return; } - current = prev; } } void MemoryCache::pruneDeadResources() { - if (!m_pruneEnabled) - return; - unsigned capacity = deadCapacity(); if (capacity && m_deadSize <= capacity) return; @@ -350,135 +352,61 @@ void MemoryCache::pruneDeadResources() pruneDeadResourcesToSize(targetSize); } -void MemoryCache::pruneDeadResourcesToPercentage(float prunePercentage) -{ - if (!m_pruneEnabled) - return; - - if (prunePercentage < 0.0f || prunePercentage > 0.95f) - return; - - unsigned currentSize = m_liveSize + m_deadSize; - unsigned targetSize = static_cast<unsigned>(currentSize * prunePercentage); - - pruneDeadResourcesToSize(targetSize); -} - void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; - TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); - - int size = m_allResources.size(); + SetForScope<bool> reentrancyProtector(m_inPruneResources, true); - // See if we have any purged resources we can evict. - for (int i = 0; i < size; i++) { - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* prev = current->m_prevInAllResourcesList; - if (current->wasPurged()) { - ASSERT(!current->hasClients()); - ASSERT(!current->isPreloaded()); - evict(current); - } - current = prev; - } - } if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; - for (int i = size - 1; i >= 0; i--) { - // Remove from the tail, since this is the least frequently accessed of the objects. - CachedResource* current = m_allResources[i].m_tail; - + for (int i = m_allResources.size() - 1; i >= 0; i--) { + // Make a copy of the LRUList first (and ref the resources) as calling + // destroyDecodedData() can alter the LRUList. + Vector<CachedResourceHandle<CachedResource>> lruList; + copyToVector(*m_allResources[i], lruList); + // First flush all the decoded data in this queue. - while (current) { - // Protect 'previous' so it can't get deleted during destroyDecodedData(). - CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; - ASSERT(!previous || previous->inCache()); - if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) { + // Remove from the head, since this is the least frequently accessed of the objects. + for (auto& resource : lruList) { + if (!resource->inCache()) + continue; + + if (!resource->hasClients() && !resource->isPreloaded() && resource->isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a different // LRU list in m_allResources. - current->destroyDecodedData(); + resource->destroyDecodedData(); if (targetSize && m_deadSize <= targetSize) return; } - // Decoded data may reference other resources. Stop iterating if 'previous' somehow got - // kicked out of cache during destroyDecodedData(). - if (previous && !previous->inCache()) - break; - current = previous.get(); } - // Now evict objects from this queue. - current = m_allResources[i].m_tail; - while (current) { - CachedResourceHandle<CachedResource> previous = current->m_prevInAllResourcesList; - ASSERT(!previous || previous->inCache()); - if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { - if (!makeResourcePurgeable(current)) - evict(current); + // Now evict objects from this list. + // Remove from the head, since this is the least frequently accessed of the objects. + for (auto& resource : lruList) { + if (!resource->inCache()) + continue; + if (!resource->hasClients() && !resource->isPreloaded() && !resource->isCacheValidator()) { + remove(*resource); if (targetSize && m_deadSize <= targetSize) return; } - if (previous && !previous->inCache()) - break; - current = previous.get(); } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. - if (m_allResources[i].m_head) + if (!m_allResources[i]->isEmpty()) canShrinkLRULists = false; else if (canShrinkLRULists) - m_allResources.resize(i); + m_allResources.shrink(i); } } -#if ENABLE(DISK_IMAGE_CACHE) -void MemoryCache::flushCachedImagesToDisk() -{ - if (!diskImageCache().isEnabled()) - return; - -#ifndef NDEBUG - double start = WTF::currentTimeMS(); - unsigned resourceCount = 0; - unsigned cachedSize = 0; -#endif - - for (size_t i = m_allResources.size(); i; ) { - --i; - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* previous = current->m_prevInAllResourcesList; - - if (!current->isUsingDiskImageCache() && current->canUseDiskImageCache()) { - current->useDiskImageCache(); - current->destroyDecodedData(); -#ifndef NDEBUG - LOG(DiskImageCache, "Cache::diskCacheResources(): attempting to save (%d) bytes", current->resourceBuffer()->sharedBuffer()->size()); - ++resourceCount; - cachedSize += current->resourceBuffer()->sharedBuffer()->size(); -#endif - } - - current = previous; - } - } - -#ifndef NDEBUG - double end = WTF::currentTimeMS(); - LOG(DiskImageCache, "DiskImageCache: took (%f) ms to cache (%d) bytes for (%d) resources", end - start, cachedSize, resourceCount); -#endif -} -#endif // ENABLE(DISK_IMAGE_CACHE) - void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) { ASSERT(minDeadBytes <= maxDeadBytes); @@ -489,400 +417,269 @@ void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, un prune(); } -bool MemoryCache::makeResourcePurgeable(CachedResource* resource) -{ - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) - return false; - - if (!resource->inCache()) - return false; - - if (resource->isPurgeable()) - return true; - - if (!resource->isSafeToMakePurgeable()) - return false; - - if (!resource->makePurgeable(true)) - return false; - - adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); - - return true; -} - -void MemoryCache::evict(CachedResource* resource) +void MemoryCache::remove(CachedResource& resource) { ASSERT(WTF::isMainThread()); - LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().string().latin1().data()); + LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", &resource, resource.url().string().latin1().data()); // The resource may have already been removed by someone other than our caller, // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. - if (resource->inCache()) { - // Remove from the resource map. -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem* item = m_resources.get(resource->url()); - if (item) { - item->remove(resource->cachePartition()); - if (!item->size()) - m_resources.remove(resource->url()); - } -#else - m_resources.remove(resource->url()); -#endif - resource->setInCache(false); - - // Remove from the appropriate LRU list. - removeFromLRUList(resource); - removeFromLiveDecodedResourcesList(resource); - - // If the resource was purged, it means we had already decremented the size when we made the - // resource purgeable in makeResourcePurgeable(). So adjust the size if we are evicting a - // resource that was not marked as purgeable. - if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() || !resource->isPurgeable()) - adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); - } else -#if ENABLE(CACHE_PARTITIONING) - ASSERT(!m_resources.get(resource->url()) || m_resources.get(resource->url())->get(resource->cachePartition()) != resource); -#else - ASSERT(m_resources.get(resource->url()) != resource); -#endif + if (auto* resources = sessionResourceMap(resource.sessionID())) { + auto key = std::make_pair(resource.url(), resource.cachePartition()); + + if (resource.inCache()) { + // Remove resource from the resource map. + resources->remove(key); + resource.setInCache(false); + + // If the resource map is now empty, remove it from m_sessionResources. + if (resources->isEmpty()) + m_sessionResources.remove(resource.sessionID()); + + // Remove from the appropriate LRU list. + removeFromLRUList(resource); + removeFromLiveDecodedResourcesList(resource); + adjustSize(resource.hasClients(), -static_cast<long long>(resource.size())); + } else + ASSERT(resources->get(key) != &resource); + } - resource->deleteIfPossible(); + resource.deleteIfPossible(); } -MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource) +auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList& { - unsigned accessCount = std::max(resource->accessCount(), 1U); - unsigned queueIndex = WTF::fastLog2(resource->size() / accessCount); + unsigned accessCount = std::max(resource.accessCount(), 1U); + unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount); #ifndef NDEBUG - resource->m_lruIndex = queueIndex; + resource.m_lruIndex = queueIndex; #endif - if (m_allResources.size() <= queueIndex) - m_allResources.grow(queueIndex + 1); - return &m_allResources[queueIndex]; + + m_allResources.reserveCapacity(queueIndex + 1); + while (m_allResources.size() <= queueIndex) + m_allResources.uncheckedAppend(std::make_unique<LRUList>()); + return *m_allResources[queueIndex]; } -void MemoryCache::removeFromLRUList(CachedResource* resource) +void MemoryCache::removeFromLRUList(CachedResource& resource) { // If we've never been accessed, then we're brand new and not in any list. - if (resource->accessCount() == 0) + if (!resource.accessCount()) return; #if !ASSERT_DISABLED - unsigned oldListIndex = resource->m_lruIndex; + unsigned oldListIndex = resource.m_lruIndex; #endif - LRUList* list = lruListFor(resource); + LRUList& list = lruListFor(resource); -#if !ASSERT_DISABLED // Verify that the list we got is the list we want. - ASSERT(resource->m_lruIndex == oldListIndex); - - // Verify that we are in fact in this list. - bool found = false; - for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - - CachedResource* next = resource->m_nextInAllResourcesList; - CachedResource* prev = resource->m_prevInAllResourcesList; - - if (next == 0 && prev == 0 && list->m_head != resource) - return; - - resource->m_nextInAllResourcesList = 0; - resource->m_prevInAllResourcesList = 0; - - if (next) - next->m_prevInAllResourcesList = prev; - else if (list->m_tail == resource) - list->m_tail = prev; + ASSERT(resource.m_lruIndex == oldListIndex); - if (prev) - prev->m_nextInAllResourcesList = next; - else if (list->m_head == resource) - list->m_head = next; + bool removed = list.remove(&resource); + ASSERT_UNUSED(removed, removed); } -void MemoryCache::insertInLRUList(CachedResource* resource) +void MemoryCache::insertInLRUList(CachedResource& resource) { - // Make sure we aren't in some list already. - ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList); - ASSERT(resource->inCache()); - ASSERT(resource->accessCount() > 0); - - LRUList* list = lruListFor(resource); - - resource->m_nextInAllResourcesList = list->m_head; - if (list->m_head) - list->m_head->m_prevInAllResourcesList = resource; - list->m_head = resource; + ASSERT(resource.inCache()); + ASSERT(resource.accessCount() > 0); - if (!resource->m_nextInAllResourcesList) - list->m_tail = resource; - -#if !ASSERT_DISABLED - // Verify that we are in now in the list like we should be. - list = lruListFor(resource); - bool found = false; - for (CachedResource* current = list->m_head; current; current = current->m_nextInAllResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - + auto addResult = lruListFor(resource).add(&resource); + ASSERT_UNUSED(addResult, addResult.isNewEntry); } -void MemoryCache::resourceAccessed(CachedResource* resource) +void MemoryCache::resourceAccessed(CachedResource& resource) { - ASSERT(resource->inCache()); + ASSERT(resource.inCache()); // Need to make sure to remove before we increase the access count, since // the queue will possibly change. removeFromLRUList(resource); // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. - if (!resource->accessCount()) - adjustSize(resource->hasClients(), resource->size()); + if (!resource.accessCount()) + adjustSize(resource.hasClients(), resource.size()); // Add to our access count. - resource->increaseAccessCount(); + resource.increaseAccessCount(); // Now insert into the new queue. insertInLRUList(resource); } -void MemoryCache::removeResourcesWithOrigin(SecurityOrigin* origin) +void MemoryCache::removeResourcesWithOrigin(SecurityOrigin& origin) { - Vector<CachedResource*> resourcesWithOrigin; - - CachedResourceMap::iterator e = m_resources.end(); -#if ENABLE(CACHE_PARTITIONING) - String originPartition = ResourceRequest::partitionName(origin->host()); -#endif + String originPartition = ResourceRequest::partitionName(origin.host()); - for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) { -#if ENABLE(CACHE_PARTITIONING) - for (CachedResourceItem::iterator itemIterator = it->value->begin(); itemIterator != it->value->end(); ++itemIterator) { - CachedResource* resource = itemIterator->value; - String partition = itemIterator->key; - if (partition == originPartition) { - resourcesWithOrigin.append(resource); + Vector<CachedResource*> resourcesWithOrigin; + for (auto& resources : m_sessionResources.values()) { + for (auto& keyValue : *resources) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (partitionName == originPartition) { + resourcesWithOrigin.append(&resource); continue; } -#else - CachedResource* resource = it->value; -#endif - RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::createFromString(resource->url()); - if (!resourceOrigin) - continue; - if (resourceOrigin->equal(origin)) - resourcesWithOrigin.append(resource); -#if ENABLE(CACHE_PARTITIONING) + RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(resource.url()); + if (resourceOrigin->equal(&origin)) + resourcesWithOrigin.append(&resource); } -#endif } - for (size_t i = 0; i < resourcesWithOrigin.size(); ++i) - remove(resourcesWithOrigin[i]); + for (auto* resource : resourcesWithOrigin) + remove(*resource); +} + +void MemoryCache::removeResourcesWithOrigins(SessionID sessionID, const HashSet<RefPtr<SecurityOrigin>>& origins) +{ + auto* resourceMap = sessionResourceMap(sessionID); + if (!resourceMap) + return; + + HashSet<String> originPartitions; + + for (auto& origin : origins) + originPartitions.add(ResourceRequest::partitionName(origin->host())); + + Vector<CachedResource*> resourcesToRemove; + for (auto& keyValuePair : *resourceMap) { + auto& resource = *keyValuePair.value; + auto& partitionName = keyValuePair.key.second; + if (originPartitions.contains(partitionName)) { + resourcesToRemove.append(&resource); + continue; + } + if (origins.contains(SecurityOrigin::create(resource.url()).ptr())) + resourcesToRemove.append(&resource); + } + + for (auto& resource : resourcesToRemove) + remove(*resource); } void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins) { -#if ENABLE(CACHE_PARTITIONING) - DEFINE_STATIC_LOCAL(String, httpString, ("http")); -#endif - CachedResourceMap::iterator e = m_resources.end(); - for (CachedResourceMap::iterator it = m_resources.begin(); it != e; ++it) { -#if ENABLE(CACHE_PARTITIONING) - if (it->value->begin()->key == emptyString()) - origins.add(SecurityOrigin::createFromString(it->value->begin()->value->url())); - else - origins.add(SecurityOrigin::create(httpString, it->value->begin()->key, 0)); -#else - origins.add(SecurityOrigin::createFromString(it->value->url())); -#endif + for (auto& resources : m_sessionResources.values()) { + for (auto& keyValue : *resources) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (!partitionName.isEmpty()) + origins.add(SecurityOrigin::create(ASCIILiteral("http"), partitionName, 0)); + else + origins.add(SecurityOrigin::create(resource.url())); + } } } -void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource) +HashSet<RefPtr<SecurityOrigin>> MemoryCache::originsWithCache(SessionID sessionID) const { - // If we've never been accessed, then we're brand new and not in any list. - if (!resource->m_inLiveDecodedResourcesList) - return; - resource->m_inLiveDecodedResourcesList = false; + HashSet<RefPtr<SecurityOrigin>> origins; -#if !ASSERT_DISABLED - // Verify that we are in fact in this list. - bool found = false; - for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) { - if (current == resource) { - found = true; - break; + auto it = m_sessionResources.find(sessionID); + if (it != m_sessionResources.end()) { + for (auto& keyValue : *it->value) { + auto& resource = *keyValue.value; + auto& partitionName = keyValue.key.second; + if (!partitionName.isEmpty()) + origins.add(SecurityOrigin::create("http", partitionName, 0)); + else + origins.add(SecurityOrigin::create(resource.url())); } } - ASSERT(found); -#endif - CachedResource* next = resource->m_nextInLiveResourcesList; - CachedResource* prev = resource->m_prevInLiveResourcesList; - - if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource) - return; - - resource->m_nextInLiveResourcesList = 0; - resource->m_prevInLiveResourcesList = 0; - - if (next) - next->m_prevInLiveResourcesList = prev; - else if (m_liveDecodedResources.m_tail == resource) - m_liveDecodedResources.m_tail = prev; + return origins; +} - if (prev) - prev->m_nextInLiveResourcesList = next; - else if (m_liveDecodedResources.m_head == resource) - m_liveDecodedResources.m_head = next; +void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource& resource) +{ + m_liveDecodedResources.remove(&resource); } -void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource) +void MemoryCache::insertInLiveDecodedResourcesList(CachedResource& resource) { // Make sure we aren't in the list already. - ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList); - resource->m_inLiveDecodedResourcesList = true; - - resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head; - if (m_liveDecodedResources.m_head) - m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource; - m_liveDecodedResources.m_head = resource; - - if (!resource->m_nextInLiveResourcesList) - m_liveDecodedResources.m_tail = resource; - -#if !ASSERT_DISABLED - // Verify that we are in now in the list like we should be. - bool found = false; - for (CachedResource* current = m_liveDecodedResources.m_head; current; current = current->m_nextInLiveResourcesList) { - if (current == resource) { - found = true; - break; - } - } - ASSERT(found); -#endif - + ASSERT(!m_liveDecodedResources.contains(&resource)); + m_liveDecodedResources.add(&resource); } -void MemoryCache::addToLiveResourcesSize(CachedResource* resource) +void MemoryCache::addToLiveResourcesSize(CachedResource& resource) { - m_liveSize += resource->size(); - m_deadSize -= resource->size(); + m_liveSize += resource.size(); + m_deadSize -= resource.size(); } -void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource) +void MemoryCache::removeFromLiveResourcesSize(CachedResource& resource) { - m_liveSize -= resource->size(); - m_deadSize += resource->size(); + m_liveSize -= resource.size(); + m_deadSize += resource.size(); } -void MemoryCache::adjustSize(bool live, int delta) +void MemoryCache::adjustSize(bool live, long long delta) { if (live) { - ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0)); + ASSERT(delta >= 0 || (static_cast<long long>(m_liveSize) + delta >= 0)); m_liveSize += delta; } else { - ASSERT(delta >= 0 || ((int)m_deadSize + delta >= 0)); + ASSERT(delta >= 0 || (static_cast<long long>(m_deadSize) + delta >= 0)); m_deadSize += delta; } } -void MemoryCache::removeUrlFromCache(ScriptExecutionContext* context, const String& urlString) -{ - removeRequestFromCache(context, ResourceRequest(urlString)); -} - -void MemoryCache::removeRequestFromCache(ScriptExecutionContext* context, const ResourceRequest& request) +void MemoryCache::removeRequestFromSessionCaches(ScriptExecutionContext& context, const ResourceRequest& request) { - if (context->isWorkerGlobalScope()) { - WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context); - workerGlobalScope->thread()->workerLoaderProxy().postTaskToLoader(createCallbackTask(&crossThreadRemoveRequestFromCache, request)); + if (is<WorkerGlobalScope>(context)) { + downcast<WorkerGlobalScope>(context).thread().workerLoaderProxy().postTaskToLoader([request = request.isolatedCopy()] (ScriptExecutionContext& context) { + MemoryCache::removeRequestFromSessionCaches(context, request); + }); return; } - removeRequestFromCacheImpl(context, request); -} - -void MemoryCache::removeRequestFromCacheImpl(ScriptExecutionContext*, const ResourceRequest& request) -{ - if (CachedResource* resource = memoryCache()->resourceForRequest(request)) - memoryCache()->remove(resource); -} - -void MemoryCache::crossThreadRemoveRequestFromCache(ScriptExecutionContext* context, PassOwnPtr<WebCore::CrossThreadResourceRequestData> requestData) -{ - OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData)); - MemoryCache::removeRequestFromCacheImpl(context, *request); + auto& memoryCache = MemoryCache::singleton(); + for (auto& resources : memoryCache.m_sessionResources) { + if (CachedResource* resource = memoryCache.resourceForRequestImpl(request, *resources.value)) + memoryCache.remove(*resource); + } } -void MemoryCache::TypeStatistic::addResource(CachedResource* o) +void MemoryCache::TypeStatistic::addResource(CachedResource& resource) { - bool purged = o->wasPurged(); - bool purgeable = o->isPurgeable() && !purged; - int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095; count++; - size += purged ? 0 : o->size(); - liveSize += o->hasClients() ? o->size() : 0; - decodedSize += o->decodedSize(); - purgeableSize += purgeable ? pageSize : 0; - purgedSize += purged ? pageSize : 0; -#if ENABLE(DISK_IMAGE_CACHE) - // Only the data inside the resource was mapped, not the entire resource. - mappedSize += o->isUsingDiskImageCache() ? o->resourceBuffer()->sharedBuffer()->size() : 0; -#endif + size += resource.size(); + liveSize += resource.hasClients() ? resource.size() : 0; + decodedSize += resource.decodedSize(); } MemoryCache::Statistics MemoryCache::getStatistics() { Statistics stats; - CachedResourceMap::iterator e = m_resources.end(); - for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) { -#if ENABLE(CACHE_PARTITIONING) - for (CachedResourceItem::iterator itemIterator = i->value->begin(); itemIterator != i->value->end(); ++itemIterator) { - CachedResource* resource = itemIterator->value; -#else - CachedResource* resource = i->value; -#endif + + for (auto& resources : m_sessionResources.values()) { + for (auto* resource : resources->values()) { switch (resource->type()) { case CachedResource::ImageResource: - stats.images.addResource(resource); + stats.images.addResource(*resource); break; case CachedResource::CSSStyleSheet: - stats.cssStyleSheets.addResource(resource); + stats.cssStyleSheets.addResource(*resource); break; case CachedResource::Script: - stats.scripts.addResource(resource); + stats.scripts.addResource(*resource); break; #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: - stats.xslStyleSheets.addResource(resource); + stats.xslStyleSheets.addResource(*resource); break; #endif +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif case CachedResource::FontResource: - stats.fonts.addResource(resource); + stats.fonts.addResource(*resource); break; default: break; } -#if ENABLE(CACHE_PARTITIONING) } -#endif } return stats; } @@ -893,16 +690,10 @@ void MemoryCache::setDisabled(bool disabled) if (!m_disabled) return; - for (;;) { - CachedResourceMap::iterator outerIterator = m_resources.begin(); - if (outerIterator == m_resources.end()) - break; -#if ENABLE(CACHE_PARTITIONING) - CachedResourceItem::iterator innerIterator = outerIterator->value->begin(); - evict(innerIterator->value); -#else - evict(outerIterator->value); -#endif + while (!m_sessionResources.isEmpty()) { + auto& resources = *m_sessionResources.begin()->value; + ASSERT(!resources.isEmpty()); + remove(*resources.begin()->value); } } @@ -915,66 +706,65 @@ void MemoryCache::evictResources() setDisabled(false); } +void MemoryCache::evictResources(SessionID sessionID) +{ + if (disabled()) + return; + + forEachSessionResource(sessionID, [this] (CachedResource& resource) { remove(resource); }); + + ASSERT(!m_sessionResources.contains(sessionID)); +} + +bool MemoryCache::needsPruning() const +{ + return m_liveSize + m_deadSize > m_capacity || m_deadSize > m_maxDeadCapacity; +} + void MemoryCache::prune() { - if (m_liveSize + m_deadSize <= m_capacity && m_deadSize <= m_maxDeadCapacity) // Fast path. + if (!needsPruning()) return; pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. pruneLiveResources(); } -void MemoryCache::pruneToPercentage(float targetPercentLive) +void MemoryCache::pruneSoon() { - pruneDeadResourcesToPercentage(targetPercentLive); // Prune dead first, in case it was "borrowing" capacity from live. - pruneLiveResourcesToPercentage(targetPercentLive); + if (m_pruneTimer.isActive()) + return; + if (!needsPruning()) + return; + m_pruneTimer.startOneShot(0); } - #ifndef NDEBUG void MemoryCache::dumpStats() { Statistics s = getStatistics(); -#if ENABLE(DISK_IMAGE_CACHE) - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize", "Mapped", "\"Real\""); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); - printf("%-13s %13d %13d %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, s.images.mappedSize, s.images.size - s.images.mappedSize); -#else - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); - 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); -#endif - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); + printf("%-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize"); + printf("%-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------"); + printf("%-13s %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize); + printf("%-13s %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize); #if ENABLE(XSLT) - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize); + printf("%-13s %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize); #endif - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize); - printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize); - printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); + printf("%-13s %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize); + printf("%-13s %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize); + printf("%-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------"); } void MemoryCache::dumpLRULists(bool includeLive) const { -#if ENABLE(DISK_IMAGE_CACHE) - printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged, isMemoryMapped):\n"); -#else - printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n"); -#endif + printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n"); int size = m_allResources.size(); for (int i = size - 1; i >= 0; i--) { printf("\n\nList %d: ", i); - CachedResource* current = m_allResources[i].m_tail; - while (current) { - CachedResource* prev = current->m_prevInAllResourcesList; - if (includeLive || !current->hasClients()) -#if ENABLE(DISK_IMAGE_CACHE) - printf("(%.1fK, %.1fK, %uA, %dR, %d, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged(), current->isUsingDiskImageCache()); -#else - printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged()); -#endif - - current = prev; + for (auto* resource : *m_allResources[i]) { + if (includeLive || !resource->hasClients()) + printf("(%.1fK, %.1fK, %uA, %dR); ", resource->decodedSize() / 1024.0f, (resource->encodedSize() + resource->overheadSize()) / 1024.0f, resource->accessCount(), resource->hasClients()); } } } |