<!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>[182357] 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/182357">182357</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2015-04-05 08:18:47 -0700 (Sun, 05 Apr 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Network cache Bloom filter is too big
https://bugs.webkit.org/show_bug.cgi?id=143400

Reviewed by Chris Dumez.

It is currently 1MB.

This patch switches the cache from a counting filter (CountingBloomFilter) to a bit filter (BloomFilter).

It also reduces the filter size from 2^20 to 2^18 elements which is good for ~26000 cache entries while
still keeping false positive rate below 1%. The current cache capacity allows around 4000 entries
with typical web contents.

The new filter size is 32KB.

* NetworkProcess/cache/NetworkCacheStorage.cpp:
(WebKit::NetworkCache::Storage::Storage):
(WebKit::NetworkCache::Storage::synchronize):

    Turn initialization function into general purpose synchronization function.

(WebKit::NetworkCache::Storage::addToContentsFilter):

    Collect newly added hashes so we don't miss entries that were added during synchronization.

(WebKit::NetworkCache::Storage::mayContain):
(WebKit::NetworkCache::Storage::remove):
(WebKit::NetworkCache::Storage::retrieve):
(WebKit::NetworkCache::Storage::store):
(WebKit::NetworkCache::Storage::dispatchPendingWriteOperations):
(WebKit::NetworkCache::Storage::dispatchFullWriteOperation):
(WebKit::NetworkCache::Storage::dispatchHeaderWriteOperation):
(WebKit::NetworkCache::Storage::setMaximumSize):
(WebKit::NetworkCache::Storage::clear):
(WebKit::NetworkCache::Storage::shrinkIfNeeded):
(WebKit::NetworkCache::Storage::shrink):

    Non-counting Bloom filter does not support removals so this requires a new strategy.

    Shrink code now simply deletes entries. The filter is updated by calling synchronize() at the end.
    While we could synchronize the filter during traversal it is better to just have one function for that.

