<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180008] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180008">180008</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2015-02-12 13:31:21 -0800 (Thu, 12 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>WebKit persistent cache uses a lot of threads
https://bugs.webkit.org/show_bug.cgi?id=141520

Reviewed by Andreas Kling.

Parallel retrieves are limited but stores are not. We may end up with lots of backround io
dispatch queue threads if they don't complete fast enough.

This patch adds pending state for write operations similar to what retrieves already have
and limits to number of active operations.

* NetworkProcess/cache/NetworkCacheStorage.h:

    Combine StoreOperation and UpdateOperation and rename to WriteOperation.
    Rename RetrieveOperation to ReadOperation.
    The idea is to emphasize that these are disk operations.

* NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
(WebKit::NetworkCacheStorage::dispatchReadOperation):
(WebKit::NetworkCacheStorage::dispatchPendingReadOperations):
(WebKit::retrieveFromMemory):
(WebKit::NetworkCacheStorage::retrieve):
(WebKit::NetworkCacheStorage::store):
(WebKit::NetworkCacheStorage::update):
(WebKit::NetworkCacheStorage::dispatchPendingWriteOperations):

    Only allow 3 parallel writes.

(WebKit::NetworkCacheStorage::dispatchFullWriteOperation):
(WebKit::NetworkCacheStorage::dispatchHeaderWriteOperation):

    More informative names.

