| 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..6d52215e2d6ec0906af822e65792f5f3ce2cbedf 100644
|
| --- a/net/http/http_cache_transaction.cc
|
| +++ b/net/http/http_cache_transaction.cc
|
| @@ -600,7 +600,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // Start():
|
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
|
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
|
| -// BeginCacheValidation() -> SetupEntryForRead()
|
| +// BeginCacheValidation() -> SetupEntryForRead() -> WaitBeforeRead*
|
| //
|
| // Read():
|
| // CacheReadData*
|
| @@ -612,7 +612,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
|
| // UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
|
| // UpdateCachedResponseComplete -> OverwriteCachedResponse ->
|
| -// PartialHeadersReceived
|
| +// PartialHeadersReceived -> WaitBeforeRead*
|
| //
|
| // Read():
|
| // CacheReadData*
|
| @@ -712,6 +712,31 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between
|
| // CacheReadResponse* and CacheDispatchValidation.
|
| int HttpCache::Transaction::DoLoop(int result) {
|
| + 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 +876,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 +917,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;
|
| }
|
|
|
| @@ -1497,7 +1524,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 +1557,26 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| }
|
|
|
| next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
|
| +
|
| + if (!entry_)
|
| + return OK;
|
| +
|
| + // If this is the writer, give chance to another transaction to start
|
| + // validation.
|
| + if (entry_->writer == this && request_->method != "HEAD") {
|
| + cache_->DoneValidationWriteEntry(entry_, this);
|
| + return OK;
|
| + }
|
| +
|
| + // Invalidate any current entry with a successful response.
|
| + if (new_response->headers->response_code() != 304) {
|
| + DCHECK_EQ(entry_->validating_transaction, this);
|
| + cache_->DoneValidationWithEntry(entry_, this, false);
|
| + entry_ = nullptr;
|
| + mode_ = NONE;
|
| + return OK;
|
| + }
|
| +
|
| return OK;
|
| }
|
|
|
| @@ -1599,8 +1646,8 @@ 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;
|
| + if (cache_->ConvertWriterToReader(entry_, this))
|
| + mode_ = READ;
|
| }
|
| // We no longer need the network transaction, so destroy it.
|
| ResetNetworkTransaction();
|
| @@ -1736,6 +1783,52 @@ 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, it does not need to wait.
|
| + if (entry_->writer == this)
|
| + return OK;
|
| +
|
| + // If the transaction was converted to a reader but not in readers yet, then
|
| + // it must be added to the queue and another transaction can start the
|
| + // validation phase. If the transaction was already successfully added as a
|
| + // reader then do nothing since it does not need to wait and processing
|
| + // another transaction would already have been taken care of by calling
|
| + // ConvertWriterToReader.
|
| + if (entry_->validating_transaction == this && !entry_->IsReader(this))
|
| + return cache_->DoneValidationWithEntry(entry_, this, true);
|
| +
|
| + return OK;
|
| +}
|
| +
|
| +int HttpCache::Transaction::DoWaitBeforeReadComplete(int rv) {
|
| + if (rv == ERR_CACHE_RACE) {
|
| + next_state_ = STATE_INIT_ENTRY;
|
| + cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED;
|
| + entry_ = nullptr;
|
| + if (network_trans_)
|
| + network_trans_.reset();
|
| + 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;
|
| +}
|
| +
|
| int HttpCache::Transaction::DoCacheReadMetadata() {
|
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadMetadata");
|
| DCHECK(entry_);
|
| @@ -2565,8 +2658,12 @@ int HttpCache::Transaction::SetupEntryForRead() {
|
| partial_.reset();
|
| }
|
| }
|
| - cache_->ConvertWriterToReader(entry_);
|
| - mode_ = READ;
|
| +
|
| + // 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.
|
| + if (cache_->ConvertWriterToReader(entry_, this))
|
| + mode_ = READ;
|
|
|
| if (request_->method == "HEAD")
|
| FixHeadersForHead();
|
| @@ -2651,7 +2748,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
|
| }
|
|
|