(WebKit::NetworkCache::Storage::initialize): Deleted.
* NetworkProcess/cache/NetworkCacheStorage.h:
(WebKit::NetworkCache::Storage::mayContain):
(WebKit::NetworkCache::Storage::cacheMayContain): Deleted.</pre>

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

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (182356 => 182357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-04-05 07:52:14 UTC (rev 182356)
+++ trunk/Source/WebKit2/ChangeLog        2015-04-05 15:18:47 UTC (rev 182357)
</span><span class="lines">@@ -1,3 +1,52 @@
</span><ins>+2015-04-04  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Network cache Bloom filter is too big
+        https://bugs.webkit.org/show_bug.cgi?id=143400
+
+        Reviewed by Chris Dumez.
+
+        It is currently 1MB.
+
+        This patch switches the cache from a counting filter (CountingBloomFilter) to a bit filter (BloomFilter).
+
+        It also reduces the filter size from 2^20 to 2^18 elements which is good for ~26000 cache entries while
+        still keeping false positive rate below 1%. The current cache capacity allows around 4000 entries
+        with typical web contents.
+
+        The new filter size is 32KB.
+
+        * NetworkProcess/cache/NetworkCacheStorage.cpp:
+        (WebKit::NetworkCache::Storage::Storage):
+        (WebKit::NetworkCache::Storage::synchronize):
+
+            Turn initialization function into general purpose synchronization function.
+
+        (WebKit::NetworkCache::Storage::addToContentsFilter):
+
+            Collect newly added hashes so we don't miss entries that were added during synchronization.
+
+        (WebKit::NetworkCache::Storage::mayContain):
+        (WebKit::NetworkCache::Storage::remove):
+        (WebKit::NetworkCache::Storage::retrieve):
+        (WebKit::NetworkCache::Storage::store):
+        (WebKit::NetworkCache::Storage::dispatchPendingWriteOperations):
+        (WebKit::NetworkCache::Storage::dispatchFullWriteOperation):
+        (WebKit::NetworkCache::Storage::dispatchHeaderWriteOperation):
+        (WebKit::NetworkCache::Storage::setMaximumSize):
+        (WebKit::NetworkCache::Storage::clear):
+        (WebKit::NetworkCache::Storage::shrinkIfNeeded):
+        (WebKit::NetworkCache::Storage::shrink):
+
+            Non-counting Bloom filter does not support removals so this requires a new strategy.
+
+            Shrink code now simply deletes entries. The filter is updated by calling synchronize() at the end.
+            While we could synchronize the filter during traversal it is better to just have one function for that.
+
+        (WebKit::NetworkCache::Storage::initialize): Deleted.
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+        (WebKit::NetworkCache::Storage::mayContain):
+        (WebKit::NetworkCache::Storage::cacheMayContain): Deleted.
+
</ins><span class="cx"> 2015-04-04  Andy Estes  &lt;aestes@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Content Filtering] Blocked page is not always displayed when it should be
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStoragecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp (182356 => 182357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp        2015-04-05 07:52:14 UTC (rev 182356)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp        2015-04-05 15:18:47 UTC (rev 182357)
</span><span class="lines">@@ -70,34 +70,75 @@
</span><span class="cx">     , m_serialBackgroundIOQueue(WorkQueue::create(&quot;com.apple.WebKit.Cache.Storage.serialBackground&quot;, WorkQueue::Type::Serial, WorkQueue::QOS::Background))
</span><span class="cx"> {
</span><span class="cx">     deleteOldVersions();
</span><del>-    initialize();
</del><ins>+    synchronize();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Storage::initialize()
</del><ins>+void Storage::synchronize()
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><ins>+    if (m_synchronizationInProgress || m_shrinkInProgress)
+        return;
+    m_synchronizationInProgress = true;
+
+    LOG(NetworkCacheStorage, &quot;(NetworkProcess) synchronizing cache&quot;);
+
</ins><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><del>-
</del><span class="cx">     backgroundIOQueue().dispatch([this, cachePathCapture] {
</span><span class="cx">         String cachePath = cachePathCapture.string();
</span><del>-        traverseCacheFiles(cachePath, [this](const String&amp; fileName, const String&amp; partitionPath) {
</del><ins>+
+        auto filter = std::make_unique&lt;ContentsFilter&gt;();
+        size_t size = 0;
+        unsigned count = 0;
+        traverseCacheFiles(cachePath, [&amp;filter, &amp;size, &amp;count](const String&amp; fileName, const String&amp; partitionPath) {
</ins><span class="cx">             Key::HashType hash;
</span><span class="cx">             if (!Key::stringToHash(fileName, hash))
</span><span class="cx">                 return;
</span><del>-            unsigned shortHash = Key::toShortHash(hash);
-            RunLoop::main().dispatch([this, shortHash] {
-                m_contentsFilter.add(shortHash);
-            });
</del><span class="cx">             auto filePath = WebCore::pathByAppendingComponent(partitionPath, fileName);
</span><span class="cx">             long long fileSize = 0;
</span><span class="cx">             WebCore::getFileSize(filePath, fileSize);
</span><del>-            m_approximateSize += fileSize;
</del><ins>+            if (!fileSize)
+                return;
+            filter-&gt;add(Key::toShortHash(hash));
+            size += fileSize;
+            ++count;
</ins><span class="cx">         });
</span><del>-        m_hasPopulatedContentsFilter = true;
</del><ins>+
+        auto* filterPtr = filter.release();
+        RunLoop::main().dispatch([this, filterPtr, size] {
+            auto filter = std::unique_ptr&lt;ContentsFilter&gt;(filterPtr);
+
+            for (auto hash : m_contentsFilterHashesAddedDuringSynchronization)
+                filter-&gt;add(hash);
+            m_contentsFilterHashesAddedDuringSynchronization.clear();
+
+            m_contentsFilter = WTF::move(filter);
+            m_approximateSize = size;
+            m_synchronizationInProgress = false;
+        });
+
+        LOG(NetworkCacheStorage, &quot;(NetworkProcess) cache synchronization completed approximateSize=%zu count=%d&quot;, size, count);
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void Storage::addToContentsFilter(const Key&amp; key)
+{
+    ASSERT(RunLoop::isMain());
+
+    if (m_contentsFilter)
+        m_contentsFilter-&gt;add(key.shortHash());
+
+    // If we get new entries during filter synchronization take care to add them to the new filter as well.
+    if (m_synchronizationInProgress)
+        m_contentsFilterHashesAddedDuringSynchronization.append(key.shortHash());
+}
+
+bool Storage::mayContain(const Key&amp; key) const
+{
+    ASSERT(RunLoop::isMain());
+    return !m_contentsFilter || m_contentsFilter-&gt;mayContain(key.shortHash());
+}
+
</ins><span class="cx"> static String directoryPathForKey(const Key&amp; key, const String&amp; cachePath)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!key.partition().isEmpty());
</span><span class="lines">@@ -288,12 +329,10 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><del>-    // For simplicity we don't reduce m_approximateSize on removals.
-    // The next cache shrink will update the size.
</del><ins>+    // We can't remove the key from the Bloom filter (but some false positives are expected anyway).
+    // For simplicity we also don't reduce m_approximateSize on removals.
+    // The next synchronization will update everything.
</ins><span class="cx"> 
</span><del>-    if (m_contentsFilter.mayContain(key.shortHash()))
-        m_contentsFilter.remove(key.shortHash());
-
</del><span class="cx">     StringCapture filePathCapture(filePathForKey(key, m_directoryPath));
</span><span class="cx">     serialBackgroundIOQueue().dispatch([this, filePathCapture] {
</span><span class="cx">         WebCore::deleteFile(filePathCapture.string());
</span><span class="lines">@@ -385,7 +424,7 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!cacheMayContain(key.shortHash())) {
</del><ins>+    if (!mayContain(key)) {
</ins><span class="cx">         completionHandler(nullptr);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -412,7 +451,7 @@
</span><span class="cx">     m_pendingWriteOperations.append(new WriteOperation { record, { }, WTF::move(completionHandler) });
</span><span class="cx"> 
</span><span class="cx">     // Add key to the filter already here as we do lookups from the pending operations too.
</span><del>-    m_contentsFilter.add(record.key.shortHash());
</del><ins>+    addToContentsFilter(record.key);
</ins><span class="cx"> 
</span><span class="cx">     dispatchPendingWriteOperations();
</span><span class="cx"> }
</span><span class="lines">@@ -479,7 +518,7 @@
</span><span class="cx">         auto&amp; write = *writeOperation;
</span><span class="cx">         m_activeWriteOperations.add(WTF::move(writeOperation));
</span><span class="cx"> 
</span><del>-        if (write.existingRecord &amp;&amp; cacheMayContain(write.record.key.shortHash())) {
</del><ins>+        if (write.existingRecord &amp;&amp; mayContain(write.record.key)) {
</ins><span class="cx">             dispatchHeaderWriteOperation(write);
</span><span class="cx">             continue;
</span><span class="cx">         }
</span><span class="lines">@@ -492,8 +531,8 @@
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx">     ASSERT(m_activeWriteOperations.contains(&amp;write));
</span><span class="cx"> 
</span><del>-    if (!m_contentsFilter.mayContain(write.record.key.shortHash()))
-        m_contentsFilter.add(write.record.key.shortHash());
</del><ins>+    // This was added already when starting the store but filter might have been wiped.
+    addToContentsFilter(write.record.key);
</ins><span class="cx"> 
</span><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><span class="cx">     backgroundIOQueue().dispatch([this, &amp;write, cachePathCapture] {
</span><span class="lines">@@ -505,14 +544,10 @@
</span><span class="cx">         size_t bodyOffset = encodedHeader.size();
</span><span class="cx"> 
</span><span class="cx">         channel-&gt;write(0, headerAndBodyData, [this, &amp;write, bodyOffset, fd](int error) {
</span><del>-            LOG(NetworkCacheStorage, &quot;(NetworkProcess) write complete error=%d&quot;, error);
-            if (error) {
-                if (m_contentsFilter.mayContain(write.record.key.shortHash()))
-                    m_contentsFilter.remove(write.record.key.shortHash());
-            }
</del><span class="cx">             size_t bodySize = write.record.body.size();
</span><span class="cx">             size_t totalSize = bodyOffset + bodySize;
</span><span class="cx"> 
</span><ins>+            // On error the entry still stays in the contents filter until next synchronization.
</ins><span class="cx">             m_approximateSize += totalSize;
</span><span class="cx"> 
</span><span class="cx">             bool shouldMapBody = !error &amp;&amp; bodySize &gt;= pageSize();
</span><span class="lines">@@ -523,6 +558,8 @@
</span><span class="cx">             ASSERT(m_activeWriteOperations.contains(&amp;write));
</span><span class="cx">             m_activeWriteOperations.remove(&amp;write);
</span><span class="cx">             dispatchPendingWriteOperations();
</span><ins>+
+            LOG(NetworkCacheStorage, &quot;(NetworkProcess) write complete error=%d&quot;, error);
</ins><span class="cx">         });
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="lines">@@ -534,7 +571,7 @@
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx">     ASSERT(write.existingRecord);
</span><span class="cx">     ASSERT(m_activeWriteOperations.contains(&amp;write));
</span><del>-    ASSERT(cacheMayContain(write.record.key.shortHash()));
</del><ins>+    ASSERT(mayContain(write.record.key));
</ins><span class="cx"> 
</span><span class="cx">     // Try to update the header of an existing entry.
</span><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><span class="lines">@@ -570,6 +607,16 @@
</span><span class="cx"> void Storage::setMaximumSize(size_t size)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><ins>+
+#if !ASSERT_DISABLED
+    const size_t assumedAverageRecordSize = 50 &lt;&lt; 20;
+    size_t maximumRecordCount = size / assumedAverageRecordSize;
+    // ~10 bits per element are required for &lt;1% false positive rate.
+    size_t effectiveBloomFilterCapacity = ContentsFilter::tableSize / 10;
+    // If this gets hit it might be time to increase the filter size.
+    ASSERT(maximumRecordCount &lt; effectiveBloomFilterCapacity);
+#endif
+
</ins><span class="cx">     m_maximumSize = size;
</span><span class="cx"> 
</span><span class="cx">     shrinkIfNeeded();
</span><span class="lines">@@ -580,7 +627,8 @@
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx">     LOG(NetworkCacheStorage, &quot;(NetworkProcess) clearing cache&quot;);
</span><span class="cx"> 
</span><del>-    m_contentsFilter.clear();
</del><ins>+    if (m_contentsFilter)
+        m_contentsFilter-&gt;clear();
</ins><span class="cx">     m_approximateSize = 0;
</span><span class="cx"> 
</span><span class="cx">     StringCapture directoryPathCapture(m_directoryPath);
</span><span class="lines">@@ -629,20 +677,24 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><del>-    if (m_approximateSize &lt;= m_maximumSize)
</del><ins>+    if (m_approximateSize &gt; m_maximumSize)
+        shrink();
+}
+
+void Storage::shrink()
+{
+    ASSERT(RunLoop::isMain());
+
+    if (m_shrinkInProgress || m_synchronizationInProgress)
</ins><span class="cx">         return;
</span><del>-    if (m_shrinkInProgress)
-        return;
</del><span class="cx">     m_shrinkInProgress = true;
</span><span class="cx"> 
</span><span class="cx">     LOG(NetworkCacheStorage, &quot;(NetworkProcess) shrinking cache approximateSize=%zu, m_maximumSize=%zu&quot;, static_cast&lt;size_t&gt;(m_approximateSize), m_maximumSize);
</span><span class="cx"> 
</span><del>-    m_approximateSize = 0;
-
</del><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><span class="cx">     backgroundIOQueue().dispatch([this, cachePathCapture] {
</span><span class="cx">         String cachePath = cachePathCapture.string();
</span><del>-        traverseCacheFiles(cachePath, [this](const String&amp; fileName, const String&amp; partitionPath) {
</del><ins>+        traverseCacheFiles(cachePath, [](const String&amp; fileName, const String&amp; partitionPath) {
</ins><span class="cx">             auto filePath = WebCore::pathByAppendingComponent(partitionPath, fileName);
</span><span class="cx"> 
</span><span class="cx">             auto times = fileTimes(filePath);
</span><span class="lines">@@ -651,22 +703,8 @@
</span><span class="cx"> 
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;Deletion probability=%f shouldDelete=%d&quot;, probability, shouldDelete);
</span><span class="cx"> 
</span><del>-            if (!shouldDelete) {
-                long long fileSize = 0;
-                WebCore::getFileSize(filePath, fileSize);
-                m_approximateSize += fileSize;
-                return;
-            }
-
-            WebCore::deleteFile(filePath);
-            Key::HashType hash;
-            if (!Key::stringToHash(fileName, hash))
-                return;
-            unsigned shortHash = Key::toShortHash(hash);
-            RunLoop::main().dispatch([this, shortHash] {
-                if (m_contentsFilter.mayContain(shortHash))
-                    m_contentsFilter.remove(shortHash);
-            });
</del><ins>+            if (shouldDelete)
+                WebCore::deleteFile(filePath);
</ins><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         // Let system figure out if they are really empty.
</span><span class="lines">@@ -675,9 +713,13 @@
</span><span class="cx">             WebCore::deleteEmptyDirectory(partitionPath);
</span><span class="cx">         });
</span><span class="cx"> 
</span><del>-        m_shrinkInProgress = false;
</del><ins>+        RunLoop::main().dispatch([this] {
+            m_shrinkInProgress = false;
+            // We could synchronize during the shrink traversal. However this is fast and it is better to have just one code path.
+            synchronize();
+        });
</ins><span class="cx"> 
</span><del>-        LOG(NetworkCacheStorage, &quot;(NetworkProcess) cache shrink completed approximateSize=%zu&quot;, static_cast&lt;size_t&gt;(m_approximateSize));
</del><ins>+        LOG(NetworkCacheStorage, &quot;(NetworkProcess) cache shrink completed&quot;);
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h (182356 => 182357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-04-05 07:52:14 UTC (rev 182356)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-04-05 15:18:47 UTC (rev 182357)
</span><span class="lines">@@ -85,9 +85,10 @@
</span><span class="cx"> private:
</span><span class="cx">     Storage(const String&amp; directoryPath);
</span><span class="cx"> 
</span><del>-    void initialize();
</del><ins>+    void synchronize();
</ins><span class="cx">     void deleteOldVersions();
</span><span class="cx">     void shrinkIfNeeded();
</span><ins>+    void shrink();
</ins><span class="cx"> 
</span><span class="cx">     struct ReadOperation {
</span><span class="cx">         Key key;
</span><span class="lines">@@ -111,19 +112,25 @@
</span><span class="cx">     WorkQueue&amp; backgroundIOQueue() { return m_backgroundIOQueue.get(); }
</span><span class="cx">     WorkQueue&amp; serialBackgroundIOQueue() { return m_serialBackgroundIOQueue.get(); }
</span><span class="cx"> 
</span><del>-    bool cacheMayContain(unsigned shortHash) { return !m_hasPopulatedContentsFilter || m_contentsFilter.mayContain(shortHash); }
</del><ins>+    bool mayContain(const Key&amp;) const;
</ins><span class="cx"> 
</span><ins>+    // 2^18 bit filter can support up to 26000 entries with false positive rate &lt; 1%.
+    using ContentsFilter = BloomFilter&lt;18&gt;;
+    void addToContentsFilter(const Key&amp;);
+
</ins><span class="cx">     const String m_baseDirectoryPath;
</span><span class="cx">     const String m_directoryPath;
</span><span class="cx"> 
</span><span class="cx">     size_t m_maximumSize { std::numeric_limits&lt;size_t&gt;::max() };
</span><ins>+    size_t m_approximateSize { 0 };
</ins><span class="cx"> 
</span><del>-    CountingBloomFilter&lt;20&gt; m_contentsFilter;
-    std::atomic&lt;bool&gt; m_hasPopulatedContentsFilter { false };
</del><ins>+    std::unique_ptr&lt;ContentsFilter&gt; m_contentsFilter;
</ins><span class="cx"> 
</span><del>-    std::atomic&lt;size_t&gt; m_approximateSize { 0 };
-    std::atomic&lt;bool&gt; m_shrinkInProgress { false };
</del><ins>+    bool m_synchronizationInProgress { false };
+    bool m_shrinkInProgress { false };
</ins><span class="cx"> 
</span><ins>+    Vector&lt;unsigned&gt; m_contentsFilterHashesAddedDuringSynchronization;
+
</ins><span class="cx">     static const int maximumRetrievePriority = 4;
</span><span class="cx">     Deque&lt;std::unique_ptr&lt;const ReadOperation&gt;&gt; m_pendingReadOperationsByPriority[maximumRetrievePriority + 1];
</span><span class="cx">     HashSet&lt;std::unique_ptr&lt;const ReadOperation&gt;&gt; m_activeReadOperations;
</span></span></pre>
</div>
</div>

</body>
</html>