Diff
Modified: trunk/Source/WebKit2/ChangeLog (181699 => 181700)
--- trunk/Source/WebKit2/ChangeLog 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/ChangeLog 2015-03-18 17:40:34 UTC (rev 181700)
@@ -1,3 +1,56 @@
+2015-03-17 Antti Koivisto <[email protected]>
+
+ Prune least valuable cache entries first
+ https://bugs.webkit.org/show_bug.cgi?id=142810
+ rdar://problem/19632130
+
+ Reviewed by Darin Adler.
+
+ When pruning the cache estimate relative value of each entry using formula
+
+ age = current time - creation time
+ accessAge = last access time - creation time
+ value = accessAge / age
+
+ That is, we value entries that have been accessed recently and survived in the cache longest.
+
+ The most valuable entries don't get deleted at all while the deletion probablity ramps up for
+ lower value entries.
+
+ * NetworkProcess/cache/NetworkCacheFileSystemPosix.h:
+ (WebKit::NetworkCache::fileTimes):
+
+ Get the creation time and access time for a file.
+
+ (WebKit::NetworkCache::updateFileAccessTimeIfNeeded):
+
+ Update access time manually if the file system doesn't do it automatically.
+
+ * NetworkProcess/cache/NetworkCacheIOChannel.h:
+ (WebKit::NetworkCache::IOChannel::path):
+ (WebKit::NetworkCache::IOChannel::type):
+ * NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm:
+ (WebKit::NetworkCache::IOChannel::IOChannel):
+ (WebKit::NetworkCache::IOChannel::open):
+
+ Remember the file path and move most of the work to constructor.
+
+ * NetworkProcess/cache/NetworkCacheStorage.cpp:
+ (WebKit::NetworkCache::Storage::Storage):
+ (WebKit::NetworkCache::Storage::remove):
+ (WebKit::NetworkCache::Storage::updateFileAccessTime):
+ (WebKit::NetworkCache::Storage::dispatchReadOperation):
+ (WebKit::NetworkCache::deletionProbability):
+
+ Compute the probability based on entry age and access time.
+
+ (WebKit::NetworkCache::Storage::shrinkIfNeeded):
+ * NetworkProcess/cache/NetworkCacheStorage.h:
+ (WebKit::NetworkCache::Storage::serialBackgroundIOQueue):
+ (WebKit::NetworkCache::Storage::deleteQueue): Deleted.
+
+ More generic name.
+
2015-03-18 Chris Dumez <[email protected]>
[WK2] Log total number of network cache queries using diagnostic logging
Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystemPosix.h (181699 => 181700)
--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystemPosix.h 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystemPosix.h 2015-03-18 17:40:34 UTC (rev 181700)
@@ -31,6 +31,8 @@
#include "NetworkCacheKey.h"
#include <WebCore/FileSystem.h>
#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/time.h>
#include <wtf/text/CString.h>
namespace WebKit {
@@ -67,9 +69,34 @@
});
}
+struct FileTimes {
+ std::chrono::system_clock::time_point creation;
+ std::chrono::system_clock::time_point access;
+};
+
+inline FileTimes fileTimes(const String& path)
+{
+ struct stat fileInfo;
+ if (stat(WebCore::fileSystemRepresentation(path).data(), &fileInfo))
+ return { };
+ return { std::chrono::system_clock::from_time_t(fileInfo.st_birthtime), std::chrono::system_clock::from_time_t(fileInfo.st_atime) };
}
+
+inline void updateFileAccessTimeIfNeeded(const String& path)
+{
+ auto times = fileTimes(path);
+ if (times.creation != times.access) {
+ // Don't update more than once per hour;
+ if (std::chrono::system_clock::now() - times.access < std::chrono::hours(1))
+ return;
+ }
+ // This really updates both access time and modification time.
+ utimes(WebCore::fileSystemRepresentation(path).data(), 0);
}
+}
+}
+
#endif // ENABLE(NETWORK_CACHE)
#endif // NetworkCacheFileSystemPosix_h
Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h (181699 => 181700)
--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h 2015-03-18 17:40:34 UTC (rev 181700)
@@ -45,15 +45,21 @@
void readSync(size_t offset, size_t, std::function<void (Data&, int error)>);
void write(size_t offset, const Data&, std::function<void (int error)>);
+ const String& path() const { return m_path; }
+ Type type() const { return m_type; }
+
int fileDescriptor() const { return m_fileDescriptor; }
private:
- IOChannel(int fd);
+ IOChannel(const String& filePath, IOChannel::Type);
+ String m_path;
+ Type m_type;
+
+ int m_fileDescriptor { 0 };
#if PLATFORM(COCOA)
DispatchPtr<dispatch_io_t> m_dispatchIO;
#endif
- int m_fileDescriptor { 0 };
};
}
Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm (181699 => 181700)
--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm 2015-03-18 17:40:34 UTC (rev 181700)
@@ -39,23 +39,14 @@
namespace WebKit {
namespace NetworkCache {
-IOChannel::IOChannel(int fd)
- : m_fileDescriptor(fd)
+IOChannel::IOChannel(const String& filePath, Type type)
+ : m_path(filePath)
+ , m_type(type)
{
- m_dispatchIO = adoptDispatch(dispatch_io_create(DISPATCH_IO_RANDOM, fd, dispatch_get_main_queue(), [fd](int) {
- close(fd);
- }));
- ASSERT(m_dispatchIO.get());
- // This makes the channel read/write all data before invoking the handlers.
- dispatch_io_set_low_water(m_dispatchIO.get(), std::numeric_limits<size_t>::max());
-}
-
-Ref<IOChannel> IOChannel::open(const String& filePath, IOChannel::Type type)
-{
int oflag;
mode_t mode;
- switch (type) {
+ switch (m_type) {
case Type::Create:
oflag = O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK;
mode = S_IRUSR | S_IWUSR;
@@ -71,10 +62,21 @@
CString path = WebCore::fileSystemRepresentation(filePath);
int fd = ::open(path.data(), oflag, mode);
+ m_fileDescriptor = fd;
- return adoptRef(*new IOChannel(fd));
+ m_dispatchIO = adoptDispatch(dispatch_io_create(DISPATCH_IO_RANDOM, fd, dispatch_get_main_queue(), [fd](int) {
+ close(fd);
+ }));
+ ASSERT(m_dispatchIO.get());
+ // This makes the channel read/write all data before invoking the handlers.
+ dispatch_io_set_low_water(m_dispatchIO.get(), std::numeric_limits<size_t>::max());
}
+Ref<IOChannel> IOChannel::open(const String& filePath, IOChannel::Type type)
+{
+ return adoptRef(*new IOChannel(filePath, type));
+}
+
void IOChannel::read(size_t offset, size_t size, std::function<void (Data&, int error)> completionHandler)
{
RefPtr<IOChannel> channel(this);
Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp (181699 => 181700)
--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp 2015-03-18 17:40:34 UTC (rev 181700)
@@ -65,7 +65,7 @@
, m_directoryPath(makeVersionedDirectoryPath(baseDirectoryPath))
, m_ioQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage", WorkQueue::Type::Concurrent))
, m_backgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.background", WorkQueue::Type::Concurrent, WorkQueue::QOS::Background))
- , m_deleteQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.delete", WorkQueue::Type::Serial, WorkQueue::QOS::Background))
+ , m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background))
{
deleteOldVersions();
initialize();
@@ -282,11 +282,19 @@
m_contentsFilter.remove(key.shortHash());
StringCapture filePathCapture(filePathForKey(key, m_directoryPath));
- deleteQueue().dispatch([this, filePathCapture] {
+ serialBackgroundIOQueue().dispatch([this, filePathCapture] {
WebCore::deleteFile(filePathCapture.string());
});
}
+void Storage::updateFileAccessTime(IOChannel& channel)
+{
+ StringCapture filePathCapture(channel.path());
+ serialBackgroundIOQueue().dispatch([filePathCapture] {
+ updateFileAccessTimeIfNeeded(filePathCapture.string());
+ });
+}
+
void Storage::dispatchReadOperation(const ReadOperation& read)
{
ASSERT(RunLoop::isMain());
@@ -294,16 +302,17 @@
StringCapture cachePathCapture(m_directoryPath);
ioQueue().dispatch([this, &read, cachePathCapture] {
- auto channel = openFileForKey(read.key, IOChannel::Type::Read, cachePathCapture.string());
- int fd = channel->fileDescriptor();
- channel->read(0, std::numeric_limits<size_t>::max(), [this, &read, fd](Data& fileData, int error) {
+ RefPtr<IOChannel> channel = openFileForKey(read.key, IOChannel::Type::Read, cachePathCapture.string());
+ channel->read(0, std::numeric_limits<size_t>::max(), [this, channel, &read](Data& fileData, int error) {
if (error) {
remove(read.key);
read.completionHandler(nullptr);
} else {
- auto entry = decodeEntry(fileData, fd, read.key);
+ auto entry = decodeEntry(fileData, channel->fileDescriptor(), read.key);
bool success = read.completionHandler(WTF::move(entry));
- if (!success)
+ if (success)
+ updateFileAccessTime(*channel);
+ else
remove(read.key);
}
@@ -569,12 +578,31 @@
});
}
+static double deletionProbability(FileTimes times)
+{
+ static const double maximumProbability { 0.33 };
+
+ using namespace std::chrono;
+ auto age = system_clock::now() - times.creation;
+ auto accessAge = times.access - times.creation;
+
+ // For sanity.
+ if (age <= seconds::zero() || accessAge < seconds::zero() || accessAge > age)
+ return maximumProbability;
+
+ // We like old entries that have been accessed recently.
+ auto relativeValue = duration<double>(accessAge) / age;
+
+ // Adjust a bit so the most valuable entries don't get deleted at all.
+ auto effectiveValue = std::min(1.1 * relativeValue, 1.);
+
+ return (1 - effectiveValue) * maximumProbability;
+}
+
void Storage::shrinkIfNeeded()
{
ASSERT(RunLoop::isMain());
- static const double deletionProbability { 0.25 };
-
if (m_approximateSize <= m_maximumSize)
return;
if (m_shrinkInProgress)
@@ -591,7 +619,12 @@
traverseCacheFiles(cachePath, [this](const String& fileName, const String& partitionPath) {
auto filePath = WebCore::pathByAppendingComponent(partitionPath, fileName);
- bool shouldDelete = randomNumber() < deletionProbability;
+ auto times = fileTimes(filePath);
+ auto probability = deletionProbability(times);
+ bool shouldDelete = randomNumber() < probability;
+
+ LOG(NetworkCacheStorage, "Deletion probability=%f shouldDelete=%d", probability, shouldDelete);
+
if (!shouldDelete) {
long long fileSize = 0;
WebCore::getFileSize(filePath, fileSize);
Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h (181699 => 181700)
--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h 2015-03-18 17:27:22 UTC (rev 181699)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h 2015-03-18 17:40:34 UTC (rev 181700)
@@ -40,6 +40,8 @@
namespace WebKit {
namespace NetworkCache {
+class IOChannel;
+
class Storage {
WTF_MAKE_NONCOPYABLE(Storage);
public:
@@ -95,9 +97,11 @@
void dispatchHeaderWriteOperation(const WriteOperation&);
void dispatchPendingWriteOperations();
+ void updateFileAccessTime(IOChannel&);
+
WorkQueue& ioQueue() { return m_ioQueue.get(); }
WorkQueue& backgroundIOQueue() { return m_backgroundIOQueue.get(); }
- WorkQueue& deleteQueue() { return m_deleteQueue.get(); }
+ WorkQueue& serialBackgroundIOQueue() { return m_serialBackgroundIOQueue.get(); }
const String m_baseDirectoryPath;
const String m_directoryPath;
@@ -117,7 +121,7 @@
Ref<WorkQueue> m_ioQueue;
Ref<WorkQueue> m_backgroundIOQueue;
- Ref<WorkQueue> m_deleteQueue;
+ Ref<WorkQueue> m_serialBackgroundIOQueue;
};
}