(WebKit::NetworkCacheStorage::dispatchRetrieveOperation): Deleted.
(WebKit::NetworkCacheStorage::dispatchPendingRetrieveOperations): Deleted.
(WebKit::retrieveActive): Deleted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageCocoamm">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (180007 => 180008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-02-12 21:18:41 UTC (rev 180007)
+++ trunk/Source/WebKit2/ChangeLog        2015-02-12 21:31:21 UTC (rev 180008)
</span><span class="lines">@@ -1,3 +1,42 @@
</span><ins>+2015-02-12  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        WebKit persistent cache uses a lot of threads
+        https://bugs.webkit.org/show_bug.cgi?id=141520
+
+        Reviewed by Andreas Kling.
+
+        Parallel retrieves are limited but stores are not. We may end up with lots of backround io
+        dispatch queue threads if they don't complete fast enough.
+
+        This patch adds pending state for write operations similar to what retrieves already have
+        and limits to number of active operations.
+
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+
+            Combine StoreOperation and UpdateOperation and rename to WriteOperation.
+            Rename RetrieveOperation to ReadOperation.
+            The idea is to emphasize that these are disk operations.
+
+        * NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
+        (WebKit::NetworkCacheStorage::dispatchReadOperation):
+        (WebKit::NetworkCacheStorage::dispatchPendingReadOperations):
+        (WebKit::retrieveFromMemory):
+        (WebKit::NetworkCacheStorage::retrieve):
+        (WebKit::NetworkCacheStorage::store):
+        (WebKit::NetworkCacheStorage::update):
+        (WebKit::NetworkCacheStorage::dispatchPendingWriteOperations):
+
+            Only allow 3 parallel writes.
+
+        (WebKit::NetworkCacheStorage::dispatchFullWriteOperation):
+        (WebKit::NetworkCacheStorage::dispatchHeaderWriteOperation):
+
+            More informative names.
+
+        (WebKit::NetworkCacheStorage::dispatchRetrieveOperation): Deleted.
+        (WebKit::NetworkCacheStorage::dispatchPendingRetrieveOperations): Deleted.
+        (WebKit::retrieveActive): Deleted.
+
</ins><span class="cx"> 2015-02-12  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] Add API to allow overriding the default color chooser implementation
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h (180007 => 180008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-02-12 21:18:41 UTC (rev 180007)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-02-12 21:31:21 UTC (rev 180008)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> #include &lt;wtf/BloomFilter.h&gt;
</span><span class="cx"> #include &lt;wtf/Deque.h&gt;
</span><span class="cx"> #include &lt;wtf/HashSet.h&gt;
</span><ins>+#include &lt;wtf/Optional.h&gt;
</ins><span class="cx"> #include &lt;wtf/text/WTFString.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -163,26 +164,23 @@
</span><span class="cx"> 
</span><span class="cx">     void removeEntry(const NetworkCacheKey&amp;);
</span><span class="cx"> 
</span><del>-    struct RetrieveOperation {
</del><ins>+    struct ReadOperation {
</ins><span class="cx">         NetworkCacheKey key;
</span><span class="cx">         RetrieveCompletionHandler completionHandler;
</span><span class="cx">     };
</span><del>-    void dispatchRetrieveOperation(std::unique_ptr&lt;const RetrieveOperation&gt;);
-    void dispatchPendingRetrieveOperations();
</del><ins>+    void dispatchReadOperation(const ReadOperation&amp;);
+    void dispatchPendingReadOperations();
</ins><span class="cx"> 
</span><del>-    struct StoreOperation {
</del><ins>+    struct WriteOperation {
</ins><span class="cx">         NetworkCacheKey key;
</span><span class="cx">         Entry entry;
</span><ins>+        Optional&lt;Entry&gt; existingEntry;
</ins><span class="cx">         StoreCompletionHandler completionHandler;
</span><span class="cx">     };
</span><ins>+    void dispatchFullWriteOperation(const WriteOperation&amp;);
+    void dispatchHeaderWriteOperation(const WriteOperation&amp;);
+    void dispatchPendingWriteOperations();
</ins><span class="cx"> 
</span><del>-    struct UpdateOperation {
-        NetworkCacheKey key;
-        Entry entry;
-        Entry existingEntry;
-        StoreCompletionHandler completionHandler;
-    };
-
</del><span class="cx">     const String m_baseDirectoryPath;
</span><span class="cx">     const String m_directoryPath;
</span><span class="cx"> 
</span><span class="lines">@@ -193,11 +191,11 @@
</span><span class="cx">     std::atomic&lt;bool&gt; m_shrinkInProgress { false };
</span><span class="cx"> 
</span><span class="cx">     static const int maximumRetrievePriority = 4;
</span><del>-    Deque&lt;std::unique_ptr&lt;const RetrieveOperation&gt;&gt; m_pendingRetrieveOperationsByPriority[maximumRetrievePriority + 1];
</del><ins>+    Deque&lt;std::unique_ptr&lt;const ReadOperation&gt;&gt; m_pendingReadOperationsByPriority[maximumRetrievePriority + 1];
+    HashSet&lt;std::unique_ptr&lt;const ReadOperation&gt;&gt; m_activeReadOperations;
</ins><span class="cx"> 
</span><del>-    HashSet&lt;std::unique_ptr&lt;const RetrieveOperation&gt;&gt; m_activeRetrieveOperations;
-    HashSet&lt;std::unique_ptr&lt;const StoreOperation&gt;&gt; m_activeStoreOperations;
-    HashSet&lt;std::unique_ptr&lt;const UpdateOperation&gt;&gt; m_activeUpdateOperations;
</del><ins>+    Deque&lt;std::unique_ptr&lt;const WriteOperation&gt;&gt; m_pendingWriteOperations;
+    HashSet&lt;std::unique_ptr&lt;const WriteOperation&gt;&gt; m_activeWriteOperations;
</ins><span class="cx"> 
</span><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">     mutable DispatchPtr&lt;dispatch_queue_t&gt; m_ioQueue;
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm (180007 => 180008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm        2015-02-12 21:18:41 UTC (rev 180007)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm        2015-02-12 21:31:21 UTC (rev 180008)
</span><span class="lines">@@ -338,66 +338,67 @@
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void NetworkCacheStorage::dispatchRetrieveOperation(std::unique_ptr&lt;const RetrieveOperation&gt; retrieveOperation)
</del><ins>+void NetworkCacheStorage::dispatchReadOperation(const ReadOperation&amp; read)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><ins>+    ASSERT(m_activeReadOperations.contains(&amp;read));
</ins><span class="cx"> 
</span><del>-    auto&amp; retrieve = *retrieveOperation;
-    m_activeRetrieveOperations.add(WTF::move(retrieveOperation));
-
</del><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><del>-    dispatch_async(m_ioQueue.get(), [this, &amp;retrieve, cachePathCapture] {
</del><ins>+    dispatch_async(m_ioQueue.get(), [this, &amp;read, cachePathCapture] {
</ins><span class="cx">         int fd;
</span><del>-        auto channel = openFileForKey(retrieve.key, FileOpenType::Read, cachePathCapture.string(), fd);
</del><ins>+        auto channel = openFileForKey(read.key, FileOpenType::Read, cachePathCapture.string(), fd);
</ins><span class="cx"> 
</span><span class="cx">         bool didCallCompletionHandler = false;
</span><del>-        dispatch_io_read(channel.get(), 0, std::numeric_limits&lt;size_t&gt;::max(), dispatch_get_main_queue(), [this, fd, &amp;retrieve, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {
</del><ins>+        dispatch_io_read(channel.get(), 0, std::numeric_limits&lt;size_t&gt;::max(), dispatch_get_main_queue(), [this, fd, &amp;read, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {
</ins><span class="cx">             if (done) {
</span><span class="cx">                 if (error)
</span><del>-                    removeEntry(retrieve.key);
</del><ins>+                    removeEntry(read.key);
</ins><span class="cx"> 
</span><span class="cx">                 if (!didCallCompletionHandler)
</span><del>-                    retrieve.completionHandler(nullptr);
</del><ins>+                    read.completionHandler(nullptr);
</ins><span class="cx"> 
</span><del>-                ASSERT(m_activeRetrieveOperations.contains(&amp;retrieve));
-                m_activeRetrieveOperations.remove(&amp;retrieve);
-                dispatchPendingRetrieveOperations();
</del><ins>+                ASSERT(m_activeReadOperations.contains(&amp;read));
+                m_activeReadOperations.remove(&amp;read);
+                dispatchPendingReadOperations();
</ins><span class="cx">                 return;
</span><span class="cx">             }
</span><span class="cx">             ASSERT(!didCallCompletionHandler); // We are requesting maximum sized chunk so we should never get called more than once with data.
</span><span class="cx"> 
</span><del>-            auto entry = decodeEntry(fileData, fd, retrieve.key);
-            bool success = retrieve.completionHandler(WTF::move(entry));
</del><ins>+            auto entry = decodeEntry(fileData, fd, read.key);
+            bool success = read.completionHandler(WTF::move(entry));
</ins><span class="cx">             didCallCompletionHandler = true;
</span><span class="cx">             if (!success)
</span><del>-                removeEntry(retrieve.key);
</del><ins>+                removeEntry(read.key);
</ins><span class="cx">         });
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void NetworkCacheStorage::dispatchPendingRetrieveOperations()
</del><ins>+void NetworkCacheStorage::dispatchPendingReadOperations()
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><del>-    const int maximumActiveRetrieveOperationCount = 5;
</del><ins>+    const int maximumActiveReadOperationCount = 5;
</ins><span class="cx"> 
</span><span class="cx">     for (int priority = maximumRetrievePriority; priority &gt;= 0; --priority) {
</span><del>-        if (m_activeRetrieveOperations.size() &gt; maximumActiveRetrieveOperationCount) {
</del><ins>+        if (m_activeReadOperations.size() &gt; maximumActiveReadOperationCount) {
</ins><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) limiting parallel retrieves&quot;);
</span><span class="cx">             return;
</span><span class="cx">         }
</span><del>-        auto&amp; pendingRetrieveQueue = m_pendingRetrieveOperationsByPriority[priority];
</del><ins>+        auto&amp; pendingRetrieveQueue = m_pendingReadOperationsByPriority[priority];
</ins><span class="cx">         if (pendingRetrieveQueue.isEmpty())
</span><span class="cx">             continue;
</span><del>-        dispatchRetrieveOperation(pendingRetrieveQueue.takeFirst());
</del><ins>+        auto readOperation = pendingRetrieveQueue.takeFirst();
+        auto&amp; read = *readOperation;
+        m_activeReadOperations.add(WTF::move(readOperation));
+        dispatchReadOperation(read);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template &lt;class T&gt; bool retrieveActive(const T&amp; operations, const NetworkCacheKey&amp; key, NetworkCacheStorage::RetrieveCompletionHandler&amp; completionHandler)
</del><ins>+template &lt;class T&gt; bool retrieveFromMemory(const T&amp; operations, const NetworkCacheKey&amp; key, NetworkCacheStorage::RetrieveCompletionHandler&amp; completionHandler)
</ins><span class="cx"> {
</span><span class="cx">     for (auto&amp; operation : operations) {
</span><span class="cx">         if (operation-&gt;key == key) {
</span><del>-            LOG(NetworkCacheStorage, &quot;(NetworkProcess) found store operation in progress&quot;);
</del><ins>+            LOG(NetworkCacheStorage, &quot;(NetworkProcess) found write operation in progress&quot;);
</ins><span class="cx">             auto entry = operation-&gt;entry;
</span><span class="cx">             dispatch_async(dispatch_get_main_queue(), [entry, completionHandler] {
</span><span class="cx">                 completionHandler(std::make_unique&lt;NetworkCacheStorage::Entry&gt;(entry));
</span><span class="lines">@@ -417,44 +418,87 @@
</span><span class="cx">         completionHandler(nullptr);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><del>-    // See if we have the resource in memory.
-    if (retrieveActive(m_activeStoreOperations, key, completionHandler))
</del><ins>+
+    if (retrieveFromMemory(m_pendingWriteOperations, key, completionHandler))
</ins><span class="cx">         return;
</span><del>-    if (retrieveActive(m_activeUpdateOperations, key, completionHandler))
</del><ins>+    if (retrieveFromMemory(m_activeWriteOperations, key, completionHandler))
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    // Fetch from disk.
-    m_pendingRetrieveOperationsByPriority[priority].append(std::make_unique&lt;RetrieveOperation&gt;(RetrieveOperation { key, WTF::move(completionHandler) }));
-    dispatchPendingRetrieveOperations();
</del><ins>+    m_pendingReadOperationsByPriority[priority].append(std::make_unique&lt;ReadOperation&gt;(ReadOperation { key, WTF::move(completionHandler) }));
+    dispatchPendingReadOperations();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void NetworkCacheStorage::store(const NetworkCacheKey&amp; key, const Entry&amp; entry, StoreCompletionHandler&amp;&amp; completionHandler)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><ins>+    auto writeOperation = std::make_unique&lt;WriteOperation&gt;(WriteOperation { key, entry, { }, WTF::move(completionHandler) });
+    m_pendingWriteOperations.append(WTF::move(writeOperation));
+
+    // Add key to the filter already here as we do lookups from the pending operations too.
</ins><span class="cx">     m_contentsFilter.add(key.shortHash());
</span><span class="cx"> 
</span><del>-    auto storeOperation = std::make_unique&lt;StoreOperation&gt;(StoreOperation { key, entry, WTF::move(completionHandler) });
-    auto&amp; store = *storeOperation;
-    m_activeStoreOperations.add(WTF::move(storeOperation));
</del><ins>+    dispatchPendingWriteOperations();
+}
</ins><span class="cx"> 
</span><ins>+void NetworkCacheStorage::update(const NetworkCacheKey&amp; key, const Entry&amp; updateEntry, const Entry&amp; existingEntry, StoreCompletionHandler&amp;&amp; completionHandler)
+{
+    ASSERT(RunLoop::isMain());
+
+    auto writeOperation = std::make_unique&lt;WriteOperation&gt;(WriteOperation { key, updateEntry, existingEntry, WTF::move(completionHandler) });
+    m_pendingWriteOperations.append(WTF::move(writeOperation));
+
+    dispatchPendingWriteOperations();
+}
+
+void NetworkCacheStorage::dispatchPendingWriteOperations()
+{
+    ASSERT(RunLoop::isMain());
+
+    const int maximumActiveWriteOperationCount { 3 };
+
+    while (!m_pendingWriteOperations.isEmpty()) {
+        if (m_activeWriteOperations.size() &gt;= maximumActiveWriteOperationCount) {
+            LOG(NetworkCacheStorage, &quot;(NetworkProcess) limiting parallel writes&quot;);
+            return;
+        }
+        auto writeOperation = m_pendingWriteOperations.takeFirst();
+        auto&amp; write = *writeOperation;
+        m_activeWriteOperations.add(WTF::move(writeOperation));
+
+        if (write.existingEntry &amp;&amp; m_contentsFilter.mayContain(write.key.shortHash())) {
+            dispatchHeaderWriteOperation(write);
+            continue;
+        }
+        dispatchFullWriteOperation(write);
+    }
+}
+
+void NetworkCacheStorage::dispatchFullWriteOperation(const WriteOperation&amp; write)
+{
+    ASSERT(RunLoop::isMain());
+    ASSERT(m_activeWriteOperations.contains(&amp;write));
+
+    if (!m_contentsFilter.mayContain(write.key.shortHash()))
+        m_contentsFilter.add(write.key.shortHash());
+
</ins><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><del>-    dispatch_async(m_backgroundIOQueue.get(), [this, &amp;store, cachePathCapture] {
-        auto encodedHeader = encodeEntryHeader(store.key, store.entry);
-        auto writeData = adoptDispatch(dispatch_data_create_concat(encodedHeader.get(), store.entry.body.dispatchData()));
</del><ins>+    dispatch_async(m_backgroundIOQueue.get(), [this, &amp;write, cachePathCapture] {
+        auto encodedHeader = encodeEntryHeader(write.key, write.entry);
+        auto writeData = adoptDispatch(dispatch_data_create_concat(encodedHeader.get(), write.entry.body.dispatchData()));
</ins><span class="cx"> 
</span><span class="cx">         size_t bodyOffset = dispatch_data_get_size(encodedHeader.get());
</span><span class="cx"> 
</span><span class="cx">         int fd;
</span><del>-        auto channel = openFileForKey(store.key, FileOpenType::Create, cachePathCapture.string(), fd);
-        dispatch_io_write(channel.get(), 0, writeData.get(), dispatch_get_main_queue(), [this, &amp;store, fd, bodyOffset](bool done, dispatch_data_t, int error) {
</del><ins>+        auto channel = openFileForKey(write.key, FileOpenType::Create, cachePathCapture.string(), fd);
+        dispatch_io_write(channel.get(), 0, writeData.get(), dispatch_get_main_queue(), [this, &amp;write, fd, bodyOffset](bool done, dispatch_data_t, int error) {
</ins><span class="cx">             ASSERT_UNUSED(done, done);
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) write complete error=%d&quot;, error);
</span><span class="cx">             if (error) {
</span><del>-                if (m_contentsFilter.mayContain(store.key.shortHash()))
-                    m_contentsFilter.remove(store.key.shortHash());
</del><ins>+                if (m_contentsFilter.mayContain(write.key.shortHash()))
+                    m_contentsFilter.remove(write.key.shortHash());
</ins><span class="cx">             }
</span><del>-            size_t bodySize = store.entry.body.size();
</del><ins>+            size_t bodySize = write.entry.body.size();
</ins><span class="cx">             size_t totalSize = bodyOffset + bodySize;
</span><span class="cx"> 
</span><span class="cx">             m_approximateSize += totalSize;
</span><span class="lines">@@ -463,60 +507,53 @@
</span><span class="cx">             auto bodyMap = shouldMapBody ? mapFile(fd, bodyOffset, bodySize) : nullptr;
</span><span class="cx"> 
</span><span class="cx">             Data bodyData(bodyMap, Data::Backing::Map);
</span><del>-            store.completionHandler(!error, bodyData);
</del><ins>+            write.completionHandler(!error, bodyData);
</ins><span class="cx"> 
</span><del>-            m_activeStoreOperations.remove(&amp;store);
</del><ins>+            ASSERT(m_activeWriteOperations.contains(&amp;write));
+            m_activeWriteOperations.remove(&amp;write);
+            dispatchPendingWriteOperations();
</ins><span class="cx">         });
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     shrinkIfNeeded();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void NetworkCacheStorage::update(const NetworkCacheKey&amp; key, const Entry&amp; updateEntry, const Entry&amp; existingEntry, StoreCompletionHandler&amp;&amp; completionHandler)
</del><ins>+void NetworkCacheStorage::dispatchHeaderWriteOperation(const WriteOperation&amp; write)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><ins>+    ASSERT(write.existingEntry);
+    ASSERT(m_activeWriteOperations.contains(&amp;write));
+    ASSERT(m_contentsFilter.mayContain(write.key.shortHash()));
</ins><span class="cx"> 
</span><del>-    if (!m_contentsFilter.mayContain(key.shortHash())) {
-        LOG(NetworkCacheStorage, &quot;(NetworkProcess) existing entry not found, storing full entry&quot;);
-        store(key, updateEntry, WTF::move(completionHandler));
-        return;
-    }
-
-    auto updateOperation = std::make_unique&lt;UpdateOperation&gt;(UpdateOperation { key, updateEntry, existingEntry, WTF::move(completionHandler) });
-    auto&amp; update = *updateOperation;
-    m_activeUpdateOperations.add(WTF::move(updateOperation));
-
</del><span class="cx">     // Try to update the header of an existing entry.
</span><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><del>-    dispatch_async(m_backgroundIOQueue.get(), [this, &amp;update, cachePathCapture] {
-        auto headerData = encodeEntryHeader(update.key, update.entry);
-        auto existingHeaderData = encodeEntryHeader(update.key, update.existingEntry);
</del><ins>+    dispatch_async(m_backgroundIOQueue.get(), [this, &amp;write, cachePathCapture] {
+        auto headerData = encodeEntryHeader(write.key, write.entry);
+        auto existingHeaderData = encodeEntryHeader(write.key, write.existingEntry.value());
</ins><span class="cx"> 
</span><span class="cx">         bool pageRoundedHeaderSizeChanged = dispatch_data_get_size(headerData.get()) != dispatch_data_get_size(existingHeaderData.get());
</span><span class="cx">         if (pageRoundedHeaderSizeChanged) {
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) page-rounded header size changed, storing full entry&quot;);
</span><del>-            dispatch_async(dispatch_get_main_queue(), [this, &amp;update] {
-                store(update.key, update.entry, WTF::move(update.completionHandler));
-
-                ASSERT(m_activeUpdateOperations.contains(&amp;update));
-                m_activeUpdateOperations.remove(&amp;update);
</del><ins>+            dispatch_async(dispatch_get_main_queue(), [this, &amp;write] {
+                dispatchFullWriteOperation(write);
</ins><span class="cx">             });
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         int fd;
</span><del>-        auto channel = openFileForKey(update.key, FileOpenType::Write, cachePathCapture.string(), fd);
-        dispatch_io_write(channel.get(), 0, headerData.get(), dispatch_get_main_queue(), [this, &amp;update](bool done, dispatch_data_t, int error) {
</del><ins>+        auto channel = openFileForKey(write.key, FileOpenType::Write, cachePathCapture.string(), fd);
+        dispatch_io_write(channel.get(), 0, headerData.get(), dispatch_get_main_queue(), [this, &amp;write](bool done, dispatch_data_t, int error) {
</ins><span class="cx">             ASSERT_UNUSED(done, done);
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) update complete error=%d&quot;, error);
</span><span class="cx"> 
</span><span class="cx">             if (error)
</span><del>-                removeEntry(update.key);
</del><ins>+                removeEntry(write.key);
</ins><span class="cx"> 
</span><del>-            update.completionHandler(!error, Data());
</del><ins>+            write.completionHandler(!error, Data());
</ins><span class="cx"> 
</span><del>-            ASSERT(m_activeUpdateOperations.contains(&amp;update));
-            m_activeUpdateOperations.remove(&amp;update);
</del><ins>+            ASSERT(m_activeWriteOperations.contains(&amp;write));
+            m_activeWriteOperations.remove(&amp;write);
+            dispatchPendingWriteOperations();
</ins><span class="cx">         });
</span><span class="cx">     });
</span><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>