Chromium Code Reviews| Index: net/http/http_cache_writers.cc | 
| diff --git a/net/http/http_cache_writers.cc b/net/http/http_cache_writers.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..84698cd97c780fedee5b45d4e238800e0461740d | 
| --- /dev/null | 
| +++ b/net/http/http_cache_writers.cc | 
| @@ -0,0 +1,418 @@ | 
| +// Copyright (c) 2017 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "net/http/http_cache_writers.h" | 
| + | 
| +#include <algorithm> | 
| +#include <utility> | 
| +#include "net/disk_cache/disk_cache.h" | 
| +#include "net/http/http_cache_transaction.h" | 
| + | 
| +namespace net { | 
| + | 
| +HttpCache::Writers::Writers(HttpCache* cache, ActiveEntry* entry) | 
| + : cache_(cache), entry_(entry), weak_factory_(this) { | 
| + io_callback_ = | 
| + base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr()); | 
| +} | 
| + | 
| +HttpCache::Writers::~Writers() {} | 
| + | 
| +int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf, | 
| + int buf_len, | 
| + const CompletionCallback& callback, | 
| + Transaction* transaction) { | 
| + DCHECK(buf); | 
| + DCHECK_GT(buf_len, 0); | 
| + DCHECK(!callback.is_null()); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
nit, suggestion: DCHECK(transaction)
 
shivanisha
2017/05/18 20:59:44
done
 
 | 
| + | 
| + // If another transaction is already reading from the network, then this | 
| + // transaction waits for the read to complete and gets its buffer filled | 
| + // with the data returned from that read. | 
| + if (active_transaction_) { | 
| + WaitingForRead waiting_transaction(transaction, buf, buf_len, callback); | 
| + waiting_for_read_.push_back(waiting_transaction); | 
| + return ERR_IO_PENDING; | 
| + } | 
| + | 
| + DCHECK_EQ(next_state_, State::NONE); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
This (and the test above) seem wrong to me--if an
 
shivanisha
2017/05/18 20:59:44
Ah, that's right. Changed the condition to next_st
 
Randy Smith (Not in Mondays)
2017/05/22 01:56:55
I'm not seeing the change (looking at PS6->8 diff)
 
shivanisha
2017/05/31 19:21:26
It is present in PS8.
 
Randy Smith (Not in Mondays)
2017/06/06 12:54:57
Ah, sorry, was looking at the DCHECK.  Sorry.
I d
 
shivanisha
2017/06/07 15:19:18
Removed the dcheck.
 
 | 
| + DCHECK(callback_.is_null()); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
This feels redundant with the below same DCHECK; d
 
shivanisha
2017/05/18 20:59:44
removed from below
 
 | 
| + active_transaction_ = transaction; | 
| + | 
| + read_buf_ = std::move(buf); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
Why std::move on a scoped_refptr<>?  This is just
 
shivanisha
2017/05/18 20:59:44
Its just taking the buffer away from the argument
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:19
Acknowledged.
 
 | 
| + io_buf_len_ = buf_len; | 
| + next_state_ = State::NETWORK_READ; | 
| + | 
| + int rv = DoLoop(OK); | 
| + if (rv == ERR_IO_PENDING) { | 
| + DCHECK(callback_.is_null()); | 
| + callback_ = callback; | 
| + } | 
| + | 
| + return rv; | 
| +} | 
| + | 
| +bool HttpCache::Writers::StopCaching(Transaction* transaction) { | 
| + // If this is the only transaction in Writers, then stopping will be | 
| + // successful. If not, then we will not stop caching since there are | 
| + // other consumers waiting to read from the cache. | 
| + if (all_writers_.size() == 1) { | 
| + DCHECK(all_writers_.count(transaction)); | 
| + network_read_only_ = true; | 
| + return true; | 
| + } | 
| + return false; | 
| +} | 
| + | 
| +void HttpCache::Writers::AddTransaction( | 
| + Transaction* transaction, | 
| + std::unique_ptr<HttpTransaction> network_transaction) { | 
| + DCHECK(transaction); | 
| + DCHECK(CanAddWriters()); | 
| + | 
| + auto return_val = all_writers_.insert(transaction); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
nit, suggestion: I don't find the type of return_v
 
shivanisha
2017/05/18 20:59:44
done
 
 | 
| + DCHECK_EQ(return_val.second, true); | 
| + | 
| + if (network_transaction) | 
| + network_transaction_ = std::move(network_transaction); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
What drove adding the network transaction here rat
 
shivanisha
2017/05/18 20:59:44
I am hoping writers can be a member variable of Ac
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Hmmm.  Ok.  There's a range of design choices here
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Worthwhile adding a DCHECK that network_transactio
 
shivanisha
2017/05/31 19:21:26
Done
 
shivanisha
2017/05/31 19:21:26
Added the dcheck
 
 | 
| + DCHECK(network_transaction_); | 
| + | 
| + priority_ = std::max(transaction->priority(), priority_); | 
| + network_transaction_->SetPriority(priority_); | 
| +} | 
| + | 
| +void HttpCache::Writers::RemoveTransaction(Transaction* transaction) { | 
| + if (!transaction) | 
| + return; | 
| + | 
| + // The transaction should be part of all_writers. | 
| + auto it = all_writers_.find(transaction); | 
| + DCHECK(it != all_writers_.end()); | 
| + all_writers_.erase(transaction); | 
| + PriorityChanged(); | 
| + | 
| + if (active_transaction_ == transaction) { | 
| + active_transaction_ = nullptr; | 
| + callback_.Reset(); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
Is this really all that needs to happen if we're n
 
shivanisha
2017/05/18 20:59:44
Updated Read to see that if next_state_ is not NON
 
 | 
| + return; | 
| + } | 
| + | 
| + auto waiting_it = waiting_for_read_.begin(); | 
| + for (; waiting_it != waiting_for_read_.end(); waiting_it++) { | 
| + if (transaction == waiting_it->transaction) { | 
| + waiting_for_read_.erase(waiting_it); | 
| + // If a waiting transaction existed, there should have been an | 
| + // active_transaction_. | 
| + DCHECK(active_transaction_); | 
| + return; | 
| + } | 
| + } | 
| +} | 
| + | 
| +void HttpCache::Writers::RemoveAllTransactions() { | 
| + all_writers_.clear(); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
No need to nuke waiting_for_read_ or do anything w
 
shivanisha
2017/05/18 20:59:44
On second thoughts, I actually do not see a need f
 
 | 
| +} | 
| + | 
| +void HttpCache::Writers::PriorityChanged() { | 
| + RequestPriority current_highest = GetCurrentHighestPriority(); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
nit, suggestion: My personal bias is that, if ther
 
shivanisha
2017/05/18 20:59:44
sgtm, done
 
 | 
| + if (priority_ != current_highest) { | 
| + network_transaction_->SetPriority(current_highest); | 
| + priority_ = current_highest; | 
| + } | 
| +} | 
| + | 
| +void HttpCache::Writers::MoveIdleWritersToReaders() { | 
| + // Should be invoked after |waiting_for_read_| transactions and | 
| + // |active_transaction_| are processed so that all_writers_ only contains idle | 
| + // writers. | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
This reads to me as a requirement on the caller, a
 
shivanisha
2017/05/18 20:59:44
done in the comment and also added a function Cont
 
 | 
| + DCHECK(waiting_for_read_.empty()); | 
| + DCHECK(!active_transaction_); | 
| + for (auto* idle_writer : all_writers_) { | 
| + entry_->readers.insert(idle_writer); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Hmm.  I think this is dependent on the cache entry
 
shivanisha
2017/05/18 20:59:44
In CL https://codereview.chromium.org/2721933002/
 
Randy Smith (Not in Mondays)
2017/05/22 01:56:55
SGTM.
 
 | 
| + } | 
| + all_writers_.clear(); | 
| +} | 
| + | 
| +bool HttpCache::Writers::CanAddWriters() const { | 
| + if (all_writers_.empty()) { | 
| + DCHECK(!network_read_only_); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Why?
 
shivanisha
2017/05/18 20:59:43
I believe this should be changed to network_read_o
 
Randy Smith (Not in Mondays)
2017/05/22 01:56:55
I'd be more inclined to say that network_read_only
 
shivanisha
2017/05/31 19:21:26
Added a function ResetStateForEmptyWriters which i
 
 | 
| + return true; | 
| + } | 
| + | 
| + return !is_exclusive_ && !network_read_only_; | 
| +} | 
| + | 
| +void HttpCache::Writers::ProcessFailure(Transaction* transaction, int error) { | 
| + DCHECK(!transaction || transaction == active_transaction_); | 
| + | 
| + // Notify waiting_for_read_ of the failure. Tasks will be posted for all the | 
| + // transactions. | 
| + ProcessWaitingForReadTransactions(error); | 
| + | 
| + // Idle readers should know to fail when Read is invoked by their consumers. | 
| + SetIdleWritersFailState(error); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Why have this logic in the consumers (HC::Ts) inst
 
shivanisha
2017/05/18 20:59:44
Its possible that entry is destroyed and then only
 
shivanisha
2017/05/19 13:49:57
Or we can doom this entry but let these transactio
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Yeah, that was the direction I think I was gesturi
 
shivanisha
2017/05/31 19:21:26
If there is a failure then waiting_for_read_ trans
 
Randy Smith (Not in Mondays)
2017/06/06 12:54:57
Makes sense.  Ok, I'm slightly in favor at the arc
 
shivanisha
2017/06/07 15:19:18
Changed the logic to keep this state in Writers it
 
shivanisha
2017/06/08 13:38:41
Actually while working on the follow up CL, a use
 
Randy Smith (Not in Mondays)
2017/06/08 17:10:00
Accepted if that's how you decide that's the best
 
shivanisha
2017/06/08 17:55:56
We actually want future reads on the active_transa
 
Randy Smith (Not in Mondays)
2017/06/13 17:45:05
Ah, makes sense.  Drat.
 
 | 
| + DCHECK(all_writers_.empty()); | 
| +} | 
| + | 
| +void HttpCache::Writers::TruncateEntry() { | 
| + // TODO(shivanisha) On integration, see if the entry really needs to be | 
| + // truncated on the lines of Transaction::AddTruncatedFlag and then proceed. | 
| + DCHECK_EQ(next_state_, State::NONE); | 
| + next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE; | 
| + DoLoop(OK); | 
| +} | 
| + | 
| +LoadState HttpCache::Writers::GetWriterLoadState() { | 
| + return network_transaction_->GetLoadState(); | 
| +} | 
| + | 
| +HttpCache::Writers::WaitingForRead::WaitingForRead( | 
| + Transaction* cache_transaction, | 
| + scoped_refptr<IOBuffer> buf, | 
| + int len, | 
| + const CompletionCallback& consumer_callback) | 
| + : transaction(cache_transaction), | 
| + read_buf(std::move(buf)), | 
| + read_buf_len(len), | 
| + write_len(0), | 
| + callback(consumer_callback) { | 
| + DCHECK(cache_transaction); | 
| + DCHECK(buf); | 
| + DCHECK_GT(len, 0); | 
| + DCHECK(!consumer_callback.is_null()); | 
| +} | 
| + | 
| +HttpCache::Writers::WaitingForRead::~WaitingForRead() {} | 
| +HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) = | 
| + default; | 
| + | 
| +int HttpCache::Writers::DoLoop(int result) { | 
| + DCHECK(next_state_ != State::NONE); | 
| + int rv = result; | 
| + do { | 
| + State state = next_state_; | 
| + next_state_ = State::NONE; | 
| + switch (state) { | 
| + case State::NETWORK_READ: | 
| + DCHECK_EQ(OK, rv); | 
| + rv = DoNetworkRead(); | 
| + break; | 
| + case State::NETWORK_READ_COMPLETE: | 
| + rv = DoNetworkReadComplete(rv); | 
| + break; | 
| + case State::CACHE_WRITE_DATA: | 
| + rv = DoCacheWriteData(rv); | 
| + break; | 
| + case State::CACHE_WRITE_DATA_COMPLETE: | 
| + rv = DoCacheWriteDataComplete(rv); | 
| + break; | 
| + case State::CACHE_WRITE_TRUNCATED_RESPONSE: | 
| + rv = DoCacheWriteTruncatedResponse(); | 
| + break; | 
| + case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: | 
| + rv = DoCacheWriteTruncatedResponseComplete(rv); | 
| + break; | 
| + default: | 
| + NOTREACHED() << "bad state"; | 
| + rv = ERR_FAILED; | 
| + break; | 
| + } | 
| + } while (next_state_ != State::NONE && rv != ERR_IO_PENDING); | 
| + | 
| + if (rv != ERR_IO_PENDING && !callback_.is_null()) { | 
| + read_buf_ = NULL; // Release the buffer before invoking the callback. | 
| + base::ResetAndReturn(&callback_).Run(rv); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
nit, suggestion: The pattern I've seen more common
 
shivanisha
2017/05/18 20:59:44
I guess I am only much aware of the way it is hand
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Acknowledged.
 
 | 
| + } | 
| + return rv; | 
| +} | 
| + | 
| +int HttpCache::Writers::DoNetworkRead() { | 
| + next_state_ = State::NETWORK_READ_COMPLETE; | 
| + return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback_); | 
| +} | 
| + | 
| +int HttpCache::Writers::DoNetworkReadComplete(int result) { | 
| + if (result < 0) { | 
| + OnNetworkReadFailure(result); | 
| + return result; | 
| + } | 
| + | 
| + next_state_ = State::CACHE_WRITE_DATA; | 
| + return result; | 
| +} | 
| + | 
| +void HttpCache::Writers::OnNetworkReadFailure(int result) { | 
| + // At this point active_transaction_ may or may not be alive. | 
| + // If no consumer, invoke entry processing here itself. | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
suggestion, possibly not for this CL: I'd rather h
 
shivanisha
2017/05/18 20:59:43
I will look at this suggestion again in the integr
 
shivanisha
2017/05/19 13:49:57
Actually on second thoughts, its simpler to invoke
 
 | 
| + if (!active_transaction_) | 
| + cache_->DoneWithEntry(entry_, nullptr, true); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Is there some path from here to ProcessFailure?  H
 
shivanisha
2017/05/18 20:59:44
DoneWithEntry => DoneWritingToEntry => ProcessFail
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Acknowledged.
 
 | 
| +} | 
| + | 
| +int HttpCache::Writers::DoCacheWriteData(int num_bytes) { | 
| + next_state_ = State::CACHE_WRITE_DATA_COMPLETE; | 
| + write_len_ = num_bytes; | 
| + if (!num_bytes || network_read_only_) | 
| + return num_bytes; | 
| + | 
| + int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | 
| + return WriteToEntry(kResponseContentIndex, current_size, read_buf_.get(), | 
| + num_bytes, io_callback_); | 
| +} | 
| + | 
| +int HttpCache::Writers::WriteToEntry(int index, | 
| + int offset, | 
| + IOBuffer* data, | 
| + int data_len, | 
| + const CompletionCallback& callback) { | 
| + int rv = 0; | 
| + | 
| + PartialData* partial = nullptr; | 
| + // Transaction must be alive if this is a partial request. | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
This isn't backed up with a DCHECK, and I'm not su
 
shivanisha
2017/05/18 20:59:43
In case of partial requests, SetExclusive will be
 
Randy Smith (Not in Mondays)
2017/05/24 23:09:18
Thanks for the explanation.  
(nit, suggestion:) I
 
shivanisha
2017/05/31 19:21:26
done
 
 | 
| + // Todo(shivanisha): When partial requests support parallel writing, this | 
| + // assumption will not be true. | 
| + if (active_transaction_) | 
| + partial = active_transaction_->partial(); | 
| + | 
| + if (!partial || !data_len) { | 
| + rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, | 
| + true); | 
| + } else { | 
| + rv = partial->CacheWrite(entry_->disk_entry, data, data_len, callback); | 
| + } | 
| + return rv; | 
| +} | 
| + | 
| +int HttpCache::Writers::DoCacheWriteDataComplete(int result) { | 
| + if (result != write_len_) { | 
| + OnCacheWriteFailure(); | 
| + | 
| + // |active_transaction_| can continue reading from the network. | 
| + result = write_len_; | 
| + } else { | 
| + OnDataReceived(result); | 
| + } | 
| + return result; | 
| +} | 
| + | 
| +int HttpCache::Writers::DoCacheWriteTruncatedResponse() { | 
| + next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE; | 
| + return WriteResponseInfo(true); | 
| +} | 
| + | 
| +int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) { | 
| + if (result != io_buf_len_) { | 
| + DLOG(ERROR) << "failed to write response info to cache"; | 
| + } | 
| + truncated_ = true; | 
| + return OK; | 
| +} | 
| + | 
| +void HttpCache::Writers::OnDataReceived(int result) { | 
| + if (result == 0 && !active_transaction_) { | 
| + // Check if the response is actually completed or if not, attempt to mark | 
| + // the entry as truncated. | 
| + if (IsResponseCompleted()) { | 
| + ProcessWaitingForReadTransactions(result); | 
| + cache_->DoneWritingToEntry(entry_, true); | 
| + } else { | 
| + OnNetworkReadFailure(result); | 
| + return; | 
| + } | 
| + } | 
| + | 
| + // Save the data in all the waiting transactions' read buffers. | 
| + for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); | 
| + it++) { | 
| + it->write_len = std::min(it->read_buf_len, result); | 
| + memcpy(it->read_buf->data(), read_buf_->data(), it->write_len); | 
| + } | 
| + | 
| + // Notify waiting_for_read_. Tasks will be posted for all the | 
| + // transactions. | 
| + ProcessWaitingForReadTransactions(write_len_); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
nit, suggestion: The grouping would feel slightly
 
