Chromium Code Reviews| 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 541fa346a25155cb4608fdbd2187973dce4a6871..4fa31adb26e38ed8ede677fab3afc45e69d75a7b 100644 |
| --- a/net/disk_cache/simple/simple_entry_impl.cc |
| +++ b/net/disk_cache/simple/simple_entry_impl.cc |
| @@ -105,7 +105,11 @@ void SimpleEntryImpl::Doom() { |
| void SimpleEntryImpl::Close() { |
| DCHECK(io_thread_checker_.CalledOnValidThread()); |
| - Release(); // Balanced in CreationOperationCompleted(). |
| + // Postpone close operation. |
| + // Push the close operation to the end of the line. This way we run all |
| + // operations before we are able close. |
| + pending_operations_.push(base::Bind(&SimpleEntryImpl::CloseInternal, this)); |
| + RunNextOperationIfNeeded(); |
| } |
| std::string SimpleEntryImpl::GetKey() const { |
| @@ -134,26 +138,21 @@ int SimpleEntryImpl::ReadData(int index, |
| int buf_len, |
| const CompletionCallback& callback) { |
| DCHECK(io_thread_checker_.CalledOnValidThread()); |
| - // 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. This might make calling SimpleSynchronousEntry::Close() |
| - // correctly more tricky (see SimpleEntryImpl::EntryOperationComplete). |
| - if (synchronous_entry_in_use_by_worker_) { |
| - NOTIMPLEMENTED(); |
| - CHECK(false); |
| - } |
| - synchronous_entry_in_use_by_worker_ = true; |
| - if (index_) |
| - index_->UseIfExists(key_); |
| - SynchronousOperationCallback sync_operation_callback = |
| - base::Bind(&SimpleEntryImpl::EntryOperationComplete, |
| - this, callback); |
| - WorkerPool::PostTask(FROM_HERE, |
| - base::Bind(&SimpleSynchronousEntry::ReadData, |
| - base::Unretained(synchronous_entry_), |
| - index, offset, make_scoped_refptr(buf), |
| - buf_len, sync_operation_callback), |
| - true); |
| + if (index < 0 || index >= kSimpleEntryFileCount || buf_len < 0) |
| + return net::ERR_INVALID_ARGUMENT; |
| + if (offset >= data_size_[index] || offset < 0 || !buf_len) |
| + return 0; |
| + // TODO(felipeg): Optimization: Add support for truly parallel read |
| + // operations. |
| + pending_operations_.push( |
| + base::Bind(&SimpleEntryImpl::ReadDataInternal, |
| + this, |
| + index, |
| + offset, |
| + make_scoped_refptr(buf), |
| + buf_len, |
| + callback)); |
| + RunNextOperationIfNeeded(); |
| return net::ERR_IO_PENDING; |
| } |
| @@ -164,22 +163,20 @@ int SimpleEntryImpl::WriteData(int index, |
| const CompletionCallback& callback, |
| bool truncate) { |
| DCHECK(io_thread_checker_.CalledOnValidThread()); |
| - if (synchronous_entry_in_use_by_worker_) { |
| - NOTIMPLEMENTED(); |
| - CHECK(false); |
| - } |
| - synchronous_entry_in_use_by_worker_ = true; |
| - if (index_) |
| - index_->UseIfExists(key_); |
| - SynchronousOperationCallback sync_operation_callback = |
| - base::Bind(&SimpleEntryImpl::EntryOperationComplete, |
| - this, callback); |
| - 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); |
| + if (index < 0 || index >= kSimpleEntryFileCount || offset < 0 || buf_len < 0) |
| + return net::ERR_INVALID_ARGUMENT; |
| + pending_operations_.push( |
| + base::Bind(&SimpleEntryImpl::WriteDataInternal, |
| + this, |
| + index, |
| + offset, |
| + make_scoped_refptr(buf), |
| + buf_len, |
| + callback, |
| + truncate)); |
| + RunNextOperationIfNeeded(); |
| + // TODO(felipeg): Optimization: Add support for optimistic writes, quickly |
| + // returning net::OK here. |
| return net::ERR_IO_PENDING; |
| } |
| @@ -235,27 +232,86 @@ int SimpleEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) { |
| SimpleEntryImpl::SimpleEntryImpl(SimpleIndex* index, |
| const FilePath& path, |
| const std::string& key) |
| - : constructor_thread_(MessageLoopProxy::current()), |
| - index_(index->AsWeakPtr()), |
| - path_(path), |
| - key_(key), |
| - synchronous_entry_(NULL), |
| - synchronous_entry_in_use_by_worker_(false) { |
| + : index_(index->AsWeakPtr()), |
| + path_(path), |
| + key_(key), |
| + synchronous_entry_(NULL), |
| + operation_running_(false) { |
| } |
| SimpleEntryImpl::~SimpleEntryImpl() { |
| - if (synchronous_entry_) { |
| - base::Closure close_sync_entry = |
| - base::Bind(&SimpleSynchronousEntry::Close, |
| - base::Unretained(synchronous_entry_)); |
| - // We aren't guaranteed to be able to run IO on our constructor thread, but |
| - // we are also not guaranteed to be allowed to run WorkerPool::PostTask on |
| - // our other threads. |
| - if (constructor_thread_->BelongsToCurrentThread()) |
| - WorkerPool::PostTask(FROM_HERE, close_sync_entry, true); |
| - else |
| - close_sync_entry.Run(); |
| - } |
| + DCHECK_EQ(0U, pending_operations_.size()); |
| + DCHECK(!operation_running_); |
| +} |
| + |
| +bool SimpleEntryImpl::RunNextOperationIfNeeded() { |
| + DCHECK(io_thread_checker_.CalledOnValidThread()); |
| + if (pending_operations_.size() <= 0 || operation_running_) |
| + return false; |
| + base::Closure operation = pending_operations_.front(); |
| + pending_operations_.pop(); |
| + operation.Run(); |
| + return true; |
| +} |
| + |
| +void SimpleEntryImpl::CloseInternal() { |
| + DCHECK(io_thread_checker_.CalledOnValidThread()); |
| + DCHECK(pending_operations_.size() == 0); |
|
gavinp
2013/04/19 05:51:39
Oops.
|
| + DCHECK(!operation_running_); |
| + DCHECK(synchronous_entry_); |
| + WorkerPool::PostTask(FROM_HERE, |
| + base::Bind(&SimpleSynchronousEntry::Close, |
| + base::Unretained(synchronous_entry_)), |
| + true); |
| + // Entry::Close() is expected to delete this entry. See disk_cache.h for |
| + // details. |
| + Release(); // Balanced in CreationOperationCompleted(). |
| +} |
| + |
| +void SimpleEntryImpl::ReadDataInternal(int index, |
| + int offset, |
| + scoped_refptr<net::IOBuffer> buf, |
| + int buf_len, |
| + const CompletionCallback& callback) { |
| + DCHECK(io_thread_checker_.CalledOnValidThread()); |
| + DCHECK(!operation_running_); |
| + operation_running_ = true; |
| + if (index_) |
| + index_->UseIfExists(key_); |
| + SynchronousOperationCallback sync_operation_callback = |
| + base::Bind(&SimpleEntryImpl::EntryOperationComplete, |
| + this, callback); |
| + WorkerPool::PostTask(FROM_HERE, |
| + base::Bind(&SimpleSynchronousEntry::ReadData, |
| + base::Unretained(synchronous_entry_), |
| + index, offset, buf, |
| + buf_len, sync_operation_callback), |
| + true); |
| +} |
| + |
| +void SimpleEntryImpl::WriteDataInternal(int index, |
| + int offset, |
| + scoped_refptr<net::IOBuffer> buf, |
| + int buf_len, |
| + const CompletionCallback& callback, |
| + bool truncate) { |
| + DCHECK(io_thread_checker_.CalledOnValidThread()); |
| + DCHECK(!operation_running_); |
| + operation_running_ = true; |
| + if (index_) |
| + index_->UseIfExists(key_); |
| + |
| + // TODO(felipeg): When adding support for optimistic writes we need to set |
| + // data_size_[index] = buf_len here. |
| + SynchronousOperationCallback sync_operation_callback = |
| + base::Bind(&SimpleEntryImpl::EntryOperationComplete, |
| + this, callback); |
| + WorkerPool::PostTask(FROM_HERE, |
| + base::Bind(&SimpleSynchronousEntry::WriteData, |
| + base::Unretained(synchronous_entry_), |
| + index, offset, buf, |
| + buf_len, sync_operation_callback, truncate), |
| + true); |
| } |
| void SimpleEntryImpl::CreationOperationComplete( |
| @@ -275,7 +331,7 @@ void SimpleEntryImpl::CreationOperationComplete( |
| // The Backend interface requires us to return |this|, and keep the Entry |
| // alive until Entry::Close(). Adding a reference to self will keep |this| |
| // alive after the scope of the Callback calling us is destroyed. |
| - AddRef(); // Balanced in Close(). |
| + AddRef(); // Balanced in CloseInternal(). |
| synchronous_entry_ = sync_entry; |
| SetSynchronousData(); |
| if (index_) |
| @@ -289,8 +345,9 @@ void SimpleEntryImpl::EntryOperationComplete( |
| int result) { |
| DCHECK(io_thread_checker_.CalledOnValidThread()); |
| DCHECK(synchronous_entry_); |
| - DCHECK(synchronous_entry_in_use_by_worker_); |
| - synchronous_entry_in_use_by_worker_ = false; |
| + DCHECK(operation_running_); |
| + |
| + operation_running_ = false; |
| SetSynchronousData(); |
| if (index_) { |
| if (result >= 0) { |
| @@ -301,11 +358,12 @@ void SimpleEntryImpl::EntryOperationComplete( |
| } |
| } |
| completion_callback.Run(result); |
| + RunNextOperationIfNeeded(); |
| } |
| void SimpleEntryImpl::SetSynchronousData() { |
| DCHECK(io_thread_checker_.CalledOnValidThread()); |
| - DCHECK(!synchronous_entry_in_use_by_worker_); |
| + DCHECK(!operation_running_); |
| // 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 |
| // in that structure. This also solves problems with last_used() on ext4 |