Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Unified Diff: net/disk_cache/simple/simple_entry_impl.cc

Issue 13907009: Support optimistic Create and Write operations on the SimpleCache. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/disk_cache/simple/simple_entry_impl.h ('k') | net/http/http_cache.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/disk_cache/simple/simple_entry_impl.cc
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index 2296ecde8e720f7945dcd219ecadd879c2ea869f..cf9066abd8734388838767db48ffdfe171b1572c 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -79,16 +79,35 @@ SimpleEntryImpl::SimpleEntryImpl(SimpleBackendImpl* backend,
arrays_should_be_same_size2);
COMPILE_ASSERT(arraysize(data_size_) == arraysize(have_written_),
arrays_should_be_same_size3);
-
MakeUninitialized();
}
int SimpleEntryImpl::OpenEntry(Entry** out_entry,
const CompletionCallback& callback) {
DCHECK(backend_);
+ // This enumeration is used in histograms, add entries only at end.
+ enum OpenEntryIndexEnum {
+ INDEX_NOEXIST = 0,
+ INDEX_MISS = 1,
+ INDEX_HIT = 2,
+ INDEX_MAX = 3,
+ };
+ OpenEntryIndexEnum open_entry_index_enum = INDEX_NOEXIST;
+ if (backend_) {
+ if (backend_->index()->Has(key_))
+ open_entry_index_enum = INDEX_HIT;
+ else
+ open_entry_index_enum = INDEX_MISS;
+ }
+ UMA_HISTOGRAM_ENUMERATION("SimpleCache.OpenEntryIndexState",
+ open_entry_index_enum, INDEX_MAX);
+
+ // If entry is not known to the index, initiate fast failover to the network.
+ if (open_entry_index_enum == INDEX_MISS)
+ return net::ERR_FAILED;
pending_operations_.push(base::Bind(&SimpleEntryImpl::OpenEntryInternal,
- this, out_entry, callback));
+ this, callback, out_entry));
RunNextOperationIfNeeded();
return net::ERR_IO_PENDING;
}
@@ -96,15 +115,42 @@ int SimpleEntryImpl::OpenEntry(Entry** out_entry,
int SimpleEntryImpl::CreateEntry(Entry** out_entry,
const CompletionCallback& callback) {
DCHECK(backend_);
- pending_operations_.push(base::Bind(&SimpleEntryImpl::CreateEntryInternal,
- this, out_entry, callback));
+ int ret_value = net::ERR_FAILED;
+ if (state_ == STATE_UNINITIALIZED &&
+ pending_operations_.size() == 0) {
+ ReturnEntryToCaller(out_entry);
+ // We can do optimistic Create.
+ pending_operations_.push(base::Bind(&SimpleEntryImpl::CreateEntryInternal,
+ this,
+ CompletionCallback(),
+ static_cast<Entry**>(NULL)));
+ ret_value = net::OK;
+ } else {
+ pending_operations_.push(base::Bind(&SimpleEntryImpl::CreateEntryInternal,
+ this,
+ callback,
+ out_entry));
+ ret_value = net::ERR_IO_PENDING;
+ }
+
+ // We insert the entry in the index before creating the entry files in the
+ // SimpleSynchronousEntry, because this way the worst scenario is when we
+ // have the entry in the index but we don't have the created files yet, this
+ // way we never leak files. CreationOperationComplete will remove the entry
+ // from the index if the creation fails.
+ if (backend_)
+ backend_->index()->Insert(key_);
+
+ // Since we don't know the correct values for |last_used_| and
+ // |last_modified_| yet, we make this approximation.
+ last_used_ = last_modified_ = base::Time::Now();
+
RunNextOperationIfNeeded();
- return net::ERR_IO_PENDING;
+ return ret_value;
}
int SimpleEntryImpl::DoomEntry(const CompletionCallback& callback) {
MarkAsDoomed();
-
scoped_ptr<int> result(new int());
Closure task = base::Bind(&SimpleSynchronousEntry::DoomEntry, path_, key_,
entry_hash_, result.get());
@@ -152,6 +198,7 @@ Time SimpleEntryImpl::GetLastModified() const {
int32 SimpleEntryImpl::GetDataSize(int stream_index) const {
DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK_LE(0, data_size_[stream_index]);
return data_size_[stream_index];
}
@@ -165,7 +212,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
return net::ERR_INVALID_ARGUMENT;
if (offset >= data_size_[stream_index] || offset < 0 || !buf_len)
return 0;
- buf_len = std::min(buf_len, data_size_[stream_index] - offset);
+
// TODO(felipeg): Optimization: Add support for truly parallel read
// operations.
pending_operations_.push(
@@ -191,19 +238,41 @@ int SimpleEntryImpl::WriteData(int stream_index,
buf_len < 0) {
return net::ERR_INVALID_ARGUMENT;
}
- pending_operations_.push(
- base::Bind(&SimpleEntryImpl::WriteDataInternal,
- this,
- stream_index,
- offset,
- make_scoped_refptr(buf),
- buf_len,
- callback,
- truncate));
+
+ int ret_value = net::ERR_FAILED;
+ if (state_ == STATE_READY && pending_operations_.size() == 0) {
+ // We can only do optimistic Write if there is no pending operations, so
+ // that we are sure that the next call to RunNextOperationIfNeeded will
+ // actually run the write operation that sets the stream size. It also
+ // prevents from previous possibly-conflicting writes that could be stacked
+ // in the |pending_operations_|. We could optimize this for when we have
+ // only read operations enqueued.
+ pending_operations_.push(
+ base::Bind(&SimpleEntryImpl::WriteDataInternal, this, stream_index,
+ offset, make_scoped_refptr(buf), buf_len,
+ CompletionCallback(), truncate));
+ ret_value = buf_len;
+ } else {
+ pending_operations_.push(
+ base::Bind(&SimpleEntryImpl::WriteDataInternal, this, stream_index,
+ offset, make_scoped_refptr(buf), buf_len, callback,
+ truncate));
+ ret_value = net::ERR_IO_PENDING;
+ }
+
+ if (truncate) {
+ data_size_[stream_index] = offset + buf_len;
+ } else {
+ data_size_[stream_index] = std::max(offset + buf_len,
+ data_size_[stream_index]);
+ }
+
+ // Since we don't know the correct values for |last_used_| and
+ // |last_modified_| yet, we make this approximation.
+ last_used_ = last_modified_ = base::Time::Now();
+
RunNextOperationIfNeeded();
- // TODO(felipeg): Optimization: Add support for optimistic writes, quickly
- // returning net::OK here.
- return net::ERR_IO_PENDING;
+ return ret_value;
}
int SimpleEntryImpl::ReadSparseData(int64 offset,
@@ -258,7 +327,7 @@ int SimpleEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
SimpleEntryImpl::~SimpleEntryImpl() {
DCHECK(io_thread_checker_.CalledOnValidThread());
DCHECK_EQ(0U, pending_operations_.size());
- DCHECK_EQ(STATE_UNINITIALIZED, state_);
+ DCHECK(STATE_UNINITIALIZED == state_ || STATE_FAILURE == state_);
DCHECK(!synchronous_entry_);
RemoveSelfFromBackend();
}
@@ -268,9 +337,11 @@ void SimpleEntryImpl::MakeUninitialized() {
std::memset(crc32s_end_offset_, 0, sizeof(crc32s_end_offset_));
std::memset(crc32s_, 0, sizeof(crc32s_));
std::memset(have_written_, 0, sizeof(have_written_));
+ std::memset(data_size_, 0, sizeof(data_size_));
}
void SimpleEntryImpl::ReturnEntryToCaller(Entry** out_entry) {
+ DCHECK(out_entry);
++open_count_;
AddRef(); // Balanced in Close()
*out_entry = this;
@@ -292,10 +363,8 @@ void SimpleEntryImpl::MarkAsDoomed() {
void SimpleEntryImpl::RunNextOperationIfNeeded() {
DCHECK(io_thread_checker_.CalledOnValidThread());
-
UMA_HISTOGRAM_CUSTOM_COUNTS("SimpleCache.EntryOperationsPending",
pending_operations_.size(), 0, 100, 20);
-
if (!pending_operations_.empty() && state_ != STATE_IO_PENDING) {
base::Closure operation = pending_operations_.front();
pending_operations_.pop();
@@ -304,43 +373,23 @@ void SimpleEntryImpl::RunNextOperationIfNeeded() {
}
}
-void SimpleEntryImpl::OpenEntryInternal(Entry** out_entry,
- const CompletionCallback& callback) {
+void SimpleEntryImpl::OpenEntryInternal(const CompletionCallback& callback,
+ Entry** out_entry) {
ScopedOperationRunner operation_runner(this);
-
if (state_ == STATE_READY) {
ReturnEntryToCaller(out_entry);
MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(callback,
net::OK));
return;
- }
- DCHECK_EQ(STATE_UNINITIALIZED, state_);
-
- // This enumeration is used in histograms, add entries only at end.
- enum OpenEntryIndexEnum {
- INDEX_NOEXIST = 0,
- INDEX_MISS = 1,
- INDEX_HIT = 2,
- INDEX_MAX = 3,
- };
- OpenEntryIndexEnum open_entry_index_enum = INDEX_NOEXIST;
- if (backend_) {
- if (backend_->index()->Has(key_))
- open_entry_index_enum = INDEX_HIT;
- else
- open_entry_index_enum = INDEX_MISS;
- }
- UMA_HISTOGRAM_ENUMERATION("SimpleCache.OpenEntryIndexState",
- open_entry_index_enum, INDEX_MAX);
- // If entry is not known to the index, initiate fast failover to the network.
- if (open_entry_index_enum == INDEX_MISS) {
- MessageLoopProxy::current()->PostTask(FROM_HERE,
- base::Bind(callback,
- net::ERR_FAILED));
+ } else if (state_ == STATE_FAILURE) {
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ callback, net::ERR_FAILED));
+ }
return;
}
+ DCHECK_EQ(STATE_UNINITIALIZED, state_);
state_ = STATE_IO_PENDING;
-
const base::TimeTicks start_time = base::TimeTicks::Now();
typedef SimpleSynchronousEntry* PointerToSimpleSynchronousEntry;
scoped_ptr<PointerToSimpleSynchronousEntry> sync_entry(
@@ -354,15 +403,15 @@ void SimpleEntryImpl::OpenEntryInternal(Entry** out_entry,
WorkerPool::PostTaskAndReply(FROM_HERE, task, reply, true);
}
-void SimpleEntryImpl::CreateEntryInternal(Entry** out_entry,
- const CompletionCallback& callback) {
+void SimpleEntryImpl::CreateEntryInternal(const CompletionCallback& callback,
+ Entry** out_entry) {
ScopedOperationRunner operation_runner(this);
-
- if (state_ == STATE_READY) {
+ if (state_ != STATE_UNINITIALIZED) {
// There is already an active normal entry.
- MessageLoopProxy::current()->PostTask(FROM_HERE,
- base::Bind(callback,
- net::ERR_FAILED));
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ callback, net::ERR_FAILED));
+ }
return;
}
DCHECK_EQ(STATE_UNINITIALIZED, state_);
@@ -373,13 +422,6 @@ void SimpleEntryImpl::CreateEntryInternal(Entry** out_entry,
for (int i = 0; i < kSimpleEntryFileCount; ++i)
have_written_[i] = true;
- // We insert the entry in the index before creating the entry files in the
- // SimpleSynchronousEntry, because this way the worst scenario is when we
- // have the entry in the index but we don't have the created files yet, this
- // way we never leak files. CreationOperationComplete will remove the entry
- // from the index if the creation fails.
- if (backend_)
- backend_->index()->Insert(key_);
const base::TimeTicks start_time = base::TimeTicks::Now();
typedef SimpleSynchronousEntry* PointerToSimpleSynchronousEntry;
scoped_ptr<PointerToSimpleSynchronousEntry> sync_entry(
@@ -395,32 +437,38 @@ void SimpleEntryImpl::CreateEntryInternal(Entry** out_entry,
void SimpleEntryImpl::CloseInternal() {
DCHECK(io_thread_checker_.CalledOnValidThread());
- DCHECK_EQ(0U, pending_operations_.size());
- DCHECK_EQ(STATE_READY, state_);
- DCHECK(synchronous_entry_);
-
- state_ = STATE_IO_PENDING;
-
typedef SimpleSynchronousEntry::CRCRecord CRCRecord;
-
scoped_ptr<std::vector<CRCRecord> >
crc32s_to_write(new std::vector<CRCRecord>());
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- if (have_written_[i]) {
- if (data_size_[i] == crc32s_end_offset_[i]) {
- int32 crc = data_size_[i] == 0 ? crc32(0, Z_NULL, 0) : crc32s_[i];
- crc32s_to_write->push_back(CRCRecord(i, true, crc));
- } else {
- crc32s_to_write->push_back(CRCRecord(i, false, 0));
+
+ if (state_ == STATE_READY) {
+ DCHECK(synchronous_entry_);
+ state_ = STATE_IO_PENDING;
+ for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ if (have_written_[i]) {
+ if (data_size_[i] == crc32s_end_offset_[i]) {
+ int32 crc = data_size_[i] == 0 ? crc32(0, Z_NULL, 0) : crc32s_[i];
+ crc32s_to_write->push_back(CRCRecord(i, true, crc));
+ } else {
+ crc32s_to_write->push_back(CRCRecord(i, false, 0));
+ }
}
}
+ } else {
+ DCHECK_EQ(STATE_FAILURE, state_);
+ }
+
+ if (synchronous_entry_) {
+ Closure task = base::Bind(&SimpleSynchronousEntry::Close,
+ base::Unretained(synchronous_entry_),
+ base::Passed(&crc32s_to_write));
+ Closure reply = base::Bind(&SimpleEntryImpl::CloseOperationComplete, this);
+ synchronous_entry_ = NULL;
+ WorkerPool::PostTaskAndReply(FROM_HERE, task, reply, true);
+ } else {
+ synchronous_entry_ = NULL;
+ CloseOperationComplete();
}
- Closure task = base::Bind(&SimpleSynchronousEntry::Close,
- base::Unretained(synchronous_entry_),
- base::Passed(&crc32s_to_write));
- Closure reply = base::Bind(&SimpleEntryImpl::CloseOperationComplete, this);
- WorkerPool::PostTaskAndReply(FROM_HERE, task, reply, true);
- synchronous_entry_ = NULL;
}
void SimpleEntryImpl::ReadDataInternal(int stream_index,
@@ -429,7 +477,26 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
int buf_len,
const CompletionCallback& callback) {
DCHECK(io_thread_checker_.CalledOnValidThread());
+ ScopedOperationRunner operation_runner(this);
+
+ if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) {
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ callback, net::ERR_FAILED));
+ }
+ return;
+ }
DCHECK_EQ(STATE_READY, state_);
+ buf_len = std::min(buf_len, GetDataSize(stream_index) - offset);
+ if (offset < 0 || buf_len <= 0) {
+ // If there is nothing to read, we bail out before setting state_ to
+ // STATE_IO_PENDING.
+ if (!callback.is_null())
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ callback, 0));
+ return;
+ }
+
state_ = STATE_IO_PENDING;
if (backend_)
backend_->index()->UseIfExists(key_);
@@ -453,6 +520,17 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
const CompletionCallback& callback,
bool truncate) {
DCHECK(io_thread_checker_.CalledOnValidThread());
+ ScopedOperationRunner operation_runner(this);
+ if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) {
+ if (!callback.is_null()) {
+ // We need to posttask so that we don't go in a loop when we call the
+ // callback directly.
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ callback, net::ERR_FAILED));
+ }
+ // |this| may be destroyed after return here.
+ return;
+ }
DCHECK_EQ(STATE_READY, state_);
state_ = STATE_IO_PENDING;
if (backend_)
@@ -495,25 +573,35 @@ void SimpleEntryImpl::CreationOperationComplete(
DCHECK_EQ(state_, STATE_IO_PENDING);
DCHECK(in_sync_entry);
DCHECK(in_result);
-
ScopedOperationRunner operation_runner(this);
-
UMA_HISTOGRAM_BOOLEAN(
"SimpleCache.EntryCreationResult", *in_result == net::OK);
if (*in_result != net::OK) {
if (*in_result!= net::ERR_FILE_EXISTS)
MarkAsDoomed();
+ if (!completion_callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ completion_callback, net::ERR_FAILED));
+ }
MakeUninitialized();
- completion_callback.Run(net::ERR_FAILED);
+ state_ = STATE_FAILURE;
return;
}
+ // If out_entry is NULL, it means we already called ReturnEntryToCaller from
+ // the optimistic Create case.
+ if (out_entry)
+ ReturnEntryToCaller(out_entry);
+
state_ = STATE_READY;
synchronous_entry_ = *in_sync_entry;
SetSynchronousData();
- ReturnEntryToCaller(out_entry);
UMA_HISTOGRAM_TIMES("SimpleCache.EntryCreationTime",
(base::TimeTicks::Now() - start_time));
- completion_callback.Run(net::OK);
+
+ if (!completion_callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ completion_callback, net::OK));
+ }
}
void SimpleEntryImpl::EntryOperationComplete(
@@ -524,15 +612,19 @@ void SimpleEntryImpl::EntryOperationComplete(
DCHECK(synchronous_entry_);
DCHECK_EQ(STATE_IO_PENDING, state_);
DCHECK(result);
-
state_ = STATE_READY;
-
if (*result < 0) {
MarkAsDoomed();
+ state_ = STATE_FAILURE;
crc32s_end_offset_[stream_index] = 0;
+ } else {
+ SetSynchronousData();
+ }
+
+ if (!completion_callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
+ completion_callback, *result));
}
- SetSynchronousData();
- completion_callback.Run(*result);
RunNextOperationIfNeeded();
}
@@ -598,14 +690,14 @@ void SimpleEntryImpl::ChecksumOperationComplete(
void SimpleEntryImpl::CloseOperationComplete() {
DCHECK(!synchronous_entry_);
DCHECK_EQ(0, open_count_);
- DCHECK_EQ(STATE_IO_PENDING, state_);
-
+ DCHECK(STATE_IO_PENDING == state_ || STATE_FAILURE == state_);
MakeUninitialized();
RunNextOperationIfNeeded();
}
void SimpleEntryImpl::SetSynchronousData() {
DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK(synchronous_entry_);
DCHECK_EQ(STATE_READY, state_);
// TODO(felipeg): These copies to avoid data races are not optimal. While
// adding an IO thread index (for fast misses etc...), we can store this data
« no previous file with comments | « net/disk_cache/simple/simple_entry_impl.h ('k') | net/http/http_cache.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698