shivanisha
2017/05/19 13:49:57
done
 
 | 
| + | 
| + if (result > 0) { // not the end of response | 
| + active_transaction_ = nullptr; | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
Why keep the active transaction set if the end of
 
shivanisha
2017/05/19 13:49:57
Now that DoneWithEntry/DoneWritingToEntry will be
 
 | 
| + return; | 
| + } | 
| + DCHECK_EQ(result, 0); | 
| + if (!active_transaction_) | 
| + cache_->DoneWritingToEntry(entry_, true); | 
| +} | 
| + | 
| +void HttpCache::Writers::OnCacheWriteFailure() { | 
| + network_read_only_ = true; | 
| + | 
| + // Call the cache_ function here even if active_transaction_ is alive because | 
| + // it wouldn't know if this was an error case, since it gets a positive result | 
| + // back. This will in turn ProcessFailure. | 
| + // TODO(shivanisha) : Also pass active_transaction_ and | 
| + // ERR_CACHE_WRITE_FAILURE to DoneWritingToEntry on integration so that it can | 
| + // be passed back in ProcessFailure. | 
| + cache_->DoneWritingToEntry(entry_, false); | 
| +} | 
| + | 
| +bool HttpCache::Writers::IsResponseCompleted() { | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
I'm a bit uncomfortable with this function's name;
 
shivanisha
2017/05/19 13:49:57
Moved it to its call site.
 
 | 
| + int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | 
| + const HttpResponseInfo* response_info = | 
| + network_transaction_->GetResponseInfo(); | 
| + int64_t content_length = response_info->headers->GetContentLength(); | 
| + if ((content_length >= 0 && content_length <= current_size) || | 
| + content_length < 0) | 
| + return true; | 
| + return false; | 
| +} | 
| + | 
| +int HttpCache::Writers::WriteResponseInfo(bool truncated) { | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Again, I'd vote in favor of hoisting this into the
 
shivanisha
2017/05/19 13:49:57
Moved to calling site. I had this as separate func
 
 | 
| + // When writing headers, we normally only write the non-transient headers. | 
| + const HttpResponseInfo* response = network_transaction_->GetResponseInfo(); | 
| + bool skip_transient_headers = true; | 
| + scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); | 
| + response->Persist(data->pickle(), skip_transient_headers, truncated); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
nit, suggestion: I'd just write this as 
response
 
