void DocumentLoader::dataReceived(Resource* resource, const char* data, size_t length) { DCHECK(data); DCHECK(length); DCHECK_EQ(resource, m_mainResource); DCHECK(!m_response.isNull()); DCHECK(!m_frame->page()->defersLoading()); if (m_inDataReceived) { // If this function is reentered, defer processing of the additional data to // the top-level invocation. Reentrant calls can occur because of web // platform (mis-)features that require running a nested message loop: // - alert(), confirm(), prompt() // - Detach of plugin elements. // - Synchronous XMLHTTPRequest m_dataBuffer->append(data, length); return; } AutoReset<bool> reentrancyProtector(&m_inDataReceived, true); processData(data, length); // Process data received in reentrant invocations. Note that the invocations // of processData() may queue more data in reentrant invocations, so iterate // until it's empty. const char* segment; size_t pos = 0; while (size_t length = m_dataBuffer->getSomeData(segment, pos)) { processData(segment, length); pos += length; } // All data has been consumed, so flush the buffer. m_dataBuffer->clear(); }
void MemoryCache::prune() { if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize <= m_maxDeadCapacity) // Fast path. return; if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. pruneLiveResources(); }
void MemoryCache::pruneNow(double currentTime) { if (m_prunePending) { m_prunePending = false; blink::Platform::current()->currentThread()->removeTaskObserver(this); } TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. pruneLiveResources(); m_pruneFrameTimeStamp = FrameView::currentFrameTimeStamp(); m_pruneTimeStamp = currentTime; }
void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources) { if (m_inPruneResources) return; TemporaryChange<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 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 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. double elapsedTime = currentTime - current->m_lastDecodedAccessTime; if (!shouldDestroyDecodedDataForAllLiveResources && elapsedTime < cMinDelayBeforeLiveDecodedPrune) return; if (current->decodedDataIsPurgeable()) continue; // 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; } } }
void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); double currentTime = FrameView::currentPaintTimeStamp(); if (!currentTime) // In case prune is called directly, outside of a Frame paint. currentTime = WTF::currentTime(); // Destroy any decoded data in live objects that we can. // Start from the tail, 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; ASSERT(current->hasClients()); if (current->isLoaded() && current->decodedSize()) { // Check to see if the remaining resources are too new to prune. double elapsedTime = currentTime - current->m_lastDecodedAccessTime; if (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. current->destroyDecodedData(); if (targetSize && m_liveSize <= targetSize) return; } current = prev; } }
void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); int size = m_allResources.size(); // 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; // 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()) { // 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_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); 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) canShrinkLRULists = false; else if (canShrinkLRULists) m_allResources.resize(i); } }
void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) { if (m_inPruneResources) return; TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); if (targetSize && m_deadSize <= targetSize) return; bool canShrinkLRULists = true; for (int i = m_allResources.size() - 1; i >= 0; i--) { LRUList& list = *m_allResources[i]; // First flush all the decoded data in this queue. // Remove from the head, since this is the least frequently accessed of the objects. auto it = list.begin(); while (it != list.end()) { CachedResource& current = **it; // Increment the iterator now as the call to destroyDecodedData() below may // invalidate the current iterator. ++it; // Protect 'next' so it can't get deleted during destroyDecodedData(). CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr; ASSERT(!next || next->inCache()); if (!current.hasClients() && !current.isPreloaded() && current.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(); if (targetSize && m_deadSize <= targetSize) return; } // Decoded data may reference other resources. Stop iterating if 'next' somehow got // kicked out of cache during destroyDecodedData(). if (next && !next->inCache()) break; } // Now evict objects from this list. // Remove from the head, since this is the least frequently accessed of the objects. it = list.begin(); while (it != list.end()) { CachedResource& current = **it; // Increment the iterator now as the call to remove() below will // invalidate the current iterator. ++it; CachedResourceHandle<CachedResource> next = it != list.end() ? *it : nullptr; ASSERT(!next || next->inCache()); if (!current.hasClients() && !current.isPreloaded() && !current.isCacheValidator()) { remove(current); if (targetSize && m_deadSize <= targetSize) return; } if (next && !next->inCache()) break; } // Shrink the vector back down so we don't waste time inspecting // empty LRU lists on future prunes. if (!m_allResources[i]->isEmpty()) canShrinkLRULists = false; else if (canShrinkLRULists) m_allResources.shrink(i); } }