summaryrefslogtreecommitdiffstats
path: root/net/disk_cache
diff options
context:
space:
mode:
authorgavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-25 22:01:33 +0000
committergavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-25 22:01:33 +0000
commit431476af95d57168f8a6e1a747eabf5d631d9946 (patch)
tree82d0dddf95dfba1ba85d524f3e984d474a961410 /net/disk_cache
parentb12a2fece633f85b8cd2079077c2e181daff30c2 (diff)
downloadchromium_src-431476af95d57168f8a6e1a747eabf5d631d9946.zip
chromium_src-431476af95d57168f8a6e1a747eabf5d631d9946.tar.gz
chromium_src-431476af95d57168f8a6e1a747eabf5d631d9946.tar.bz2
Add new simple disk cache backend.
Introducing the simple backend that stores one disk_cache entry per file. At this point, entries can be created, doomed, and basic caching appears to work. It is asynchronous, performing all IO operations on a worker pool. It's careful enough about lifetime that it should be possible to delete quickly, without error or races. It's missing many features of a real disk cache. Most notably, it can't detect corruption, it does not have any kind of IO thread bitmap/hashes to allow fast responses, and it does not have eviction at all. This issue is downstream of https://codereview.chromium.org/12207120/ (remove bad const from disk cache interfaces), which must land before it. It is upstream of https://codereview.chromium.org/12226095/ (Make synchronous methods reentrant), and must land before that issue. R=rvargas@chromium.org,pasko@chromium.org BUG=173388,173390 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=182845 Review URL: https://codereview.chromium.org/12192005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@184493 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/disk_cache')
-rw-r--r--net/disk_cache/backend_impl.cc7
-rw-r--r--net/disk_cache/simple/simple_backend_impl.cc154
-rw-r--r--net/disk_cache/simple/simple_backend_impl.h87
-rw-r--r--net/disk_cache/simple/simple_disk_format.h28
-rw-r--r--net/disk_cache/simple/simple_entry_impl.cc266
-rw-r--r--net/disk_cache/simple/simple_entry_impl.h112
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.cc322
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.h111
8 files changed, 1087 insertions, 0 deletions
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc
index f870531..ae08765 100644
--- a/net/disk_cache/backend_impl.cc
+++ b/net/disk_cache/backend_impl.cc
@@ -28,6 +28,7 @@
#include "net/disk_cache/experiments.h"
#include "net/disk_cache/file.h"
#include "net/disk_cache/mem_backend_impl.h"
+#include "net/disk_cache/simple/simple_backend_impl.h"
// This has to be defined before including histogram_macros.h from this file.
#define NET_DISK_CACHE_BACKEND_IMPL_CC_
@@ -262,6 +263,7 @@ int CreateCacheBackend(net::CacheType type, const base::FilePath& path,
bool force, base::MessageLoopProxy* thread,
net::NetLog* net_log, Backend** backend,
const net::CompletionCallback& callback) {
+ // TODO(pasko): Separate out cache creation when landing cache tracer.
DCHECK(!callback.is_null());
if (type == net::MEMORY_CACHE) {
*backend = MemBackendImpl::CreateBackend(max_bytes, net_log);
@@ -269,8 +271,13 @@ int CreateCacheBackend(net::CacheType type, const base::FilePath& path,
}
DCHECK(thread);
+#if defined(USE_SIMPLE_CACHE_BACKEND)
+ return SimpleBackendImpl::CreateBackend(path, force, max_bytes, type, kNone,
+ thread, net_log, backend, callback);
+#else
return BackendImpl::CreateBackend(path, force, max_bytes, type, kNone, thread,
net_log, backend, callback);
+#endif
}
// Returns the preferred maximum number of bytes for the cache given the
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
new file mode 100644
index 0000000..a9da4ad
--- /dev/null
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/simple/simple_backend_impl.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/simple/simple_entry_impl.h"
+
+using base::FilePath;
+using base::MessageLoopProxy;
+using base::Time;
+using base::WorkerPool;
+using file_util::DirectoryExists;
+using file_util::CreateDirectory;
+
+namespace {
+
+const char* kSimpleBackendSubdirectory = "Simple";
+
+} // namespace
+
+namespace disk_cache {
+
+// static
+int SimpleBackendImpl::CreateBackend(
+ const FilePath& full_path,
+ bool force,
+ int max_bytes,
+ net::CacheType type,
+ uint32 flags,
+ scoped_refptr<base::TaskRunner> task_runner,
+ net::NetLog* net_log,
+ Backend** backend,
+ const CompletionCallback& callback) {
+ // TODO(gavinp): Use the |net_log|.
+ DCHECK_EQ(net::DISK_CACHE, type);
+ FilePath simple_cache_path =
+ full_path.AppendASCII(kSimpleBackendSubdirectory);
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleBackendImpl::EnsureCachePathExists,
+ simple_cache_path,
+ MessageLoopProxy::current(), callback,
+ backend),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+SimpleBackendImpl::~SimpleBackendImpl() {
+}
+
+net::CacheType SimpleBackendImpl::GetCacheType() const {
+ return net::DISK_CACHE;
+}
+
+int32 SimpleBackendImpl::GetEntryCount() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+int SimpleBackendImpl::OpenEntry(const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback) {
+ return SimpleEntryImpl::OpenEntry(path_, key, entry, callback);
+}
+
+int SimpleBackendImpl::CreateEntry(const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback) {
+ return SimpleEntryImpl::CreateEntry(path_, key, entry, callback);
+}
+
+int SimpleBackendImpl::DoomEntry(const std::string& key,
+ const net::CompletionCallback& callback) {
+ return SimpleEntryImpl::DoomEntry(path_, key, callback);
+}
+
+int SimpleBackendImpl::DoomAllEntries(const CompletionCallback& callback) {
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+int SimpleBackendImpl::DoomEntriesBetween(
+ const Time initial_time,
+ const Time end_time,
+ const CompletionCallback& callback) {
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+int SimpleBackendImpl::DoomEntriesSince(
+ const Time initial_time,
+ const CompletionCallback& callback) {
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+int SimpleBackendImpl::OpenNextEntry(void** iter,
+ Entry** next_entry,
+ const CompletionCallback& callback) {
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+void SimpleBackendImpl::EndEnumeration(void** iter) {
+ NOTIMPLEMENTED();
+}
+
+void SimpleBackendImpl::GetStats(
+ std::vector<std::pair<std::string, std::string> >* stats) {
+ NOTIMPLEMENTED();
+}
+
+void SimpleBackendImpl::OnExternalCacheHit(const std::string& key) {
+ NOTIMPLEMENTED();
+}
+
+SimpleBackendImpl::SimpleBackendImpl(
+ const FilePath& path) : path_(path) {
+}
+
+// static
+void SimpleBackendImpl::EnsureCachePathExists(
+ const FilePath& path,
+ const scoped_refptr<base::TaskRunner>& callback_runner,
+ const CompletionCallback& callback,
+ Backend** backend) {
+ int result = net::OK;
+ if (!DirectoryExists(path) && !CreateDirectory(path))
+ result = net::ERR_FAILED;
+ callback_runner->PostTask(FROM_HERE,
+ base::Bind(&SimpleBackendImpl::OnCachePathCreated,
+ result, path, callback, backend));
+}
+
+// static
+void SimpleBackendImpl::OnCachePathCreated(int result,
+ const FilePath& path,
+ const CompletionCallback& callback,
+ Backend** backend) {
+ if (result == net::OK)
+ *backend = new SimpleBackendImpl(path);
+ else
+ *backend = NULL;
+ callback.Run(result);
+}
+
+} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h
new file mode 100644
index 0000000..9e56915
--- /dev/null
+++ b/net/disk_cache/simple/simple_backend_impl.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/task_runner.h"
+#include "net/base/cache_type.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace disk_cache {
+
+// SimpleBackendImpl is a new cache backend that stores entries in individual
+// files.
+
+// It is currently a work in progress, missing many features of a real cache,
+// such as eviction.
+
+// See http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend
+
+class SimpleBackendImpl : public Backend {
+ public:
+ virtual ~SimpleBackendImpl();
+
+ static int CreateBackend(const base::FilePath& full_path,
+ bool force,
+ int max_bytes,
+ net::CacheType type,
+ uint32 flags,
+ scoped_refptr<base::TaskRunner> thread,
+ net::NetLog* net_log,
+ Backend** backend,
+ const CompletionCallback& callback);
+
+ // From Backend:
+ virtual net::CacheType GetCacheType() const OVERRIDE;
+ virtual int32 GetEntryCount() const OVERRIDE;
+ virtual int OpenEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int CreateEntry(const std::string& key, Entry** entry,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int DoomEntry(const std::string& key,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int DoomAllEntries(const CompletionCallback& callback) OVERRIDE;
+ virtual int DoomEntriesBetween(base::Time initial_time,
+ base::Time end_time,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int DoomEntriesSince(base::Time initial_time,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int OpenNextEntry(void** iter, Entry** next_entry,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual void EndEnumeration(void** iter) OVERRIDE;
+ virtual void GetStats(
+ std::vector<std::pair<std::string, std::string> >* stats) OVERRIDE;
+ virtual void OnExternalCacheHit(const std::string& key) OVERRIDE;
+
+ private:
+ explicit SimpleBackendImpl(const base::FilePath& path);
+
+ // Creates the Cache directory if needed. Performs blocking IO, so it cannot
+ // be called on IO thread.
+ static void EnsureCachePathExists(
+ const base::FilePath& path,
+ const scoped_refptr<base::TaskRunner>& callback_runner,
+ const CompletionCallback& callback,
+ Backend** backend);
+
+ // IO thread completion of cache creation, called from EnsureCachePath exists
+ // to complete initialization of the cache on the IO thread.
+ static void OnCachePathCreated(int result,
+ const base::FilePath& path,
+ const CompletionCallback& callback,
+ Backend** backend);
+
+ const base::FilePath path_;
+};
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_
diff --git a/net/disk_cache/simple/simple_disk_format.h b/net/disk_cache/simple/simple_disk_format.h
new file mode 100644
index 0000000..052adf2
--- /dev/null
+++ b/net/disk_cache/simple/simple_disk_format.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_
+
+#include "base/port.h"
+
+namespace disk_cache {
+
+const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30);
+
+// A file in the Simple cache consists of a SimpleFileHeader followed
+// by data.
+
+const uint32 kSimpleVersion = 1;
+
+struct SimpleFileHeader {
+ uint64 initial_magic_number;
+ uint32 version;
+ uint32 key_length;
+ uint32 key_hash;
+};
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
new file mode 100644
index 0000000..ba19844
--- /dev/null
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/simple/simple_entry_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/simple/simple_synchronous_entry.h"
+
+namespace {
+
+typedef disk_cache::Entry::CompletionCallback CompletionCallback;
+typedef disk_cache::SimpleSynchronousEntry::SynchronousCreationCallback
+ SynchronousCreationCallback;
+typedef disk_cache::SimpleSynchronousEntry::SynchronousOperationCallback
+ SynchronousOperationCallback;
+
+} // namespace
+
+namespace disk_cache {
+
+using base::FilePath;
+using base::MessageLoopProxy;
+using base::Time;
+using base::WeakPtr;
+using base::WorkerPool;
+
+// static
+int SimpleEntryImpl::OpenEntry(const FilePath& path,
+ const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback) {
+ SynchronousCreationCallback sync_creation_callback =
+ base::Bind(&SimpleEntryImpl::CreationOperationComplete, callback, entry);
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::OpenEntry, path, key,
+ MessageLoopProxy::current(),
+ sync_creation_callback),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+// static
+int SimpleEntryImpl::CreateEntry(const FilePath& path,
+ const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback) {
+ SynchronousCreationCallback sync_creation_callback =
+ base::Bind(&SimpleEntryImpl::CreationOperationComplete, callback, entry);
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::CreateEntry, path,
+ key, MessageLoopProxy::current(),
+ sync_creation_callback),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+// static
+int SimpleEntryImpl::DoomEntry(const FilePath& path,
+ const std::string& key,
+ const CompletionCallback& callback) {
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::DoomEntry, path, key,
+ MessageLoopProxy::current(), callback),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+void SimpleEntryImpl::Doom() {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ return;
+ }
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::DoomAndClose,
+ base::Unretained(synchronous_entry_)),
+ true);
+ synchronous_entry_ = NULL;
+ has_been_doomed_ = true;
+}
+
+void SimpleEntryImpl::Close() {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ delete this;
+ return;
+ }
+ DCHECK(synchronous_entry_ || has_been_doomed_);
+ if (!has_been_doomed_) {
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::Close,
+ base::Unretained(synchronous_entry_)),
+ true);
+ synchronous_entry_ = NULL;
+ }
+ // Entry::Close() is expected to release this entry. See disk_cache.h for
+ // details.
+ delete this;
+}
+
+std::string SimpleEntryImpl::GetKey() const {
+ return key_;
+}
+
+Time SimpleEntryImpl::GetLastUsed() const {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ CHECK(false);
+ }
+ return synchronous_entry_->last_used();
+}
+
+Time SimpleEntryImpl::GetLastModified() const {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ CHECK(false);
+ }
+ return synchronous_entry_->last_modified();
+}
+
+int32 SimpleEntryImpl::GetDataSize(int index) const {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ CHECK(false);
+ }
+ return synchronous_entry_->data_size(index);
+}
+
+int SimpleEntryImpl::ReadData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ // TODO(gavinp): Add support for overlapping reads. The net::HttpCache does
+ // make overlapping read requests when multiple transactions access the same
+ // entry as read only.
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ CHECK(false);
+ }
+ synchronous_entry_in_use_by_worker_ = true;
+ SynchronousOperationCallback sync_operation_callback =
+ base::Bind(&SimpleEntryImpl::EntryOperationComplete,
+ callback, weak_ptr_factory_.GetWeakPtr());
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::ReadData,
+ base::Unretained(synchronous_entry_),
+ index, offset, make_scoped_refptr(buf),
+ buf_len, sync_operation_callback),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+int SimpleEntryImpl::WriteData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback,
+ bool truncate) {
+ if (synchronous_entry_in_use_by_worker_) {
+ NOTIMPLEMENTED();
+ CHECK(false);
+ }
+ synchronous_entry_in_use_by_worker_ = true;
+ SynchronousOperationCallback sync_operation_callback =
+ base::Bind(&SimpleEntryImpl::EntryOperationComplete,
+ callback, weak_ptr_factory_.GetWeakPtr());
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&SimpleSynchronousEntry::WriteData,
+ base::Unretained(synchronous_entry_),
+ index, offset, make_scoped_refptr(buf),
+ buf_len, sync_operation_callback, truncate),
+ true);
+ return net::ERR_IO_PENDING;
+}
+
+int SimpleEntryImpl::ReadSparseData(int64 offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+int SimpleEntryImpl::WriteSparseData(int64 offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+int SimpleEntryImpl::GetAvailableRange(int64 offset,
+ int len,
+ int64* start,
+ const CompletionCallback& callback) {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+bool SimpleEntryImpl::CouldBeSparse() const {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ return false;
+}
+
+void SimpleEntryImpl::CancelSparseIO() {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ NOTIMPLEMENTED();
+}
+
+int SimpleEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
+ // TODO(gavinp): Determine if the simple backend should support sparse data.
+ NOTIMPLEMENTED();
+ return net::ERR_FAILED;
+}
+
+SimpleEntryImpl::SimpleEntryImpl(
+ SimpleSynchronousEntry* synchronous_entry)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ key_(synchronous_entry->key()),
+ synchronous_entry_(synchronous_entry),
+ synchronous_entry_in_use_by_worker_(false),
+ has_been_doomed_(false) {
+ DCHECK(synchronous_entry);
+}
+
+SimpleEntryImpl::~SimpleEntryImpl() {
+ DCHECK(!synchronous_entry_);
+}
+
+// static
+void SimpleEntryImpl::CreationOperationComplete(
+ const CompletionCallback& completion_callback,
+ Entry** out_entry,
+ SimpleSynchronousEntry* sync_entry) {
+ if (!sync_entry) {
+ completion_callback.Run(net::ERR_FAILED);
+ return;
+ }
+ *out_entry = new SimpleEntryImpl(sync_entry);
+ completion_callback.Run(net::OK);
+}
+
+// static
+void SimpleEntryImpl::EntryOperationComplete(
+ const CompletionCallback& completion_callback,
+ base::WeakPtr<SimpleEntryImpl> entry,
+ int result) {
+ if (entry) {
+ DCHECK(entry->synchronous_entry_in_use_by_worker_);
+ entry->synchronous_entry_in_use_by_worker_ = false;
+ }
+ completion_callback.Run(result);
+}
+
+} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
new file mode 100644
index 0000000..26f9d4d
--- /dev/null
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace disk_cache {
+
+class SimpleSynchronousEntry;
+
+// SimpleEntryImpl is the IO thread interface to an entry in the very simple
+// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO
+// on the worker thread.
+class SimpleEntryImpl : public Entry {
+ public:
+ static int OpenEntry(const base::FilePath& path,
+ const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback);
+
+ static int CreateEntry(const base::FilePath& path,
+ const std::string& key,
+ Entry** entry,
+ const CompletionCallback& callback);
+
+ static int DoomEntry(const base::FilePath& path,
+ const std::string& key,
+ const CompletionCallback& callback);
+
+ // From Entry:
+ virtual void Doom() OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual std::string GetKey() const OVERRIDE;
+ virtual base::Time GetLastUsed() const OVERRIDE;
+ virtual base::Time GetLastModified() const OVERRIDE;
+ virtual int32 GetDataSize(int index) const OVERRIDE;
+ virtual int ReadData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int WriteData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback,
+ bool truncate) OVERRIDE;
+ virtual int ReadSparseData(int64 offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int WriteSparseData(int64 offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int GetAvailableRange(int64 offset,
+ int len,
+ int64* start,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual bool CouldBeSparse() const OVERRIDE;
+ virtual void CancelSparseIO() OVERRIDE;
+ virtual int ReadyForSparseIO(const CompletionCallback& callback) OVERRIDE;
+
+ private:
+ explicit SimpleEntryImpl(SimpleSynchronousEntry* synchronous_entry);
+
+ virtual ~SimpleEntryImpl();
+
+ // Called after a SimpleSynchronousEntry has completed CreateEntry() or
+ // OpenEntry(). Constructs the new SimpleEntryImpl (if |result| is net::OK)
+ // and passes it back to the caller via |out_entry|. Also runs
+ // |completion_callback|.
+ static void CreationOperationComplete(
+ const CompletionCallback& completion_callback,
+ Entry** out_entry,
+ SimpleSynchronousEntry* sync_entry);
+
+ // Called after a SimpleSynchronousEntry has completed an asynchronous IO
+ // operation, such as ReadData() or WriteData(). Calls |completion_callback|.
+ static void EntryOperationComplete(
+ const CompletionCallback& completion_callback,
+ base::WeakPtr<SimpleEntryImpl> entry,
+ int result);
+
+ base::WeakPtrFactory<SimpleEntryImpl> weak_ptr_factory_;
+ std::string key_;
+
+ // The |synchronous_entry_| is the worker thread object that performs IO on
+ // entries.
+ SimpleSynchronousEntry* synchronous_entry_;
+
+ // Set to true when a worker operation is posted on the |synchronous_entry_|,
+ // and false after. Used to insure thread safety by not allowing multiple
+ // threads to access the |synchronous_entry_| simultaneously.
+ bool synchronous_entry_in_use_by_worker_;
+
+ bool has_been_doomed_;
+};
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
new file mode 100644
index 0000000..6a8b7f5
--- /dev/null
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/disk_cache/simple/simple_synchronous_entry.h"
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/hash.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/sha1.h"
+#include "base/stringprintf.h"
+#include "base/task_runner.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/simple/simple_disk_format.h"
+
+using base::ClosePlatformFile;
+using base::FilePath;
+using base::GetPlatformFileInfo;
+using base::PlatformFileError;
+using base::PlatformFileInfo;
+using base::PLATFORM_FILE_CREATE;
+using base::PLATFORM_FILE_OK;
+using base::PLATFORM_FILE_OPEN;
+using base::PLATFORM_FILE_READ;
+using base::PLATFORM_FILE_WRITE;
+using base::ReadPlatformFile;
+using base::TaskRunner;
+using base::Time;
+using base::TruncatePlatformFile;
+using base::WritePlatformFile;
+
+namespace {
+
+std::string GetFilenameForKeyAndIndex(const std::string& key, int index) {
+ const std::string sha_hash = base::SHA1HashString(key);
+ return StringPrintf("%02x%02x%02x%02x%02x_%1d",
+ implicit_cast<unsigned char>(sha_hash[0]),
+ implicit_cast<unsigned char>(sha_hash[1]),
+ implicit_cast<unsigned char>(sha_hash[2]),
+ implicit_cast<unsigned char>(sha_hash[3]),
+ implicit_cast<unsigned char>(sha_hash[4]), index);
+}
+
+int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) {
+ int64 data_size = file_size - key_size - sizeof(disk_cache::SimpleFileHeader);
+ DCHECK_GE(implicit_cast<int64>(std::numeric_limits<int32>::max()), data_size);
+ return data_size;
+}
+
+int64 FileOffsetFromDataOffset(size_t key_size, int data_offset) {
+ const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) +
+ key_size;
+ return headers_size + data_offset;
+}
+
+} // namespace
+
+namespace disk_cache {
+
+// static
+void SimpleSynchronousEntry::OpenEntry(
+ const FilePath& path,
+ const std::string& key,
+ const scoped_refptr<TaskRunner>& callback_runner,
+ const SynchronousCreationCallback& callback) {
+ SimpleSynchronousEntry* sync_entry =
+ new SimpleSynchronousEntry(callback_runner, path, key);
+
+ if (!sync_entry->InitializeForOpen()) {
+ delete sync_entry;
+ sync_entry = NULL;
+ }
+ callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry));
+}
+
+// static
+void SimpleSynchronousEntry::CreateEntry(
+ const FilePath& path,
+ const std::string& key,
+ const scoped_refptr<TaskRunner>& callback_runner,
+ const SynchronousCreationCallback& callback) {
+ SimpleSynchronousEntry* sync_entry =
+ new SimpleSynchronousEntry(callback_runner, path, key);
+
+ if (!sync_entry->InitializeForCreate()) {
+ delete sync_entry;
+ sync_entry = NULL;
+ }
+ callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry));
+}
+
+// static
+void SimpleSynchronousEntry::DoomEntry(
+ const FilePath& path,
+ const std::string& key,
+ scoped_refptr<TaskRunner> callback_runner,
+ const net::CompletionCallback& callback) {
+ for (int i = 0; i < kIndexCount; ++i) {
+ FilePath to_delete = path.AppendASCII(GetFilenameForKeyAndIndex(key, i));
+ bool ALLOW_UNUSED result = file_util::Delete(to_delete, false);
+ DLOG_IF(ERROR, !result) << "Could not delete " << to_delete.MaybeAsASCII();
+ }
+ if (!callback.is_null())
+ callback_runner->PostTask(FROM_HERE, base::Bind(callback, net::OK));
+}
+
+void SimpleSynchronousEntry::DoomAndClose() {
+ scoped_refptr<TaskRunner> callback_runner = callback_runner_;
+ FilePath path = path_;
+ std::string key = key_;
+
+ Close();
+ // |this| is now deleted.
+
+ DoomEntry(path, key, callback_runner, net::CompletionCallback());
+}
+
+void SimpleSynchronousEntry::Close() {
+ for (int i = 0; i < kIndexCount; ++i) {
+ bool ALLOW_UNUSED result = ClosePlatformFile(files_[i]);
+ DLOG_IF(INFO, !result) << "Could not Close() file.";
+ }
+ delete this;
+}
+
+void SimpleSynchronousEntry::ReadData(
+ int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const SynchronousOperationCallback& callback) {
+ DCHECK(initialized_);
+
+ int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset);
+ int bytes_read = ReadPlatformFile(files_[index], file_offset,
+ buf->data(), buf_len);
+ if (bytes_read > 0)
+ last_used_ = Time::Now();
+ int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED;
+ callback_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+void SimpleSynchronousEntry::WriteData(
+ int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const SynchronousOperationCallback& callback,
+ bool truncate) {
+ DCHECK(initialized_);
+
+ int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset);
+ if (buf_len > 0) {
+ if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) !=
+ buf_len) {
+ callback_runner_->PostTask(FROM_HERE,
+ base::Bind(callback, net::ERR_FAILED));
+ return;
+ }
+ data_size_[index] = std::max(data_size_[index], offset + buf_len);
+ }
+ if (truncate) {
+ data_size_[index] = offset + buf_len;
+ if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) {
+ callback_runner_->PostTask(FROM_HERE,
+ base::Bind(callback, net::ERR_FAILED));
+ return;
+ }
+ }
+ last_modified_ = Time::Now();
+ callback_runner_->PostTask(FROM_HERE, base::Bind(callback, buf_len));
+}
+
+SimpleSynchronousEntry::SimpleSynchronousEntry(
+ const scoped_refptr<TaskRunner>& callback_runner,
+ const FilePath& path,
+ const std::string& key)
+ : callback_runner_(callback_runner),
+ path_(path),
+ key_(key),
+ initialized_(false) {
+}
+
+SimpleSynchronousEntry::~SimpleSynchronousEntry() {
+}
+
+bool SimpleSynchronousEntry::OpenOrCreateFiles(bool create) {
+ for (int i = 0; i < kIndexCount; ++i) {
+ FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i));
+ int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
+ if (create)
+ flags |= PLATFORM_FILE_CREATE;
+ else
+ flags |= PLATFORM_FILE_OPEN;
+ PlatformFileError error;
+ files_[i] = CreatePlatformFile(filename, flags, NULL, &error);
+ if (error != PLATFORM_FILE_OK) {
+ DVLOG(8) << "CreatePlatformFile error " << error << " while "
+ << (create ? "creating " : "opening ")
+ << filename.MaybeAsASCII();
+ while (--i >= 0) {
+ bool ALLOW_UNUSED did_close = ClosePlatformFile(files_[i]);
+ DLOG_IF(INFO, !did_close) << "Could not close file "
+ << filename.MaybeAsASCII();
+ }
+ return false;
+ }
+ }
+
+ for (int i = 0; i < kIndexCount; ++i) {
+ PlatformFileInfo file_info;
+ bool success = GetPlatformFileInfo(files_[i], &file_info);
+ if (!success) {
+ DLOG(WARNING) << "Could not get platform file info.";
+ continue;
+ }
+ last_used_ = std::max(last_used_, file_info.last_accessed);
+ last_modified_ = std::max(last_modified_, file_info.last_modified);
+ data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size);
+ }
+
+ return true;
+}
+
+bool SimpleSynchronousEntry::InitializeForOpen() {
+ DCHECK(!initialized_);
+ if (!OpenOrCreateFiles(false))
+ return false;
+
+ for (int i = 0; i < kIndexCount; ++i) {
+ SimpleFileHeader header;
+ int header_read_result =
+ ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
+ sizeof(header));
+ if (header_read_result != sizeof(header)) {
+ DLOG(WARNING) << "Cannot read header from entry.";
+ return false;
+ }
+
+ if (header.initial_magic_number != kSimpleInitialMagicNumber) {
+ // TODO(gavinp): This seems very bad; for now we log at WARNING, but we
+ // should give consideration to not saturating the log with these if that
+ // becomes a problem.
+ DLOG(WARNING) << "Magic number did not match.";
+ return false;
+ }
+
+ if (header.version != kSimpleVersion) {
+ DLOG(WARNING) << "Unreadable version.";
+ return false;
+ }
+
+ scoped_ptr<char[]> key(new char[header.key_length]);
+ int key_read_result = ReadPlatformFile(files_[i], sizeof(header),
+ key.get(), header.key_length);
+ if (key_read_result != implicit_cast<int>(header.key_length)) {
+ DLOG(WARNING) << "Cannot read key from entry.";
+ return false;
+ }
+ if (header.key_length != key_.size() ||
+ std::memcmp(key_.data(), key.get(), key_.size()) != 0) {
+ // TODO(gavinp): Since the way we use Entry SHA to name entries means this
+ // is expected to occur at some frequency, add unit_tests that this does
+ // is handled gracefully at higher levels.
+ DLOG(WARNING) << "Key mismatch on open.";
+ return false;
+ }
+
+ if (base::Hash(key.get(), header.key_length) != header.key_hash) {
+ DLOG(WARNING) << "Hash mismatch on key.";
+ return false;
+ }
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+bool SimpleSynchronousEntry::InitializeForCreate() {
+ DCHECK(!initialized_);
+ if (!OpenOrCreateFiles(true)) {
+ DLOG(WARNING) << "Could not create platform files.";
+ return false;
+ }
+
+ for (int i = 0; i < kIndexCount; ++i) {
+ SimpleFileHeader header;
+ header.initial_magic_number = kSimpleInitialMagicNumber;
+ header.version = kSimpleVersion;
+
+ header.key_length = key_.size();
+ header.key_hash = base::Hash(key_);
+
+ if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
+ sizeof(header)) != sizeof(header)) {
+ // TODO(gavinp): Clean up created files.
+ DLOG(WARNING) << "Could not write headers to new cache entry.";
+ return false;
+ }
+
+ if (WritePlatformFile(files_[i], sizeof(header), key_.data(),
+ key_.size()) != implicit_cast<int>(key_.size())) {
+ // TODO(gavinp): Clean up created files.
+ DLOG(WARNING) << "Could not write keys to new cache entry.";
+ return false;
+ }
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h
new file mode 100644
index 0000000..1bf6a54
--- /dev/null
+++ b/net/disk_cache/simple/simple_synchronous_entry.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_
+#define NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/platform_file.h"
+#include "base/task_runner.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace net {
+class IOBuffer;
+}
+
+namespace disk_cache {
+
+// Worker thread interface to the very simple cache. This interface is not
+// thread safe, and callers must insure that it is only ever accessed from
+// a single thread between synchronization points.
+class SimpleSynchronousEntry {
+ public:
+ typedef base::Callback<void(SimpleSynchronousEntry*)>
+ SynchronousCreationCallback;
+
+ typedef base::Callback<void(int)>
+ SynchronousOperationCallback;
+
+ static void OpenEntry(
+ const base::FilePath& path,
+ const std::string& key,
+ const scoped_refptr<base::TaskRunner>& callback_runner,
+ const SynchronousCreationCallback& callback);
+
+ static void CreateEntry(
+ const base::FilePath& path,
+ const std::string& key,
+ const scoped_refptr<base::TaskRunner>& callback_runner,
+ const SynchronousCreationCallback& callback);
+
+ // Deletes an entry without first Opening it. Does not check if there is
+ // already an Entry object in memory holding the open files. Be careful! This
+ // is meant to be used by the Backend::DoomEntry() call. |callback| will be
+ // run by |callback_runner|.
+ static void DoomEntry(const base::FilePath& path,
+ const std::string& key,
+ scoped_refptr<base::TaskRunner> callback_runner,
+ const net::CompletionCallback& callback);
+
+ // N.B. DoomAndClose(), Close(), ReadData() and WriteData() may block on IO.
+ void DoomAndClose();
+ void Close();
+ void ReadData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const SynchronousOperationCallback& callback);
+ void WriteData(int index,
+ int offset,
+ net::IOBuffer* buf,
+ int buf_len,
+ const SynchronousOperationCallback& callback,
+ bool truncate);
+
+ std::string key() const { return key_; }
+ base::Time last_used() const { return last_used_; }
+ base::Time last_modified() const { return last_modified_; }
+ int32 data_size(int index) const { return data_size_[index]; }
+
+ private:
+ static const int kIndexCount = 3;
+
+ SimpleSynchronousEntry(
+ const scoped_refptr<base::TaskRunner>& callback_runner,
+ const base::FilePath& path,
+ const std::string& key);
+
+ // Like Entry, the SimpleSynchronousEntry self releases when Close() is
+ // called.
+ ~SimpleSynchronousEntry();
+
+ bool OpenOrCreateFiles(bool create);
+ bool InitializeForOpen();
+ bool InitializeForCreate();
+
+ scoped_refptr<base::TaskRunner> callback_runner_;
+ const base::FilePath path_;
+ const std::string key_;
+
+ bool initialized_;
+
+ base::Time last_used_;
+ base::Time last_modified_;
+ int32 data_size_[kIndexCount];
+
+ base::PlatformFile files_[kIndexCount];
+};
+
+} // namespace disk_cache
+
+#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_