// Verifies that CachedResources are evicted from the decode cache // according to their DecodeCachePriority. static void TestDecodeCacheOrder(const ResourcePtr<Resource>& cachedImageLowPriority, const ResourcePtr<Resource>& cachedImageHighPriority) { memoryCache()->setDelayBeforeLiveDecodedPrune(0); memoryCache()->setMaxPruneDeferralDelay(0); MockImageResourceClient clientLowPriority(cachedImageLowPriority); MockImageResourceClient clientHighPriority(cachedImageHighPriority); const char data[5] = "abcd"; cachedImageLowPriority->appendData(data, 1u); cachedImageHighPriority->appendData(data, 4u); const unsigned lowPrioritySize = cachedImageLowPriority->size(); const unsigned highPrioritySize = cachedImageHighPriority->size(); const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize(); const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize(); const unsigned totalSize = lowPrioritySize + highPrioritySize; // Verify that the sizes are different to ensure that we can test eviction order. ASSERT_GT(lowPrioritySize, 0u); ASSERT_NE(lowPrioritySize, highPrioritySize); ASSERT_GT(lowPriorityMockDecodeSize, 0u); ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), 0u); // Add the items. The item added first would normally be evicted first. memoryCache()->add(cachedImageHighPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize); memoryCache()->add(cachedImageLowPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize); // Insert all items in the decoded items list with the same priority memoryCache()->updateDecodedResource(cachedImageHighPriority.get(), UpdateForPropertyChange); memoryCache()->updateDecodedResource(cachedImageLowPriority.get(), UpdateForPropertyChange); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize); // Now we will assign their priority and make sure they are moved to the correct buckets. memoryCache()->updateDecodedResource(cachedImageLowPriority.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityLow); memoryCache()->updateDecodedResource(cachedImageHighPriority.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityHigh); // Should first prune the LowPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize); // Should prune the HighPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize); }
void MemoryCache::dumpLRULists(bool includeLive) const { printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n"); int size = m_allResources.size(); for (int i = size - 1; i >= 0; i--) { printf("\n\nList %d: ", i); MemoryCacheEntry* current = m_allResources[i].m_tail; while (current) { ResourcePtr<Resource> currentResource = current->m_resource; if (includeLive || !currentResource->hasClients()) printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", currentResource->decodedSize() / 1024.0f, (currentResource->encodedSize() + currentResource->overheadSize()) / 1024.0f, current->m_accessCount, currentResource->hasClients(), currentResource->isPurgeable(), currentResource->wasPurged()); current = current->m_previousInAllResourcesList; } } }
// Verifies that cached resources are evicted immediately after release when // the total dead resource size is more than double the dead resource capacity. static void TestClientRemoval(const ResourcePtr<Resource>& resource1, const ResourcePtr<Resource>& resource2) { const char data[6] = "abcde"; MockImageResourceClient client1(resource1); resource1->appendData(data, 4u); MockImageResourceClient client2(resource2); resource2->appendData(data, 4u); const unsigned minDeadCapacity = 0; const unsigned maxDeadCapacity = ((resource1->size() + resource2->size()) / 2) - 1; const unsigned totalCapacity = maxDeadCapacity; memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); memoryCache()->add(resource1.get()); memoryCache()->add(resource2.get()); // Call prune. There is nothing to prune, but this will initialize // the prune timestamp, allowing future prunes to be deferred. memoryCache()->prune(); ASSERT_GT(resource1->decodedSize(), 0u); ASSERT_GT(resource2->decodedSize(), 0u); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), resource1->size() + resource2->size()); // Removing the client from resource1 should result in all resources // remaining in cache since the prune is deferred. client1.removeAsClient(); ASSERT_GT(resource1->decodedSize(), 0u); ASSERT_GT(resource2->decodedSize(), 0u); ASSERT_EQ(memoryCache()->deadSize(), resource1->size()); ASSERT_EQ(memoryCache()->liveSize(), resource2->size()); ASSERT_TRUE(memoryCache()->contains(resource1.get())); ASSERT_TRUE(memoryCache()->contains(resource2.get())); // Removing the client from resource2 should result in immediate // eviction of resource2 because we are over the prune deferral limit. client2.removeAsClient(); ASSERT_GT(resource1->decodedSize(), 0u); ASSERT_GT(resource2->decodedSize(), 0u); ASSERT_EQ(memoryCache()->deadSize(), resource1->size()); ASSERT_EQ(memoryCache()->liveSize(), 0u); ASSERT_TRUE(memoryCache()->contains(resource1.get())); ASSERT_FALSE(memoryCache()->contains(resource2.get())); }
// Verifies that CachedResources are evicted from the decode cache // according to their DecodeCachePriority. TEST_F(MemoryCacheTest, DecodeCacheOrder) { memoryCache()->setDelayBeforeLiveDecodedPrune(0); ResourcePtr<MockImageResource> cachedImageLowPriority = new MockImageResource(ResourceRequest(""), Resource::Raw); ResourcePtr<MockImageResource> cachedImageHighPriority = new MockImageResource(ResourceRequest(""), Resource::Raw); MockImageResourceClient clientLowPriority; MockImageResourceClient clientHighPriority; cachedImageLowPriority->addClient(&clientLowPriority); cachedImageHighPriority->addClient(&clientHighPriority); const char data[5] = "abcd"; cachedImageLowPriority->appendData(data, 1); cachedImageHighPriority->appendData(data, 4); const unsigned lowPrioritySize = cachedImageLowPriority->size(); const unsigned highPrioritySize = cachedImageHighPriority->size(); const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize(); const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize(); const unsigned totalSize = lowPrioritySize + highPrioritySize; // Verify that the sizes are different to ensure that we can test eviction order. ASSERT_GT(lowPrioritySize, 0u); ASSERT_NE(lowPrioritySize, highPrioritySize); ASSERT_GT(lowPriorityMockDecodeSize, 0u); ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), 0u); // Add the items. The item added first would normally be evicted first. memoryCache()->add(cachedImageHighPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize); memoryCache()->add(cachedImageLowPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize); // Insert all items in the decoded items list with the same priority memoryCache()->insertInLiveDecodedResourcesList(cachedImageHighPriority.get()); memoryCache()->insertInLiveDecodedResourcesList(cachedImageLowPriority.get()); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize); // Now we will assign their priority and make sure they are moved to the correct buckets. cachedImageLowPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow); cachedImageHighPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh); // Should first prune the LowPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize); // Should prune the HighPriority item. memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); memoryCache()->prune(); ASSERT_EQ(memoryCache()->deadSize(), 0u); ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize); }