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 b8f1eb06b2f9eae0f29072e47e64ecadb800d03e..7f93f704ddcad05ff275395570b74490e059c357 100644 |
| --- a/net/http/http_cache_transaction.cc |
| +++ b/net/http/http_cache_transaction.cc |
| @@ -180,6 +180,9 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| couldnt_conditionalize_request_(false), |
| bypass_lock_for_test_(false), |
| fail_conditionalization_for_test_(false), |
| + shared_(false), |
| + finalize_doomed_(false), |
| + orphaned_(false), |
| io_buf_len_(0), |
| read_offset_(0), |
| effective_load_flags_(0), |
| @@ -189,6 +192,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| total_received_bytes_(0), |
| total_sent_bytes_(0), |
| websocket_handshake_stream_base_create_helper_(NULL), |
| + have_full_request_headers_(false), |
| weak_factory_(this) { |
| TRACE_EVENT0("io", "HttpCacheTransaction::Transaction"); |
| static_assert(HttpCache::Transaction::kNumValidationHeaders == |
| @@ -201,12 +205,18 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| HttpCache::Transaction::~Transaction() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::~Transaction"); |
| + |
| + // Assert to make sure that any owner of HttpCache::Transaction always invokes |
| + // Orphan and not the destructor directly. |
| + DCHECK(orphaned_); |
| + |
| // We may have to issue another IO, but we should never invoke the callback_ |
| // after this point. |
| callback_.Reset(); |
| + weak_factory_.InvalidateWeakPtrs(); |
| if (cache_) { |
| - if (entry_) { |
| + if (entry_ && !finalize_doomed_) { |
| bool cancel_request = reading_ && response_.headers.get(); |
| if (cancel_request) { |
| if (partial_) { |
| @@ -215,7 +225,6 @@ HttpCache::Transaction::~Transaction() { |
| cancel_request &= (response_.headers->response_code() == 200); |
| } |
| } |
| - |
| cache_->DoneWithEntry(entry_, this, cancel_request); |
| } else if (cache_pending_) { |
| cache_->RemovePendingTransaction(this); |
| @@ -262,6 +271,8 @@ bool HttpCache::Transaction::AddTruncatedFlag() { |
| LoadState HttpCache::Transaction::GetWriterLoadState() const { |
| if (network_trans_.get()) |
| return network_trans_->GetLoadState(); |
| + if (shared_ && entry_) |
| + return entry_->shared_writers->GetLoadState(); |
| if (entry_ || !request_) |
| return LOAD_STATE_IDLE; |
| return LOAD_STATE_WAITING_FOR_CACHE; |
| @@ -371,6 +382,10 @@ bool HttpCache::Transaction::IsReadyToRestartForAuth() { |
| int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| const CompletionCallback& callback) { |
| + if (next_state_ == STATE_SHARED_READ_WRITE_FAILED) { |
| + return ERR_CACHE_WRITE_FAILURE; |
| + } |
| + |
| DCHECK_EQ(next_state_, STATE_NONE); |
| DCHECK(buf); |
| DCHECK_GT(buf_len, 0); |
| @@ -395,12 +410,21 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| read_buf_ = buf; |
| io_buf_len_ = buf_len; |
| if (network_trans_) { |
| + DCHECK(!shared_); |
| DCHECK(mode_ == WRITE || mode_ == NONE || |
| (mode_ == READ_WRITE && partial_)); |
| next_state_ = STATE_NETWORK_READ; |
| - } else { |
| + } else if (!shared_) { |
| DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_)); |
| next_state_ = STATE_CACHE_READ_DATA; |
| + } else { // uses SharedWriters |
| + if (read_offset_ == entry_->disk_entry->GetDataSize(kResponseContentIndex)) |
| + next_state_ = STATE_SHARED_NETWORK_READ; |
| + else { |
| + DCHECK_LT(read_offset_, |
| + entry_->disk_entry->GetDataSize(kResponseContentIndex)); |
| + next_state_ = STATE_CACHE_READ_DATA; |
| + } |
| } |
| int rv = DoLoop(OK); |
| @@ -424,8 +448,13 @@ void HttpCache::Transaction::StopCaching() { |
| // TODO(mmenke): This doesn't release the lock on the cache entry, so a |
| // future request for the resource will be blocked on this one. |
| // Fix this. |
| - if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() && |
| - !is_sparse_ && !range_requested_) { |
| + if (shared_) { |
| + // This might or might not stop caching based on whether other consumers |
| + // exist for this resource or not. If it does, shared_ will be set to false. |
| + network_trans_ = cache_->StopCachingSharedWriters(this, entry_); |
| + } |
| + if (!shared_ && cache_.get() && entry_ && (mode_ & WRITE) && |
| + network_trans_.get() && !is_sparse_ && !range_requested_) { |
| mode_ = NONE; |
| } |
| } |
| @@ -434,7 +463,12 @@ bool HttpCache::Transaction::GetFullRequestHeaders( |
| HttpRequestHeaders* headers) const { |
| if (network_trans_) |
| return network_trans_->GetFullRequestHeaders(headers); |
| - |
| + else if (shared_) { |
| + return entry_->shared_writers->GetFullRequestHeaders(headers); |
| + } else if (have_full_request_headers_) { |
| + *headers = full_request_headers_; |
| + return true; |
| + } |
| // TODO(juliatuttle): Read headers from cache. |
| return false; |
| } |
| @@ -443,6 +477,9 @@ int64_t HttpCache::Transaction::GetTotalReceivedBytes() const { |
| int64_t total_received_bytes = total_received_bytes_; |
| if (network_trans_) |
| total_received_bytes += network_trans_->GetTotalReceivedBytes(); |
| + else if (shared_) { |
| + total_received_bytes += entry_->shared_writers->GetTotalReceivedBytes(); |
| + } |
| return total_received_bytes; |
| } |
| @@ -450,14 +487,23 @@ int64_t HttpCache::Transaction::GetTotalSentBytes() const { |
| int64_t total_sent_bytes = total_sent_bytes_; |
| if (network_trans_) |
| total_sent_bytes += network_trans_->GetTotalSentBytes(); |
| + else if (shared_) { |
| + total_sent_bytes += entry_->shared_writers->GetTotalSentBytes(); |
| + } |
| return total_sent_bytes; |
| } |
| void HttpCache::Transaction::DoneReading() { |
| if (cache_.get() && entry_) { |
| DCHECK_NE(mode_, UPDATE); |
| + bool perform_entry_cleanup = true; |
| + if (shared_) { |
| + cache_->DoneReadingSharedWriters(this, entry_); |
| + // entry_ cleanup already performed, if any. |
| + perform_entry_cleanup = false; |
| + } |
| if (mode_ & WRITE) { |
| - DoneWritingToEntry(true); |
| + DoneWritingToEntry(true, perform_entry_cleanup); |
| } else if (mode_ & READ) { |
| // It is necessary to check mode_ & READ because it is possible |
| // for mode_ to be NONE and entry_ non-NULL with a write entry |
| @@ -498,6 +544,8 @@ bool HttpCache::Transaction::GetLoadTimingInfo( |
| LoadTimingInfo* load_timing_info) const { |
| if (network_trans_) |
| return network_trans_->GetLoadTimingInfo(load_timing_info); |
| + else if (shared_) |
| + return entry_->shared_writers->GetLoadTimingInfo(load_timing_info); |
| if (old_network_trans_load_timing_) { |
| *load_timing_info = *old_network_trans_load_timing_; |
| @@ -518,6 +566,8 @@ bool HttpCache::Transaction::GetLoadTimingInfo( |
| bool HttpCache::Transaction::GetRemoteEndpoint(IPEndPoint* endpoint) const { |
| if (network_trans_) |
| return network_trans_->GetRemoteEndpoint(endpoint); |
| + else if (shared_) |
| + return entry_->shared_writers->GetRemoteEndpoint(endpoint); |
| if (!old_remote_endpoint_.address().empty()) { |
| *endpoint = old_remote_endpoint_; |
| @@ -531,6 +581,8 @@ void HttpCache::Transaction::PopulateNetErrorDetails( |
| NetErrorDetails* details) const { |
| if (network_trans_) |
| return network_trans_->PopulateNetErrorDetails(details); |
| + else if (shared_) |
| + return entry_->shared_writers->PopulateNetErrorDetails(details); |
| return; |
| } |
| @@ -538,6 +590,8 @@ void HttpCache::Transaction::SetPriority(RequestPriority priority) { |
| priority_ = priority; |
| if (network_trans_) |
| network_trans_->SetPriority(priority_); |
| + else if (shared_) |
| + return entry_->shared_writers->SetPriority(priority_); |
| } |
| void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( |
| @@ -545,6 +599,10 @@ void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( |
| websocket_handshake_stream_base_create_helper_ = create_helper; |
| if (network_trans_) |
| network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper); |
| + else if (shared_) { |
| + return entry_->shared_writers->SetWebSocketHandshakeStreamCreateHelper( |
| + create_helper); |
| + } |
| } |
| void HttpCache::Transaction::SetBeforeNetworkStartCallback( |
| @@ -562,6 +620,8 @@ void HttpCache::Transaction::SetBeforeHeadersSentCallback( |
| int HttpCache::Transaction::ResumeNetworkStart() { |
| if (network_trans_) |
| return network_trans_->ResumeNetworkStart(); |
| + else if (shared_) |
| + return entry_->shared_writers->ResumeNetworkStart(); |
| return ERR_UNEXPECTED; |
| } |
| @@ -570,6 +630,9 @@ void HttpCache::Transaction::GetConnectionAttempts( |
| ConnectionAttempts new_connection_attempts; |
| if (network_trans_) |
| network_trans_->GetConnectionAttempts(&new_connection_attempts); |
| + else if (shared_) { |
| + entry_->shared_writers->GetConnectionAttempts(&new_connection_attempts); |
| + } |
| out->swap(new_connection_attempts); |
| out->insert(out->begin(), old_connection_attempts_.begin(), |
| @@ -852,6 +915,16 @@ int HttpCache::Transaction::DoLoop(int result) { |
| case STATE_NETWORK_READ_COMPLETE: |
| rv = DoNetworkReadComplete(rv); |
| break; |
| + case STATE_SHARED_NETWORK_READ: |
| + DCHECK_EQ(OK, rv); |
| + rv = DoSharedNetworkRead(); |
| + break; |
| + case STATE_SHARED_NETWORK_READ_COMPLETE: |
| + rv = DoSharedNetworkReadComplete(rv); |
| + break; |
| + case STATE_SHARED_NETWORK_READ_WAIT_COMPLETE: |
| + rv = DoSharedNetworkReadWaitComplete(rv); |
| + break; |
| case STATE_CACHE_READ_DATA: |
| DCHECK_EQ(OK, rv); |
| rv = DoCacheReadData(); |
| @@ -1508,6 +1581,27 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| return OK; |
| } |
| + // If the transaction is shared and it is a 200 or an error response, then |
| + // doom the entry and let this transaction continue without writing to the |
| + // cache if shared writers contain more transactions. If not, continue |
| + // writing to the cache and also transfer the network transaction to shared |
| + // writers. |
| + if (shared_ && new_response->headers->response_code() != 304) { |
| + network_trans_ = cache_->ValidationNoMatchSharedWriters( |
| + cache_key_, this, std::move(network_trans_), priority_, entry_); |
| + if (!shared_) { |
| + DCHECK(network_trans_); |
| + if (cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED) { |
| + UpdateCacheEntryStatus( |
| + CacheEntryStatus::ENTRY_VALIDATED_DOOM_SHARED_WRITING); |
| + } |
| + DoneWritingToEntry(false, false); |
| + return OK; |
| + } else { |
| + DCHECK(!network_trans_); |
| + } |
| + } |
| + |
| // Are we expecting a response to a conditional query? |
| if (mode_ == READ_WRITE || mode_ == UPDATE) { |
| if (new_response->headers->response_code() == 304 || handling_206_) { |
| @@ -1653,7 +1747,40 @@ int HttpCache::Transaction::DoCacheWriteResponse() { |
| int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponseComplete"); |
| next_state_ = STATE_TRUNCATE_CACHED_DATA; |
| - return OnWriteResponseInfoToEntryComplete(result); |
| + int res = OnWriteResponseInfoToEntryComplete(result); |
| + |
| + if (res != OK) |
| + return result; |
| + |
| + return OK; |
| +} |
| + |
| +void HttpCache::Transaction::ResetShared(bool continue_network_reading, |
| + bool continue_cache_reading) { |
| + if (!continue_network_reading) { |
| + SaveSharedNetworkTransInfo(); |
| + } |
| + if (continue_cache_reading) { |
| + mode_ = READ; |
| + } |
| + shared_ = false; |
| +} |
| + |
| +void HttpCache::Transaction::SetFinalizeDoomed() { |
| + finalize_doomed_ = true; |
| +} |
| + |
| +void HttpCache::Transaction::SetShared() { |
| + shared_ = true; |
| +} |
| + |
| +bool HttpCache::Transaction::shared() const { |
| + return shared_; |
| +} |
| + |
| +bool HttpCache::Transaction::validating_shared() const { |
| + return shared_ && next_state_ != STATE_NONE && |
| + next_state_ <= STATE_START_STATE_MACHINE_FINAL; |
| } |
| int HttpCache::Transaction::DoTruncateCachedData() { |
| @@ -1704,11 +1831,46 @@ int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { |
| return OK; |
| } |
| +void HttpCache::Transaction::ProcessForSharedWriting() { |
| + // Should not be already reading. |
| + if (reading_) |
| + return; |
| + |
| + // If not already part of SharedWriters, then check if one should be |
| + // created. |
| + if (!shared_ && !IsEligibleForSharedWriting()) |
| + return; |
| + |
| + if (shared_) { |
| + // Non 304 case is already handled in DoSuccessfulSendRequest. |
| + if (response_.headers->response_code() != 304) { |
| + return; |
| + } |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_VALIDATED_DO_SHARED_WRITING); |
| + cache_->ValidationMatchSharedWriters(this, priority_, entry_); |
| + network_trans_.reset(); |
| + return; |
| + } |
| + |
| + // Do not create a SharedWriters if its a Redirect response. |
| + if (response_.headers->response_code() != 200) |
| + return; |
| + |
| + // or if its a no-store response. |
| + if (!entry_) |
| + return; |
| + |
| + DCHECK(entry_ && network_trans_); |
| + cache_->CreateSharedWriters(this, std::move(network_trans_), priority_); |
| +} |
| + |
| int HttpCache::Transaction::DoPartialHeadersReceived() { |
| new_response_ = NULL; |
| if (entry_ && !partial_ && entry_->disk_entry->GetDataSize(kMetadataIndex)) |
| next_state_ = STATE_CACHE_READ_METADATA; |
| + ProcessForSharedWriting(); |
| + |
| if (!partial_) |
| return OK; |
| @@ -1754,6 +1916,7 @@ int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { |
| int HttpCache::Transaction::DoNetworkRead() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkRead"); |
| next_state_ = STATE_NETWORK_READ_COMPLETE; |
| + |
| return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); |
| } |
| @@ -1773,6 +1936,57 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) { |
| return result; |
| } |
| +int HttpCache::Transaction::DoSharedNetworkRead() { |
| + next_state_ = STATE_SHARED_NETWORK_READ_COMPLETE; |
| + bool shared_read_in_progress = false; |
| + int result = |
| + entry_->shared_writers->Read(read_buf_.get(), io_buf_len_, io_callback_, |
| + this, shared_read_in_progress); |
| + if (shared_read_in_progress) { |
| + next_state_ = STATE_SHARED_NETWORK_READ_WAIT_COMPLETE; |
| + } |
| + return result; |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedNetworkReadComplete(int result) { |
| + if (result < 0) { |
| + cache_->NetworkReadFailedSharedWriters(this, entry_, result); |
| + |
| + // Set entry as null so that the destructor does not invoke DoneWithEntry |
| + // again as we have already processed the entry_ in |
| + // NetworkReadFailedSharedWriters |
| + entry_ = nullptr; |
| + return result; |
| + } |
| + |
| + read_offset_ += result; |
| + entry_->shared_writers->OnNetworkReadSuccess(this, read_buf_, result); |
| + |
| + next_state_ = STATE_CACHE_WRITE_DATA; |
| + return result; |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedNetworkReadWaitComplete(int result) { |
| + // If its a network read failure, we just return the result. |
| + // If its a cache write failure, we return the result which is filled as -1 |
| + // by SharedWriters, |
| + // If its a cache write success, read_buf_ would have been filled |
| + // with the read data by SharedWriters, so just return from here. |
| + |
| + if (result == 0) { // response is complete. |
| + DoneWritingToEntry(true, false); // changes the mode_ to NONE. |
| + } else if (result > 0) { |
| + read_offset_ += result; |
| + } else { |
| + // Set entry as null so that the destructor does not invoke DoneWithEntry |
| + // again as we have already processed the entry_ in |
| + // NetworkReadFailedSharedWriters |
| + entry_ = nullptr; |
| + } |
| + |
| + return result; |
| +} |
| + |
| int HttpCache::Transaction::DoCacheReadData() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadData"); |
| if (request_->method == "HEAD") |
| @@ -1852,7 +2066,14 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| if (result != write_len_) { |
| DLOG(ERROR) << "failed to write response data to cache"; |
| - DoneWritingToEntry(false); |
| + if (shared_) { |
| + // Fail any shared writers. |
| + network_trans_.reset(cache_->CacheWriteFailedSharedWriters(this, entry_)); |
| + DCHECK(network_trans_); |
| + DoneWritingToEntry(false, false); |
| + } else { |
| + DoneWritingToEntry(false); |
| + } |
| // We want to ignore errors writing to disk and just keep reading from |
| // the network. |
| @@ -1872,15 +2093,28 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| } |
| } |
| + bool complete = false; |
| if (result == 0) { |
| // End of file. This may be the result of a connection problem so see if we |
| // have to keep the entry around to be flagged as truncated later on. |
| if (done_reading_ || !entry_ || partial_ || |
| response_.headers->GetContentLength() <= 0) { |
| - DoneWritingToEntry(true); |
| + complete = true; |
| } |
| } |
| + bool perform_entry_cleanup = true; |
| + if (shared_) { |
| + if (result || (!result && complete)) { |
| + cache_->CacheWriteSuccessSharedWriters(this, entry_, result); |
| + // entry_ cleanup already performed, if any. |
| + perform_entry_cleanup = false; |
| + } |
| + } |
| + if (complete) { |
| + DoneWritingToEntry(true, perform_entry_cleanup); |
| + } |
| + |
| return result; |
| } |
| @@ -2089,8 +2323,14 @@ int HttpCache::Transaction::BeginCacheValidation() { |
| } |
| if (skip_validation) { |
| - UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED); |
| - return SetupEntryForRead(); |
| + if (shared_) { |
| + UpdateCacheEntryStatus( |
| + CacheEntryStatus::ENTRY_SKIP_VALIDATION_DO_SHARED_WRITING); |
| + entry_->shared_writers->OnValidationMatch(this, priority_); |
| + } else { |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED); |
| + return SetupEntryForRead(); |
| + } |
| } else { |
| // Make the network request conditional, to see if we may reuse our cached |
| // response. If we cannot do so, then we just resort to a normal fetch. |
| @@ -2241,6 +2481,49 @@ int HttpCache::Transaction::RestartNetworkRequestWithAuth( |
| return rv; |
| } |
| +void HttpCache::Transaction::Orphan(std::unique_ptr<HttpTransaction> trans) { |
| + DCHECK_EQ(trans.get(), this); |
| + orphaned_ = true; |
| + |
| + // Reset consumer's callback. |
| + callback_.Reset(); |
| + |
| + if (shared_) { |
| + if (next_state_ == STATE_SHARED_NETWORK_READ_COMPLETE || |
| + next_state_ == STATE_CACHE_WRITE_DATA_COMPLETE) { |
| + // Might lead to setting shared_ to false if this is the only transaction |
| + // in SharedWriters. |
| + cache_->DoomCurrentSharedWriter(std::move(trans), entry_); |
| + } else if (next_state_ == STATE_SHARED_NETWORK_READ_WAIT_COMPLETE) { |
| + entry_->shared_writers->RemoveWaitingWriter(this); |
| + } else if (next_state_ != STATE_NONE && |
| + next_state_ <= STATE_ADD_TO_ENTRY_COMPLETE) { |
| + cache_->RemovePendingTransaction(this); |
| + // Set cache_pending_ to false so that more cleanup is not attempted in |
| + // the destructor. |
| + cache_pending_ = false; |
| + } else if (validating_shared()) { |
| + cache_->RemoveValidatingTransSharedWriters(this, entry_); |
| + } else { |
| + bool cancel_request = reading_ && response_.headers.get(); |
| + if (cancel_request) { |
| + cancel_request &= (response_.headers->response_code() == 200); |
| + } |
| + cache_->RemoveIdleSharedWriter(this, entry_, cancel_request); |
| + } |
| + |
| + if (!shared_) { |
| + // Since any cleanup needed in entry_ and shared_writers is already done, |
| + // set entry_ to null. |
| + entry_ = nullptr; |
| + } |
| + } |
| + |
| + if (!shared_) { |
| + trans.reset(); |
| + } |
| +} |
| + |
| ValidationType HttpCache::Transaction::RequiresValidation() { |
| // TODO(darin): need to do more work here: |
| // - make sure we have a matching request method |
| @@ -2573,8 +2856,10 @@ int HttpCache::Transaction::WriteToEntry(int index, int offset, |
| int rv = 0; |
| if (!partial_ || !data_len) { |
| - rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, |
| - true); |
| + CompletionCallback io_callback = callback; |
| + |
| + rv = entry_->disk_entry->WriteData(index, offset, data, data_len, |
| + io_callback, true); |
| } else { |
| rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback); |
| } |
| @@ -2634,17 +2919,28 @@ int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { |
| return OK; |
| } |
| -void HttpCache::Transaction::DoneWritingToEntry(bool success) { |
| +void HttpCache::Transaction::DoneWritingToEntry(bool success, |
| + bool perform_entry_cleanup) { |
| if (!entry_) |
| return; |
| RecordHistograms(); |
| - cache_->DoneWritingToEntry(entry_, success); |
| - entry_ = NULL; |
| + if (perform_entry_cleanup) { |
| + cache_->DoneWritingToEntry(entry_, success); |
| + } |
| + entry_ = nullptr; |
| mode_ = NONE; // switch to 'pass through' mode |
| } |
| +void HttpCache::Transaction::ContinueWithoutSharedWriting() { |
| + // Should own a network transaction to continue. |
| + DCHECK(network_trans_); |
| + shared_ = false; |
| + entry_ = nullptr; |
| + mode_ = NONE; |
| +} |
| + |
| int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| DLOG(ERROR) << "ReadData failed: " << result; |
| const int result_for_histogram = std::max(0, -result); |
| @@ -2663,7 +2959,11 @@ int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| if (restart) { |
| DCHECK(!reading_); |
| DCHECK(!network_trans_.get()); |
| - cache_->DoneWithEntry(entry_, this, false); |
| + if (shared_) { |
| + cache_->RemoveValidatingTransSharedWriters(this, entry_); |
| + } else { |
| + cache_->DoneWithEntry(entry_, this, false); |
| + } |
| entry_ = NULL; |
| is_sparse_ = false; |
| partial_.reset(); |
| @@ -2688,6 +2988,8 @@ void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { |
| } |
| void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { |
| + // Partial requests not supported with shared writing as of now. |
| + DCHECK(!shared_); |
| DVLOG(2) << "DoomPartialEntry"; |
| int rv = cache_->DoomEntry(cache_key_, NULL); |
| DCHECK_EQ(OK, rv); |
| @@ -2748,7 +3050,7 @@ void HttpCache::Transaction::ResetPartialState(bool delete_object) { |
| } |
| } |
| -void HttpCache::Transaction::ResetNetworkTransaction() { |
| +void HttpCache::Transaction::SaveNetworkTransInfo() { |
| DCHECK(!old_network_trans_load_timing_); |
| DCHECK(network_trans_); |
| LoadTimingInfo load_timing; |
| @@ -2762,6 +3064,37 @@ void HttpCache::Transaction::ResetNetworkTransaction() { |
| old_connection_attempts_.push_back(attempt); |
| old_remote_endpoint_ = IPEndPoint(); |
| network_trans_->GetRemoteEndpoint(&old_remote_endpoint_); |
| +} |
| + |
| +void HttpCache::Transaction::SaveSharedNetworkTransInfo() { |
| + // If network_trans_ is still present, this transaction has not started using |
| + // the "shared" network transaction, so do nothing. |
| + if (network_trans_) |
| + return; |
| + DCHECK(!old_network_trans_load_timing_); |
| + |
| + // If the transaction is being deleted while still in the waiting queue, |
| + // entry_ is not yet set, do nothing. |
| + if (!entry_) |
| + return; |
| + DCHECK(entry_->shared_writers); |
| + LoadTimingInfo load_timing; |
| + if (entry_->shared_writers->GetLoadTimingInfo(&load_timing)) |
| + old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing)); |
| + total_received_bytes_ += entry_->shared_writers->GetTotalReceivedBytes(); |
| + total_sent_bytes_ += entry_->shared_writers->GetTotalSentBytes(); |
| + ConnectionAttempts attempts; |
| + entry_->shared_writers->GetConnectionAttempts(&attempts); |
| + for (const auto& attempt : attempts) |
| + old_connection_attempts_.push_back(attempt); |
| + old_remote_endpoint_ = IPEndPoint(); |
| + entry_->shared_writers->GetRemoteEndpoint(&old_remote_endpoint_); |
| + have_full_request_headers_ = |
| + entry_->shared_writers->GetFullRequestHeaders(&full_request_headers_); |
| +} |
| + |
| +void HttpCache::Transaction::ResetNetworkTransaction() { |
| + SaveNetworkTransInfo(); |
| network_trans_.reset(); |
| } |
| @@ -2929,19 +3262,35 @@ void HttpCache::Transaction::RecordHistograms() { |
| UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time); |
| + if (cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_VALIDATED_DO_SHARED_WRITING) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.ValidationMatchSharedWriting", |
| + total_time); |
| + } |
| + |
| bool did_send_request = !send_request_since_.is_null(); |
| - DCHECK( |
| - (did_send_request && |
| - (cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE || |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED || |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED || |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE)) || |
| - (!did_send_request && |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_USED)); |
| + DCHECK((did_send_request && |
| + (cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE || |
| + cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_VALIDATED_DO_SHARED_WRITING || |
| + cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_VALIDATED_DOOM_SHARED_WRITING)) || |
| + (!did_send_request && |
| + (cache_entry_status_ == CacheEntryStatus::ENTRY_USED || |
| + cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_SKIP_VALIDATION_DO_SHARED_WRITING))); |
| if (!did_send_request) { |
| - DCHECK(cache_entry_status_ == CacheEntryStatus::ENTRY_USED); |
| - UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); |
| + if (cache_entry_status_ == CacheEntryStatus::ENTRY_USED) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); |
| + } else if (cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_SKIP_VALIDATION_DO_SHARED_WRITING) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SkipValidationSharedWriting", |
| + total_time); |
| + } |
| return; |
| } |
| @@ -2986,13 +3335,58 @@ void HttpCache::Transaction::RecordHistograms() { |
| before_send_sample); |
| break; |
| } |
| + case CacheEntryStatus::ENTRY_VALIDATED_DO_SHARED_WRITING: { |
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.ValidationMatchSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "HttpCache.PercentBeforeSend.ValidationMatchSharedWriting", |
| + before_send_sample); |
| + break; |
| + } |
| + case CacheEntryStatus::ENTRY_VALIDATED_DOOM_SHARED_WRITING: { |
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.ValidationDoomSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "HttpCache.PercentBeforeSend.ValidationDoomSharedWriting", |
| + before_send_sample); |
| + break; |
| + } |
| default: |
| NOTREACHED(); |
| } |
| } |
| +bool HttpCache::Transaction::IsEligibleForSharedWriting() const { |
| + if (!(mode_ & WRITE)) |
| + return false; |
| + |
| + DCHECK(request_); |
| + if (request_->method != "GET") |
| + return false; |
| + |
| + if (partial_ || range_requested_) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +void HttpCache::Transaction::SetSharedWritingFailState() { |
| + // This state should only be set when the transaction is idle waiting for Read |
| + // to be invoked. |
| + DCHECK_EQ(next_state_, STATE_NONE); |
| + next_state_ = STATE_SHARED_READ_WRITE_FAILED; |
| + entry_ = nullptr; |
| +} |
| + |
| void HttpCache::Transaction::OnIOComplete(int result) { |
| + // Setting this in a local variable because if the consumer still exists, its |
| + // possible for this object to get destroyed during the consumer's callback |
| + // processing. |
| + bool orphaned = orphaned_; |
| DoLoop(result); |
| + if (orphaned && finalize_doomed_) { |
|
jkarlin
2016/12/06 21:06:28
finalized_doomed_ should cause a crash if this obj
shivanisha
2016/12/06 21:53:35
The underlying assumption here is that since orpha
jkarlin
2016/12/07 01:09:04
Makes sense, thanks.
|
| + cache_->FinalizeDoomedSharedWriter(entry_); |
|
jkarlin
2016/12/06 21:06:28
Also entry_
shivanisha
2016/12/06 21:53:35
Same as above.
shivanisha
2016/12/07 00:09:02
For clarity, I will add a comment for this assumpt
|
| + } |
| } |
| } // namespace net |