shivanisha
2017/05/19 13:49:57
done
 
 | 
| + data->Done(); | 
| + io_buf_len_ = data->pickle()->size(); | 
| + return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), | 
| + io_buf_len_, io_callback_, truncated); | 
| +} | 
| + | 
| +void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) { | 
| + for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); | 
| + it++) { | 
| + Transaction* transaction = it->transaction; | 
| + if (result > 0) { // success | 
| + result = it->write_len; | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:55
(Just an addition to an earlier comment: Setting r
 
shivanisha
2017/05/19 13:49:57
Moved copying to this function.
 
 | 
| + } else { | 
| + // If its response completion or failure, this transaction needs to be | 
| + // removed. | 
| + all_writers_.erase(transaction); | 
| 
 
Randy Smith (Not in Mondays)
2017/05/18 01:02:54
Caption: This comment is an extended babble with m
 
shivanisha
2017/05/19 13:49:58
In case transaction B comes back , reads from netw
 
shivanisha
2017/05/19 17:12:58
Actually, keeping all_writers_ is important from a
 
Randy Smith (Not in Mondays)
2017/05/22 01:56:55
Acknowledged.
 
 | 
| + } | 
| + // Post task to notify transaction. | 
| + base::ThreadTaskRunnerHandle::Get()->PostTask( | 
| + FROM_HERE, base::Bind(it->callback, result)); | 
| + } | 
| + waiting_for_read_.clear(); | 
| +} | 
| + | 
| +void HttpCache::Writers::SetIdleWritersFailState(int result) { | 
| + // Since this is only for idle transactions, all waiting_for_read_ and | 
| + // active_transaction_ should be empty. | 
| + DCHECK(waiting_for_read_.empty()); | 
| + DCHECK(!active_transaction_); | 
| + for (auto* transaction : all_writers_) | 
| + transaction->SetSharedWritingFailState(result); | 
| + all_writers_.clear(); | 
| +} | 
| + | 
| +RequestPriority HttpCache::Writers::GetCurrentHighestPriority() { | 
| + RequestPriority priority = MINIMUM_PRIORITY; | 
| + for (auto* transaction : all_writers_) | 
| + priority = std::max(transaction->priority(), priority); | 
| + return priority; | 
| +} | 
| + | 
| +void HttpCache::Writers::OnIOComplete(int result) { | 
| + DoLoop(result); | 
| +} | 
| + | 
| +} // namespace net |