Chromium Code Reviews| Index: net/http/http_cache_transaction.cc |
| diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc |
| index ede3541aa4010750256d9acb89f3140bf6938def..bd80ac269c55582dc020fbb91b7382f6739d5b83 100644 |
| --- a/net/http/http_cache_transaction.cc |
| +++ b/net/http/http_cache_transaction.cc |
| @@ -169,6 +169,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| new_entry_(NULL), |
| new_response_(NULL), |
| mode_(NONE), |
| + original_mode_(NONE), |
| reading_(false), |
| invalid_range_(false), |
| truncated_(false), |
| @@ -181,6 +182,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| couldnt_conditionalize_request_(false), |
| bypass_lock_for_test_(false), |
| fail_conditionalization_for_test_(false), |
| + validating_cannot_proceed_(false), |
| io_buf_len_(0), |
| read_offset_(0), |
| effective_load_flags_(0), |
| @@ -577,6 +579,11 @@ void HttpCache::Transaction::GetConnectionAttempts( |
| old_connection_attempts_.end()); |
| } |
| +void HttpCache::Transaction::SetValidatingCannotProceed() { |
| + validating_cannot_proceed_ = true; |
| + entry_ = nullptr; |
| +} |
| + |
| size_t HttpCache::Transaction::EstimateMemoryUsage() const { |
| // TODO(xunjieli): Consider improving the coverage. crbug.com/669108. |
| return 0; |
| @@ -600,7 +607,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const { |
| // Start(): |
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* |
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> |
| -// BeginCacheValidation() -> SetupEntryForRead() |
| +// BeginCacheValidation() -> SetupEntryForRead() -> WaitBeforeRead* |
| // |
| // Read(): |
| // CacheReadData* |
| @@ -612,7 +619,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const { |
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> |
| // UpdateCachedResponse -> CacheWriteUpdatedResponse* -> |
| // UpdateCachedResponseComplete -> OverwriteCachedResponse -> |
| -// PartialHeadersReceived |
| +// PartialHeadersReceived -> WaitBeforeRead* |
| // |
| // Read(): |
| // CacheReadData* |
| @@ -712,6 +719,38 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const { |
| // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between |
| // CacheReadResponse* and CacheDispatchValidation. |
| int HttpCache::Transaction::DoLoop(int result) { |
| + // If its the start state machine and it cannot proceed due to failure by |
| + // another transaction in writing the response, restart this transaction. |
| + if (!reading_ && validating_cannot_proceed_) { |
| + validating_cannot_proceed_ = false; |
| + RestartAfterValidationStarted(); |
| + } |
| + |
| + int rv = DoLoopImpl(result); |
| + |
| + // If its the end of the Start() state machine, the transaction might have to |
| + // wait before it can read from the cache if there is another transaction |
| + // writing to the cache currently. This is being done to enable parallelizing |
| + // the validation phase of a transaction while another transaction is still |
| + // writing the response to the cache. |
| + // Checking reading_ will make sure that the control is still in the start |
| + // state machine and also that we do not enter the wait state for |
| + // partial requests that have already started reading and re-entered the |
| + // start() state machine. |
| + if (!reading_ && rv == OK) { |
| + next_state_ = STATE_WAIT_BEFORE_READ; |
| + rv = DoLoopImpl(rv); |
| + } |
| + |
| + if (rv != ERR_IO_PENDING && !callback_.is_null()) { |
| + read_buf_ = nullptr; // Release the buffer before invoking the callback. |
| + base::ResetAndReturn(&callback_).Run(rv); |
| + } |
| + |
| + return rv; |
| +} |
| + |
| +int HttpCache::Transaction::DoLoopImpl(int result) { |
| DCHECK(next_state_ != STATE_NONE); |
| int rv = result; |
| @@ -851,6 +890,13 @@ int HttpCache::Transaction::DoLoop(int result) { |
| case STATE_CACHE_READ_METADATA_COMPLETE: |
| rv = DoCacheReadMetadataComplete(rv); |
| break; |
| + case STATE_WAIT_BEFORE_READ: |
| + DCHECK_EQ(OK, rv); |
| + rv = DoWaitBeforeRead(); |
| + break; |
| + case STATE_WAIT_BEFORE_READ_COMPLETE: |
| + rv = DoWaitBeforeReadComplete(rv); |
| + break; |
| case STATE_NETWORK_READ: |
| DCHECK_EQ(OK, rv); |
| rv = DoNetworkRead(); |
| @@ -885,11 +931,6 @@ int HttpCache::Transaction::DoLoop(int result) { |
| } |
| } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| - if (rv != ERR_IO_PENDING && !callback_.is_null()) { |
| - read_buf_ = NULL; // Release the buffer before invoking the callback. |
| - base::ResetAndReturn(&callback_).Run(rv); |
| - } |
| - |
| return rv; |
| } |
| @@ -965,7 +1006,7 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) { |
| // This is only set if we have something to do with the response. |
| range_requested_ = (partial_.get() != NULL); |
| - |
| + original_mode_ = mode_; |
| return OK; |
| } |
| @@ -1497,7 +1538,7 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| int ret = cache_->DoomEntry(cache_key_, NULL); |
| DCHECK_EQ(OK, ret); |
| } |
| - cache_->DoneWritingToEntry(entry_, true); |
| + cache_->DoneWritingToEntry(entry_, true, this); |
| entry_ = NULL; |
| mode_ = NONE; |
| } |
| @@ -1530,6 +1571,21 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| } |
| next_state_ = STATE_OVERWRITE_CACHED_RESPONSE; |
| + |
| + if (!entry_) |
| + return OK; |
| + |
| + // Invalidate any current entry with a successful response if this transaction |
| + // cannot write to this entry. |
| + if (new_response->headers->response_code() != 304 && |
| + (entry_->writer || !entry_->readers.empty())) { |
| + DCHECK_EQ(entry_->headers_transaction, this); |
| + cache_->DoneResponseHeaders(entry_, this, false); |
| + entry_ = nullptr; |
| + mode_ = NONE; |
| + return OK; |
| + } |
| + |
| return OK; |
| } |
| @@ -1599,7 +1655,6 @@ int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { |
| } else if (entry_ && !handling_206_) { |
| DCHECK_EQ(READ_WRITE, mode_); |
| if (!partial_ || partial_->IsLastRange()) { |
| - cache_->ConvertWriterToReader(entry_); |
| mode_ = READ; |
| } |
| // We no longer need the network transaction, so destroy it. |
| @@ -1736,6 +1791,53 @@ int HttpCache::Transaction::DoPartialHeadersReceived() { |
| return OK; |
| } |
| +int HttpCache::Transaction::DoWaitBeforeRead() { |
| + if (!entry_) |
| + return OK; |
| + |
| + next_state_ = STATE_WAIT_BEFORE_READ_COMPLETE; |
| + |
| + // If |this| is the writer or there is no response body to be written or read, |
| + // it does not need to wait. |
| + if (entry_->writer == this || request_->method == "HEAD") |
|
Randy Smith (Not in Mondays)
2017/03/17 18:13:06
I'm a bit concerned about races here. The transit
shivanisha
2017/03/20 18:14:49
Actually, after the state simplification since a t
|
| + return OK; |
| + |
| + // If |this| is in readers, then no need to wait. |
|
Randy Smith (Not in Mondays)
2017/03/17 18:13:06
This comment doesn't seem to fit the following lin
shivanisha
2017/03/20 18:14:49
Expanded the comment to be more clear. The if cond
|
| + if (entry_->headers_transaction == this) |
|
Randy Smith (Not in Mondays)
2017/03/17 18:13:05
When does this happen? Naively, I think of WAIT_B
shivanisha
2017/03/20 18:14:49
At this point a transaction should always be a hea
|
| + return cache_->DoneResponseHeaders(entry_, this, true); |
| + |
| + return OK; |
| +} |
| + |
| +int HttpCache::Transaction::DoWaitBeforeReadComplete(int rv) { |
| + if (rv == ERR_CACHE_RACE) { |
| + RestartAfterValidationStarted(); |
| + return OK; |
| + } |
| + |
| + if (rv != OK) |
| + return rv; |
| + |
| + // If successful then this must be either be the writer or a reader as the |
| + // response must have completely been written to the cache. |
| + if (entry_->writer == this) |
| + return OK; |
| + |
| + mode_ = READ; |
| + if (network_trans_) |
| + ResetNetworkTransaction(); |
| + return OK; |
| +} |
| + |
| +void HttpCache::Transaction::RestartAfterValidationStarted() { |
|
shivanisha
2017/03/16 15:43:26
DCHECK(!reading_);
shivanisha
2017/03/20 18:14:49
done
|
| + next_state_ = STATE_INIT_ENTRY; |
| + cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED; |
| + entry_ = nullptr; |
| + mode_ = original_mode_; |
| + if (network_trans_) |
| + network_trans_.reset(); |
| +} |
| + |
| int HttpCache::Transaction::DoCacheReadMetadata() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadMetadata"); |
| DCHECK(entry_); |
| @@ -2565,7 +2667,10 @@ int HttpCache::Transaction::SetupEntryForRead() { |
| partial_.reset(); |
| } |
| } |
| - cache_->ConvertWriterToReader(entry_); |
| + |
| + // This might or might not be able to add it as an active reader based on |
| + // whether this is the active writer or not. If not, it will be added to a |
| + // wait queue in DoWaitBeforeRead. |
| mode_ = READ; |
| if (request_->method == "HEAD") |
| @@ -2651,7 +2756,7 @@ void HttpCache::Transaction::DoneWritingToEntry(bool success) { |
| RecordHistograms(); |
| - cache_->DoneWritingToEntry(entry_, success); |
| + cache_->DoneWritingToEntry(entry_, success, this); |
| entry_ = NULL; |
| mode_ = NONE; // switch to 'pass through' mode |
| } |