<!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>[179052] 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/179052">179052</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2015-01-23 18:27:31 -0800 (Fri, 23 Jan 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Implement cache size limit
https://bugs.webkit.org/show_bug.cgi?id=140844

Reviewed by Andreas Kling.

Prevent the cache from growing without bounds. The simple scheme implemented here
estimates the cache size from number of entries. When the estimated size exceeds
the maximum size we randomly clear 1/4 of the entries.

* NetworkProcess/cache/NetworkCacheStorage.h:
* NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
(WebKit::NetworkCacheStorage::NetworkCacheStorage):
(WebKit::NetworkCacheStorage::initialize):
(WebKit::NetworkCacheStorage::removeEntry):
(WebKit::NetworkCacheStorage::store):
(WebKit::NetworkCacheStorage::setMaximumSize):
(WebKit::NetworkCacheStorage::clear):
(WebKit::NetworkCacheStorage::shrinkIfNeeded):
(WebKit::NetworkCacheStorage::initializeKeyFilter): 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 (179051 => 179052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-01-24 02:20:22 UTC (rev 179051)
+++ trunk/Source/WebKit2/ChangeLog        2015-01-24 02:27:31 UTC (rev 179052)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2015-01-23  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Implement cache size limit
+        https://bugs.webkit.org/show_bug.cgi?id=140844
+
+        Reviewed by Andreas Kling.
+
+        Prevent the cache from growing without bounds. The simple scheme implemented here
+        estimates the cache size from number of entries. When the estimated size exceeds
+        the maximum size we randomly clear 1/4 of the entries.
+
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+        * NetworkProcess/cache/NetworkCacheStorageCocoa.mm:
+        (WebKit::NetworkCacheStorage::NetworkCacheStorage):
+        (WebKit::NetworkCacheStorage::initialize):
+        (WebKit::NetworkCacheStorage::removeEntry):
+        (WebKit::NetworkCacheStorage::store):
+        (WebKit::NetworkCacheStorage::setMaximumSize):
+        (WebKit::NetworkCacheStorage::clear):
+        (WebKit::NetworkCacheStorage::shrinkIfNeeded):
+        (WebKit::NetworkCacheStorage::initializeKeyFilter): Deleted.
+
</ins><span class="cx"> 2015-01-23  Timothy Horton  &lt;timothy_horton@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix the pre-Yosemite build.
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h (179051 => 179052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-01-24 02:20:22 UTC (rev 179051)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-01-24 02:27:31 UTC (rev 179052)
</span><span class="lines">@@ -96,7 +96,8 @@
</span><span class="cx"> private:
</span><span class="cx">     NetworkCacheStorage(const String&amp; directoryPath);
</span><span class="cx"> 
</span><del>-    void initializeKeyFilter();
</del><ins>+    void initialize();
+    void shrinkIfNeeded();
</ins><span class="cx"> 
</span><span class="cx">     void removeEntry(const NetworkCacheKey&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -109,12 +110,14 @@
</span><span class="cx"> 
</span><span class="cx">     const String m_directoryPath;
</span><span class="cx"> 
</span><del>-    size_t m_maximumSize;
</del><ins>+    size_t m_maximumSize { std::numeric_limits&lt;size_t&gt;::max() };
</ins><span class="cx"> 
</span><span class="cx">     BloomFilter&lt;20&gt; m_keyFilter;
</span><ins>+    std::atomic&lt;size_t&gt; m_approximateEntryCount { 0 };
+    std::atomic&lt;bool&gt; m_shrinkInProgress { false };
</ins><span class="cx"> 
</span><span class="cx">     Vector&lt;Deque&lt;RetrieveOperation&gt;&gt; m_pendingRetrieveOperationsByPriority;
</span><del>-    unsigned m_activeRetrieveOperationCount;
</del><ins>+    unsigned m_activeRetrieveOperationCount { 0 };
</ins><span class="cx"> 
</span><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx">     mutable OSObjectPtr&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 (179051 => 179052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm        2015-01-24 02:20:22 UTC (rev 179051)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorageCocoa.mm        2015-01-24 02:27:31 UTC (rev 179052)
</span><span class="lines">@@ -122,30 +122,30 @@
</span><span class="cx"> 
</span><span class="cx"> NetworkCacheStorage::NetworkCacheStorage(const String&amp; directoryPath)
</span><span class="cx">     : m_directoryPath(directoryPath)
</span><del>-    , m_maximumSize(std::numeric_limits&lt;size_t&gt;::max())
-    , m_activeRetrieveOperationCount(0)
</del><span class="cx">     , m_ioQueue(adoptOSObject(dispatch_queue_create(&quot;com.apple.WebKit.Cache.Storage&quot;, DISPATCH_QUEUE_CONCURRENT)))
</span><span class="cx">     , m_backgroundIOQueue(adoptOSObject(dispatch_queue_create(&quot;com.apple.WebKit.Cache.Storage.Background&quot;, DISPATCH_QUEUE_CONCURRENT)))
</span><span class="cx"> {
</span><span class="cx">     dispatch_set_target_queue(m_backgroundIOQueue.get(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
</span><span class="cx"> 
</span><del>-    initializeKeyFilter();
</del><ins>+    initialize();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void NetworkCacheStorage::initializeKeyFilter()
</del><ins>+void NetworkCacheStorage::initialize()
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx"> 
</span><span class="cx">     StringCapture cachePathCapture(m_directoryPath);
</span><span class="cx">     auto&amp; keyFilter = m_keyFilter;
</span><ins>+    auto&amp; entryCount = m_approximateEntryCount;
</ins><span class="cx"> 
</span><del>-    dispatch_async(m_backgroundIOQueue.get(), [cachePathCapture, &amp;keyFilter] {
</del><ins>+    dispatch_async(m_backgroundIOQueue.get(), [cachePathCapture, &amp;keyFilter, &amp;entryCount] {
</ins><span class="cx">         String cachePath = cachePathCapture.string();
</span><del>-        traverseCacheFiles(cachePath, [&amp;keyFilter](const String&amp; fileName, const String&amp;) {
</del><ins>+        traverseCacheFiles(cachePath, [&amp;keyFilter, &amp;entryCount](const String&amp; fileName, const String&amp;) {
</ins><span class="cx">             NetworkCacheKey::HashType hash;
</span><span class="cx">             if (!NetworkCacheKey::stringToHash(fileName, hash))
</span><span class="cx">                 return;
</span><span class="cx">             keyFilter.add(hash);
</span><ins>+            ++entryCount;
</ins><span class="cx">         });
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="lines">@@ -343,9 +343,11 @@
</span><span class="cx">         m_keyFilter.remove(key.hash());
</span><span class="cx"> 
</span><span class="cx">     StringCapture filePathCapture(filePathForKey(key, m_directoryPath));
</span><del>-    dispatch_async(m_ioQueue.get(), [filePathCapture] {
</del><ins>+    dispatch_async(m_ioQueue.get(), [this, filePathCapture] {
</ins><span class="cx">         CString path = WebCore::fileSystemRepresentation(filePathCapture.string());
</span><span class="cx">         unlink(path.data());
</span><ins>+        if (m_approximateEntryCount)
+            --m_approximateEntryCount;
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -427,8 +429,11 @@
</span><span class="cx">         dispatch_io_write(channel.get(), 0, data.get(), dispatch_get_main_queue(), [this, key, completionHandler](bool done, dispatch_data_t, int error) {
</span><span class="cx">             ASSERT_UNUSED(done, done);
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) write complete error=%d&quot;, error);
</span><del>-            if (!error)
</del><ins>+            if (!error) {
</ins><span class="cx">                 m_keyFilter.add(key.hash());
</span><ins>+                ++m_approximateEntryCount;
+                shrinkIfNeeded();
+            }
</ins><span class="cx">             completionHandler(!error);
</span><span class="cx">         });
</span><span class="cx">     });
</span><span class="lines">@@ -437,8 +442,9 @@
</span><span class="cx"> void NetworkCacheStorage::setMaximumSize(size_t size)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><del>-    // FIXME: Implement.
</del><span class="cx">     m_maximumSize = size;
</span><ins>+
+    shrinkIfNeeded();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void NetworkCacheStorage::clear()
</span><span class="lines">@@ -447,6 +453,7 @@
</span><span class="cx">     LOG(NetworkCacheStorage, &quot;(NetworkProcess) clearing cache&quot;);
</span><span class="cx"> 
</span><span class="cx">     m_keyFilter.clear();
</span><ins>+    m_approximateEntryCount = 0;
</ins><span class="cx"> 
</span><span class="cx">     StringCapture directoryPathCapture(m_directoryPath);
</span><span class="cx"> 
</span><span class="lines">@@ -463,6 +470,51 @@
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void NetworkCacheStorage::shrinkIfNeeded()
+{
+    const size_t assumedAverageResourceSize { 48 * 1024 };
+    const size_t everyNthResourceToDelete { 4 };
+
+    size_t estimatedCacheSize = assumedAverageResourceSize * m_approximateEntryCount;
+
+    if (estimatedCacheSize &lt;= m_maximumSize)
+        return;
+    if (m_shrinkInProgress)
+        return;
+    m_shrinkInProgress = true;
+
+    LOG(NetworkCacheStorage, &quot;(NetworkProcess) shrinking cache m_approximateEntryCount=%d estimatedCacheSize=%d, m_maximumSize=%d&quot;, static_cast&lt;size_t&gt;(m_approximateEntryCount), estimatedCacheSize, m_maximumSize);
+
+    StringCapture cachePathCapture(m_directoryPath);
+    dispatch_async(m_backgroundIOQueue.get(), [this, cachePathCapture] {
+        String cachePath = cachePathCapture.string();
+        size_t foundEntryCount = 0;
+        size_t deletedCount = 0;
+        traverseCacheFiles(cachePath, [this, &amp;foundEntryCount, &amp;deletedCount](const String&amp; fileName, const String&amp; directory) {
+            String partitionPath = WebCore::pathByAppendingComponent(directory, fileName);
+            CString path = WebCore::fileSystemRepresentation(partitionPath);
+            ++foundEntryCount;
+            if (foundEntryCount % everyNthResourceToDelete)
+                return;
+            ++deletedCount;
+
+            unlink(path.data());
+
+            NetworkCacheKey::HashType hash;
+            if (!NetworkCacheKey::stringToHash(fileName, hash))
+                return;
+            dispatch_async(dispatch_get_main_queue(), [this, hash] {
+                if (m_keyFilter.mayContain(hash))
+                    m_keyFilter.remove(hash);
+            });
+        });
+        m_approximateEntryCount = foundEntryCount - deletedCount;
+        m_shrinkInProgress = false;
+
+        LOG(NetworkCacheStorage, &quot;(NetworkProcess) cache shrink completed m_approximateEntryCount=%d&quot;, static_cast&lt;size_t&gt;(m_approximateEntryCount));
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+}
+
</ins><span class="cx"> #endif
</span></span></pre>
</div>
</div>

</body>
</html>