Commit 689ff336 authored by pliard@chromium.org's avatar pliard@chromium.org

Fix race condition for non-open/create operations happening after a doom. ...

Fix race condition for non-open/create operations happening after a doom.                                                                                                                                                                                                                                   

Doom used not to be treated as a regular operation in the sense that it didn't
use the pending operations queue. Therefore it was possible to kick off a doom
operation on an entry while another one was happening.

Review URL: https://chromiumcodereview.appspot.com/22859060

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222014 0039d316-1c4b-4281-b951-d872f2087c98
parent 2b9740bd
...@@ -2874,9 +2874,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) { ...@@ -2874,9 +2874,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) {
entry2->Close(); entry2->Close();
} }
// This test is flaky because of the race of Create followed by a Doom. TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic5) {
// See test SimpleCacheCreateDoomRace.
TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheOptimistic5) {
// Test sequence: // Test sequence:
// Create, Doom, Write, Read, Close. // Create, Doom, Write, Read, Close.
SetSimpleCacheMode(); SetSimpleCacheMode();
...@@ -2945,10 +2943,6 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) { ...@@ -2945,10 +2943,6 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) {
EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1)); EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1));
entry->Doom(); entry->Doom();
// Check that we are not leaking.
EXPECT_TRUE(
static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef());
} }
// Confirm that IO buffers are not referenced by the Simple Cache after a write // Confirm that IO buffers are not referenced by the Simple Cache after a write
...@@ -2987,7 +2981,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticWriteReleases) { ...@@ -2987,7 +2981,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticWriteReleases) {
EXPECT_TRUE(buffer1->HasOneRef()); EXPECT_TRUE(buffer1->HasOneRef());
} }
TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheCreateDoomRace) { TEST_F(DiskCacheEntryTest, SimpleCacheCreateDoomRace) {
// Test sequence: // Test sequence:
// Create, Doom, Write, Close, Check files are not on disk anymore. // Create, Doom, Write, Close, Check files are not on disk anymore.
SetSimpleCacheMode(); SetSimpleCacheMode();
...@@ -3008,17 +3002,10 @@ TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheCreateDoomRace) { ...@@ -3008,17 +3002,10 @@ TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheCreateDoomRace) {
cache_->DoomEntry(key, cb.callback()); cache_->DoomEntry(key, cb.callback());
EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING)); EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING));
// Lets do a Write so we block until all operations are done, so we can check
// the HasOneRef() below. This call can't be optimistic and we are checking
// that here.
EXPECT_EQ( EXPECT_EQ(
net::ERR_IO_PENDING, kSize1,
entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false)); entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
// Check that we are not leaking.
EXPECT_TRUE(
static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef());
entry->Close(); entry->Close();
// Finish running the pending tasks so that we fully complete the close // Finish running the pending tasks so that we fully complete the close
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h" #include "base/message_loop/message_loop_proxy.h"
#include "base/task_runner.h" #include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
...@@ -264,12 +265,8 @@ int SimpleEntryImpl::DoomEntry(const CompletionCallback& callback) { ...@@ -264,12 +265,8 @@ int SimpleEntryImpl::DoomEntry(const CompletionCallback& callback) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_DOOM_BEGIN); net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_DOOM_BEGIN);
MarkAsDoomed(); MarkAsDoomed();
scoped_ptr<int> result(new int()); pending_operations_.push(SimpleEntryOperation::DoomOperation(this, callback));
Closure task = base::Bind(&SimpleSynchronousEntry::DoomEntry, path_, key_, RunNextOperationIfNeeded();
entry_hash_, result.get());
Closure reply = base::Bind(&CallCompletionCallback,
callback, base::Passed(&result));
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
return net::ERR_IO_PENDING; return net::ERR_IO_PENDING;
} }
...@@ -587,6 +584,9 @@ void SimpleEntryImpl::RunNextOperationIfNeeded() { ...@@ -587,6 +584,9 @@ void SimpleEntryImpl::RunNextOperationIfNeeded() {
operation->callback(), operation->callback(),
operation->truncate()); operation->truncate());
break; break;
case SimpleEntryOperation::TYPE_DOOM:
DoomEntryInternal(operation->callback());
break;
default: default:
NOTREACHED(); NOTREACHED();
} }
...@@ -613,7 +613,8 @@ void SimpleEntryImpl::OpenEntryInternal(bool have_index, ...@@ -613,7 +613,8 @@ void SimpleEntryImpl::OpenEntryInternal(bool have_index,
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END, net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END,
CreateNetLogSimpleEntryCreationCallback(this, net::OK)); CreateNetLogSimpleEntryCreationCallback(this, net::OK));
return; return;
} else if (state_ == STATE_FAILURE) { }
if (state_ == STATE_FAILURE) {
if (!callback.is_null()) { if (!callback.is_null()) {
MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind( MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(
callback, net::ERR_FAILED)); callback, net::ERR_FAILED));
...@@ -743,7 +744,6 @@ void SimpleEntryImpl::CloseInternal() { ...@@ -743,7 +744,6 @@ void SimpleEntryImpl::CloseInternal() {
} }
} }
} else { } else {
synchronous_entry_ = NULL;
CloseOperationComplete(); CloseOperationComplete();
} }
} }
...@@ -901,6 +901,15 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, ...@@ -901,6 +901,15 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply); worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
} }
void SimpleEntryImpl::DoomEntryInternal(const CompletionCallback& callback) {
PostTaskAndReplyWithResult(
worker_pool_, FROM_HERE,
base::Bind(&SimpleSynchronousEntry::DoomEntry, path_, key_, entry_hash_),
base::Bind(&SimpleEntryImpl::DoomOperationComplete, this, callback,
state_));
state_ = STATE_IO_PENDING;
}
void SimpleEntryImpl::CreationOperationComplete( void SimpleEntryImpl::CreationOperationComplete(
const CompletionCallback& completion_callback, const CompletionCallback& completion_callback,
const base::TimeTicks& start_time, const base::TimeTicks& start_time,
...@@ -1073,6 +1082,15 @@ void SimpleEntryImpl::WriteOperationComplete( ...@@ -1073,6 +1082,15 @@ void SimpleEntryImpl::WriteOperationComplete(
stream_index, completion_callback, *entry_stat, result.Pass()); stream_index, completion_callback, *entry_stat, result.Pass());
} }
void SimpleEntryImpl::DoomOperationComplete(const CompletionCallback& callback,
State state_to_restore,
int result) {
state_ = state_to_restore;
if (!callback.is_null())
callback.Run(result);
RunNextOperationIfNeeded();
}
void SimpleEntryImpl::ChecksumOperationComplete( void SimpleEntryImpl::ChecksumOperationComplete(
int orig_result, int orig_result,
int stream_index, int stream_index,
......
...@@ -179,6 +179,8 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>, ...@@ -179,6 +179,8 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
const CompletionCallback& callback, const CompletionCallback& callback,
bool truncate); bool truncate);
void DoomEntryInternal(const CompletionCallback& callback);
// Called after a SimpleSynchronousEntry has completed CreateEntry() or // Called after a SimpleSynchronousEntry has completed CreateEntry() or
// OpenEntry(). If |in_sync_entry| is non-NULL, creation is successful and we // OpenEntry(). If |in_sync_entry| is non-NULL, creation is successful and we
// can return |this| SimpleEntryImpl to |*out_entry|. Runs // can return |this| SimpleEntryImpl to |*out_entry|. Runs
...@@ -216,6 +218,11 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>, ...@@ -216,6 +218,11 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
scoped_ptr<SimpleEntryStat> entry_stat, scoped_ptr<SimpleEntryStat> entry_stat,
scoped_ptr<int> result); scoped_ptr<int> result);
// Called after an asynchronous doom completes.
void DoomOperationComplete(const CompletionCallback& callback,
State state_to_restore,
int result);
// Called after validating the checksums on an entry. Passes through the // Called after validating the checksums on an entry. Passes through the
// original result if successful, propogates the error if the checksum does // original result if successful, propogates the error if the checksum does
// not validate. // not validate.
...@@ -277,8 +284,11 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>, ...@@ -277,8 +284,11 @@ class SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl>,
CheckCrcResult crc_check_state_[kSimpleEntryFileCount]; CheckCrcResult crc_check_state_[kSimpleEntryFileCount];
// The |synchronous_entry_| is the worker thread object that performs IO on // The |synchronous_entry_| is the worker thread object that performs IO on
// entries. It's owned by this SimpleEntryImpl whenever |operation_running_| // entries. It's owned by this SimpleEntryImpl whenever |executing_operation_|
// is false (i.e. when an operation is not pending on the worker pool). // is false (i.e. when an operation is not pending on the worker pool). When
// an operation is being executed no one owns the synchronous entry. Therefore
// SimpleEntryImpl should not be deleted while an operation is running as that
// would leak the SimpleSynchronousEntry.
SimpleSynchronousEntry* synchronous_entry_; SimpleSynchronousEntry* synchronous_entry_;
std::queue<SimpleEntryOperation> pending_operations_; std::queue<SimpleEntryOperation> pending_operations_;
......
...@@ -28,7 +28,7 @@ SimpleEntryOperation::SimpleEntryOperation(const SimpleEntryOperation& other) ...@@ -28,7 +28,7 @@ SimpleEntryOperation::SimpleEntryOperation(const SimpleEntryOperation& other)
SimpleEntryOperation::~SimpleEntryOperation() {} SimpleEntryOperation::~SimpleEntryOperation() {}
// Static. // static
SimpleEntryOperation SimpleEntryOperation::OpenOperation( SimpleEntryOperation SimpleEntryOperation::OpenOperation(
SimpleEntryImpl* entry, SimpleEntryImpl* entry,
bool have_index, bool have_index,
...@@ -48,7 +48,7 @@ SimpleEntryOperation SimpleEntryOperation::OpenOperation( ...@@ -48,7 +48,7 @@ SimpleEntryOperation SimpleEntryOperation::OpenOperation(
false); false);
} }
// Static. // static
SimpleEntryOperation SimpleEntryOperation::CreateOperation( SimpleEntryOperation SimpleEntryOperation::CreateOperation(
SimpleEntryImpl* entry, SimpleEntryImpl* entry,
bool have_index, bool have_index,
...@@ -68,7 +68,7 @@ SimpleEntryOperation SimpleEntryOperation::CreateOperation( ...@@ -68,7 +68,7 @@ SimpleEntryOperation SimpleEntryOperation::CreateOperation(
false); false);
} }
// Static. // static
SimpleEntryOperation SimpleEntryOperation::CloseOperation( SimpleEntryOperation SimpleEntryOperation::CloseOperation(
SimpleEntryImpl* entry) { SimpleEntryImpl* entry) {
return SimpleEntryOperation(entry, return SimpleEntryOperation(entry,
...@@ -85,7 +85,7 @@ SimpleEntryOperation SimpleEntryOperation::CloseOperation( ...@@ -85,7 +85,7 @@ SimpleEntryOperation SimpleEntryOperation::CloseOperation(
false); false);
} }
// Static. // static
SimpleEntryOperation SimpleEntryOperation::ReadOperation( SimpleEntryOperation SimpleEntryOperation::ReadOperation(
SimpleEntryImpl* entry, SimpleEntryImpl* entry,
int index, int index,
...@@ -108,7 +108,7 @@ SimpleEntryOperation SimpleEntryOperation::ReadOperation( ...@@ -108,7 +108,7 @@ SimpleEntryOperation SimpleEntryOperation::ReadOperation(
alone_in_queue); alone_in_queue);
} }
// Static. // static
SimpleEntryOperation SimpleEntryOperation::WriteOperation( SimpleEntryOperation SimpleEntryOperation::WriteOperation(
SimpleEntryImpl* entry, SimpleEntryImpl* entry,
int index, int index,
...@@ -132,6 +132,33 @@ SimpleEntryOperation SimpleEntryOperation::WriteOperation( ...@@ -132,6 +132,33 @@ SimpleEntryOperation SimpleEntryOperation::WriteOperation(
false); false);
} }
// static
SimpleEntryOperation SimpleEntryOperation::DoomOperation(
SimpleEntryImpl* entry,
const CompletionCallback& callback) {
net::IOBuffer* const buf = NULL;
Entry** const out_entry = NULL;
const int offset = 0;
const int length = 0;
const bool have_index = false;
const int index = 0;
const bool truncate = false;
const bool optimistic = false;
const bool alone_in_queue = false;
return SimpleEntryOperation(entry,
buf,
callback,
out_entry,
offset,
length,
TYPE_DOOM,
have_index,
index,
truncate,
optimistic,
alone_in_queue);
}
bool SimpleEntryOperation::ConflictsWith( bool SimpleEntryOperation::ConflictsWith(
const SimpleEntryOperation& other_op) const { const SimpleEntryOperation& other_op) const {
if (type_ != TYPE_READ && type_ != TYPE_WRITE) if (type_ != TYPE_READ && type_ != TYPE_WRITE)
......
...@@ -31,6 +31,7 @@ class SimpleEntryOperation { ...@@ -31,6 +31,7 @@ class SimpleEntryOperation {
TYPE_CLOSE = 2, TYPE_CLOSE = 2,
TYPE_READ = 3, TYPE_READ = 3,
TYPE_WRITE = 4, TYPE_WRITE = 4,
TYPE_DOOM = 5,
}; };
SimpleEntryOperation(const SimpleEntryOperation& other); SimpleEntryOperation(const SimpleEntryOperation& other);
...@@ -63,6 +64,10 @@ class SimpleEntryOperation { ...@@ -63,6 +64,10 @@ class SimpleEntryOperation {
bool optimistic, bool optimistic,
const CompletionCallback& callback); const CompletionCallback& callback);
static SimpleEntryOperation DoomOperation(
SimpleEntryImpl* entry,
const CompletionCallback& callback);
bool ConflictsWith(const SimpleEntryOperation& other_op) const; bool ConflictsWith(const SimpleEntryOperation& other_op) const;
// Releases all references. After calling this operation, SimpleEntryOperation // Releases all references. After calling this operation, SimpleEntryOperation
// will only hold POD members. // will only hold POD members.
......
...@@ -257,14 +257,13 @@ bool SimpleSynchronousEntry::DeleteFilesForEntryHash( ...@@ -257,14 +257,13 @@ bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
} }
// static // static
void SimpleSynchronousEntry::DoomEntry( int SimpleSynchronousEntry::DoomEntry(
const FilePath& path, const FilePath& path,
const std::string& key, const std::string& key,
uint64 entry_hash, uint64 entry_hash) {
int* out_result) {
DCHECK_EQ(entry_hash, GetEntryHashKey(key)); DCHECK_EQ(entry_hash, GetEntryHashKey(key));
bool deleted_well = DeleteFilesForEntryHash(path, entry_hash); const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash);
*out_result = deleted_well ? net::OK : net::ERR_FAILED; return deleted_well ? net::OK : net::ERR_FAILED;
} }
// static // static
......
...@@ -85,14 +85,12 @@ class SimpleSynchronousEntry { ...@@ -85,14 +85,12 @@ class SimpleSynchronousEntry {
bool had_index, bool had_index,
SimpleEntryCreationResults* out_results); SimpleEntryCreationResults* out_results);
// Deletes an entry without first Opening it. Does not check if there is // Deletes an entry from the file system without affecting the state of the
// already an Entry object in memory holding the open files. Be careful! This // corresponding instance, if any (allowing operations to continue to be
// is meant to be used by the Backend::DoomEntry() call. |callback| will be // executed through that instance). Returns a net error code.
// run by |callback_runner|. static int DoomEntry(const base::FilePath& path,
static void DoomEntry(const base::FilePath& path, const std::string& key,
const std::string& key, uint64 entry_hash);
uint64 entry_hash,
int* out_result);
// Like |DoomEntry()| above. Deletes all entries corresponding to the // Like |DoomEntry()| above. Deletes all entries corresponding to the
// |key_hashes|. Succeeds only when all entries are deleted. Returns a net // |key_hashes|. Succeeds only when all entries are deleted. Returns a net
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment