diff options
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/http/http_cache.cc | 10 | ||||
-rw-r--r-- | net/http/http_cache.h | 7 | ||||
-rw-r--r-- | net/http/http_cache_transaction.cc | 40 | ||||
-rw-r--r-- | net/http/http_cache_transaction.h | 9 | ||||
-rw-r--r-- | net/http/http_cache_unittest.cc | 27 | ||||
-rw-r--r-- | net/http/mock_http_cache.cc | 4 | ||||
-rw-r--r-- | net/http/mock_http_cache.h | 3 |
8 files changed, 101 insertions, 2 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index e2686bd..68d5687 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -645,6 +645,9 @@ NET_ERROR(CACHE_CHECKSUM_READ_FAILURE, -407) // SimpleCache backend, but not by any URLRequest methods or members. NET_ERROR(CACHE_CHECKSUM_MISMATCH, -408) +// Internal error code for the HTTP cache. The cache lock timeout has fired. +NET_ERROR(CACHE_LOCK_TIMEOUT, -409) + // The server's response was insecure (e.g. there was a cert error). NET_ERROR(INSECURE_RESPONSE, -501) diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index 74ce8a3..aa6fc22 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -290,6 +290,7 @@ HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, : net_log_(params.net_log), backend_factory_(backend_factory), building_backend_(false), + bypass_lock_for_test_(false), mode_(NORMAL), network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), weak_factory_(this) { @@ -304,6 +305,7 @@ HttpCache::HttpCache(HttpNetworkSession* session, : net_log_(session->net_log()), backend_factory_(backend_factory), building_backend_(false), + bypass_lock_for_test_(false), mode_(NORMAL), network_layer_(new HttpNetworkLayer(session)), weak_factory_(this) { @@ -315,6 +317,7 @@ HttpCache::HttpCache(HttpTransactionFactory* network_layer, : net_log_(net_log), backend_factory_(backend_factory), building_backend_(false), + bypass_lock_for_test_(false), mode_(NORMAL), network_layer_(network_layer), weak_factory_(this) { @@ -457,7 +460,12 @@ int HttpCache::CreateTransaction(RequestPriority priority, CreateBackend(NULL, net::CompletionCallback()); } - trans->reset(new HttpCache::Transaction(priority, this)); + HttpCache::Transaction* transaction = + new HttpCache::Transaction(priority, this); + if (bypass_lock_for_test_) + transaction->BypassLockForTest(); + + trans->reset(transaction); return OK; } diff --git a/net/http/http_cache.h b/net/http/http_cache.h index 732a9c0..752b98a 100644 --- a/net/http/http_cache.h +++ b/net/http/http_cache.h @@ -187,6 +187,12 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, // Initializes the Infinite Cache, if selected by the field trial. void InitializeInfiniteCache(const base::FilePath& path); + // Causes all transactions created after this point to effectively bypass + // the cache lock whenever there is lock contention. + void BypassLockForTest() { + bypass_lock_for_test_ = true; + } + // HttpTransactionFactory implementation: virtual int CreateTransaction(RequestPriority priority, scoped_ptr<HttpTransaction>* trans) OVERRIDE; @@ -389,6 +395,7 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, // Used when lazily constructing the disk_cache_. scoped_ptr<BackendFactory> backend_factory_; bool building_backend_; + bool bypass_lock_for_test_; Mode mode_; diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index efb21e1..6c9140b 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -212,6 +212,7 @@ HttpCache::Transaction::Transaction( done_reading_(false), vary_mismatch_(false), couldnt_conditionalize_request_(false), + bypass_lock_for_test_(false), io_buf_len_(0), read_offset_(0), effective_load_flags_(0), @@ -1210,7 +1211,20 @@ int HttpCache::Transaction::DoAddToEntry() { net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY); DCHECK(entry_lock_waiting_since_.is_null()); entry_lock_waiting_since_ = TimeTicks::Now(); - return cache_->AddTransactionToEntry(new_entry_, this); + int rv = cache_->AddTransactionToEntry(new_entry_, this); + if (rv == ERR_IO_PENDING) { + if (bypass_lock_for_test_) { + OnAddToEntryTimeout(entry_lock_waiting_since_); + } else { + const int kTimeoutSeconds = 20; + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, + weak_factory_.GetWeakPtr(), entry_lock_waiting_since_), + TimeDelta::FromSeconds(kTimeoutSeconds)); + } + } + return rv; } int HttpCache::Transaction::DoAddToEntryComplete(int result) { @@ -1235,6 +1249,17 @@ int HttpCache::Transaction::DoAddToEntryComplete(int result) { return OK; } + if (result == ERR_CACHE_LOCK_TIMEOUT) { + // The cache is busy, bypass it for this transaction. + mode_ = NONE; + next_state_ = STATE_SEND_REQUEST; + if (partial_) { + partial_->RestoreHeaders(&custom_request_->extra_headers); + partial_.reset(); + } + return OK; + } + if (result != OK) { NOTREACHED(); return result; @@ -2360,6 +2385,19 @@ int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { return ERR_CACHE_READ_FAILURE; } +void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { + if (entry_lock_waiting_since_ != start_time) + return; + + DCHECK_EQ(next_state_, STATE_ADD_TO_ENTRY_COMPLETE); + + if (!cache_) + return; + + cache_->RemovePendingTransaction(this); + OnIOComplete(ERR_CACHE_LOCK_TIMEOUT); +} + void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { DVLOG(2) << "DoomPartialEntry"; int rv = cache_->DoomEntry(cache_key_, NULL); diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h index aeef83e..084b601 100644 --- a/net/http/http_cache_transaction.h +++ b/net/http/http_cache_transaction.h @@ -103,6 +103,11 @@ class HttpCache::Transaction : public HttpTransaction { const BoundNetLog& net_log() const; + // Bypasses the cache lock whenever there is lock contention. + void BypassLockForTest() { + bypass_lock_for_test_ = true; + } + // HttpTransaction methods: virtual int Start(const HttpRequestInfo* request_info, const CompletionCallback& callback, @@ -350,6 +355,9 @@ class HttpCache::Transaction : public HttpTransaction { // transaction should be restarted. int OnCacheReadError(int result, bool restart); + // Called when the cache lock timeout fires. + void OnAddToEntryTimeout(base::TimeTicks start_time); + // Deletes the current partial cache entry (sparse), and optionally removes // the control object (partial_). void DoomPartialEntry(bool delete_object); @@ -412,6 +420,7 @@ class HttpCache::Transaction : public HttpTransaction { bool done_reading_; // All available data was read. bool vary_mismatch_; // The request doesn't match the stored vary data. bool couldnt_conditionalize_request_; + bool bypass_lock_for_test_; // A test is exercising the cache lock. scoped_refptr<IOBuffer> read_buf_; int io_buf_len_; int read_offset_; diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc index c87fa1b..83241ab 100644 --- a/net/http/http_cache_unittest.cc +++ b/net/http/http_cache_unittest.cc @@ -1699,6 +1699,33 @@ TEST(HttpCache, SimpleGET_ManyWriters_BypassCache) { } } +// Tests that a (simulated) timeout allows transactions waiting on the cache +// lock to continue. +TEST(HttpCache, SimpleGET_WriterTimeout) { + MockHttpCache cache; + cache.BypassCacheLock(); + + MockHttpRequest request(kSimpleGET_Transaction); + Context c1, c2; + ASSERT_EQ(net::OK, cache.CreateTransaction(&c1.trans)); + ASSERT_EQ(net::ERR_IO_PENDING, + c1.trans->Start(&request, c1.callback.callback(), + net::BoundNetLog())); + ASSERT_EQ(net::OK, cache.CreateTransaction(&c2.trans)); + ASSERT_EQ(net::ERR_IO_PENDING, + c2.trans->Start(&request, c2.callback.callback(), + net::BoundNetLog())); + + // The second request is queued after the first one. + + c2.callback.WaitForResult(); + ReadAndVerifyTransaction(c2.trans.get(), kSimpleGET_Transaction); + + // Complete the first transaction. + c1.callback.WaitForResult(); + ReadAndVerifyTransaction(c1.trans.get(), kSimpleGET_Transaction); +} + TEST(HttpCache, SimpleGET_AbandonedCacheRead) { MockHttpCache cache; diff --git a/net/http/mock_http_cache.cc b/net/http/mock_http_cache.cc index 5dbd486..a91b2c1 100644 --- a/net/http/mock_http_cache.cc +++ b/net/http/mock_http_cache.cc @@ -516,6 +516,10 @@ int MockHttpCache::CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) { return http_cache_.CreateTransaction(net::DEFAULT_PRIORITY, trans); } +void MockHttpCache::BypassCacheLock() { + http_cache_.BypassLockForTest(); +} + bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry, net::HttpResponseInfo* response_info, bool* response_truncated) { diff --git a/net/http/mock_http_cache.h b/net/http/mock_http_cache.h index 0fd192e..5ead727 100644 --- a/net/http/mock_http_cache.h +++ b/net/http/mock_http_cache.h @@ -177,6 +177,9 @@ class MockHttpCache { // Wrapper around http_cache()->CreateTransaction(net::DEFAULT_PRIORITY...) int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans); + // Wrapper to bypass the cache lock for new transactions. + void BypassCacheLock(); + // Helper function for reading response info from the disk cache. static bool ReadResponseInfo(disk_cache::Entry* disk_entry, net::HttpResponseInfo* response_info, |