Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: net/http/http_cache_writers.cc

Issue 2886483002: Adds a new class HttpCache::Writers for multiple cache transactions reading from the network. (Closed)
Patch Set: Rebased till refs/heads/master@{#475981} Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/http_cache_writers.h"
6
7 #include <algorithm>
8 #include <utility>
9 #include "net/disk_cache/disk_cache.h"
10 #include "net/http/http_cache_transaction.h"
11
12 namespace net {
13
14 HttpCache::Writers::Writers(HttpCache* cache, ActiveEntry* entry)
15 : cache_(cache), entry_(entry), weak_factory_(this) {
16 io_callback_ =
17 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
18 }
19
20 HttpCache::Writers::~Writers() {}
21
22 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf,
23 int buf_len,
24 const CompletionCallback& callback,
25 Transaction* transaction) {
26 DCHECK(buf);
27 DCHECK_GT(buf_len, 0);
28 DCHECK(!callback.is_null());
29 DCHECK(transaction);
30
31 // If another transaction invoked a Read which is currently ongoing , then
32 // this transaction waits for the read to complete and gets its buffer filled
33 // with the data returned from that read.
34 if (next_state_ != State::NONE) {
35 WaitingForRead waiting_transaction(transaction, buf, buf_len, callback);
36 waiting_for_read_.push_back(waiting_transaction);
37 return ERR_IO_PENDING;
38 }
39
40 DCHECK_EQ(next_state_, State::NONE);
41 DCHECK(callback_.is_null());
42 active_transaction_ = transaction;
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 DCHECK_EQ(nullptr, active_transaction_.get())? (S
shivanisha 2017/06/07 15:19:19 done
43
44 read_buf_ = std::move(buf);
45 io_buf_len_ = buf_len;
46 next_state_ = State::NETWORK_READ;
47
48 int rv = DoLoop(OK);
49 if (rv == ERR_IO_PENDING) {
50 callback_ = callback;
51 }
52
53 return rv;
54 }
55
56 bool HttpCache::Writers::StopCaching(Transaction* transaction) {
57 // If this is the only transaction in Writers, then stopping will be
58 // successful. If not, then we will not stop caching since there are
59 // other consumers waiting to read from the cache.
60 if (all_writers_.size() == 1) {
61 DCHECK(all_writers_.count(transaction));
62 network_read_only_ = true;
63 return true;
64 }
65 return false;
66 }
67
68 void HttpCache::Writers::AddTransaction(
69 Transaction* transaction,
70 std::unique_ptr<HttpTransaction> network_transaction) {
71 DCHECK(transaction);
72 DCHECK(CanAddWriters());
73
74 std::pair<TransactionSet::iterator, bool> return_val =
75 all_writers_.insert(transaction);
76 DCHECK_EQ(return_val.second, true);
77
78 if (network_transaction) {
79 DCHECK(!network_transaction_);
80 network_transaction_ = std::move(network_transaction);
81 }
82
83 DCHECK(network_transaction_);
84
85 priority_ = std::max(transaction->priority(), priority_);
86 network_transaction_->SetPriority(priority_);
87 }
88
89 void HttpCache::Writers::SetExclusive() {
90 DCHECK(all_writers_.size() <= 1);
91 is_exclusive_ = true;
92 }
93
94 void HttpCache::Writers::RemoveTransaction(Transaction* transaction) {
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 Suggestion: DCHECK that the transaction was found
shivanisha 2017/06/07 15:19:18 DCHECK(it != all_writers_.end()); below takes care
95 if (!transaction)
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 What's the use case for calling RemoveTransaction(
shivanisha 2017/06/07 15:19:18 Let's say active_transaction_ is null as it has be
Randy Smith (Not in Mondays) 2017/06/08 17:10:00 I guess I'll put this off until the integration CL
shivanisha 2017/06/08 17:55:56 DoneWritingToEntry() takes the transaction in the
96 return;
97
98 // The transaction should be part of all_writers.
99 auto it = all_writers_.find(transaction);
100 DCHECK(it != all_writers_.end());
101 all_writers_.erase(transaction);
102 if (all_writers_.empty())
103 ResetStateForEmptyWriters();
104 PriorityChanged();
105
106 if (active_transaction_ == transaction) {
107 active_transaction_ = nullptr;
108 callback_.Reset();
109 return;
110 }
111
112 auto waiting_it = waiting_for_read_.begin();
113 for (; waiting_it != waiting_for_read_.end(); waiting_it++) {
114 if (transaction == waiting_it->transaction) {
115 waiting_for_read_.erase(waiting_it);
116 // If a waiting transaction existed, there should have been an
117 // active_transaction_.
118 DCHECK(active_transaction_);
119 return;
120 }
121 }
122 }
123
124 void HttpCache::Writers::PriorityChanged() {
125 // Get the current highest priority.
126 RequestPriority current_highest = MINIMUM_PRIORITY;
127 for (auto* transaction : all_writers_)
128 current_highest = std::max(transaction->priority(), current_highest);
129
130 if (priority_ != current_highest) {
131 network_transaction_->SetPriority(current_highest);
132 priority_ = current_highest;
133 }
134 }
135
136 bool HttpCache::Writers::ContainsOnlyIdleWriters() const {
137 return waiting_for_read_.empty() && !active_transaction_;
138 }
139
140 void HttpCache::Writers::MoveIdleWritersToReaders() {
141 // Should be invoked after |waiting_for_read_| transactions and
142 // |active_transaction_| are processed so that all_writers_ only contains idle
143 // writers.
144 DCHECK(ContainsOnlyIdleWriters());
145 for (auto* idle_writer : all_writers_) {
146 entry_->readers.insert(idle_writer);
147 }
148 all_writers_.clear();
149 ResetStateForEmptyWriters();
150 }
151
152 bool HttpCache::Writers::CanAddWriters() {
153 if (all_writers_.empty())
154 return true;
155
156 return !is_exclusive_ && !network_read_only_;
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 What's the use case for adding writers when |all_w
shivanisha 2017/06/07 15:19:18 That shouldn't happen since network_read_only_ is
157 }
158
159 void HttpCache::Writers::ProcessFailure(Transaction* transaction, int error) {
160 DCHECK(!transaction || transaction == active_transaction_);
161
162 // Notify waiting_for_read_ of the failure. Tasks will be posted for all the
163 // transactions.
164 ProcessWaitingForReadTransactions(error);
165
166 // Idle readers should know to fail when Read is invoked by their consumers.
167 SetIdleWritersFailState(error);
168 DCHECK(all_writers_.empty());
169 ResetStateForEmptyWriters();
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 Shouldn't this already have happened if all_writer
shivanisha 2017/06/07 15:19:18 This basically resets some member variables and sh
Randy Smith (Not in Mondays) 2017/06/08 17:10:00 This makes the interface contract for ProcessWaiti
shivanisha 2017/06/08 17:55:56 ResetStateForEmptyWriters() is not really related
Randy Smith (Not in Mondays) 2017/06/13 17:45:05 TL;DR: I want to make on more try at explaining my
shivanisha 2017/06/14 02:33:16 I understand the concern here that it's not obviou
170 }
171
172 void HttpCache::Writers::TruncateEntry() {
173 // TODO(shivanisha) On integration, see if the entry really needs to be
174 // truncated on the lines of Transaction::AddTruncatedFlag and then proceed.
175 DCHECK_EQ(next_state_, State::NONE);
176 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE;
177 DoLoop(OK);
178 }
179
180 LoadState HttpCache::Writers::GetWriterLoadState() {
181 return network_transaction_->GetLoadState();
182 }
183
184 HttpCache::Writers::WaitingForRead::WaitingForRead(
185 Transaction* cache_transaction,
186 scoped_refptr<IOBuffer> buf,
187 int len,
188 const CompletionCallback& consumer_callback)
189 : transaction(cache_transaction),
190 read_buf(std::move(buf)),
191 read_buf_len(len),
192 write_len(0),
193 callback(consumer_callback) {
194 DCHECK(cache_transaction);
195 DCHECK(buf);
196 DCHECK_GT(len, 0);
197 DCHECK(!consumer_callback.is_null());
198 }
199
200 HttpCache::Writers::WaitingForRead::~WaitingForRead() {}
201 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) =
202 default;
203
204 int HttpCache::Writers::DoLoop(int result) {
205 DCHECK(next_state_ != State::NONE);
206 int rv = result;
207 do {
208 State state = next_state_;
209 next_state_ = State::NONE;
210 switch (state) {
211 case State::NETWORK_READ:
212 DCHECK_EQ(OK, rv);
213 rv = DoNetworkRead();
214 break;
215 case State::NETWORK_READ_COMPLETE:
216 rv = DoNetworkReadComplete(rv);
217 break;
218 case State::CACHE_WRITE_DATA:
219 rv = DoCacheWriteData(rv);
220 break;
221 case State::CACHE_WRITE_DATA_COMPLETE:
222 rv = DoCacheWriteDataComplete(rv);
223 break;
224 case State::CACHE_WRITE_TRUNCATED_RESPONSE:
225 rv = DoCacheWriteTruncatedResponse();
226 break;
227 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE:
228 rv = DoCacheWriteTruncatedResponseComplete(rv);
229 break;
230 default:
231 NOTREACHED() << "bad state";
232 rv = ERR_FAILED;
233 break;
234 }
235 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING);
236
237 if (rv != ERR_IO_PENDING && !callback_.is_null()) {
238 read_buf_ = NULL; // Release the buffer before invoking the callback.
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 Does it matter whether we release the buffer befor
shivanisha 2017/06/07 15:19:19 From what I understand its important to release be
Randy Smith (Not in Mondays) 2017/06/08 17:10:00 It's true that it can be only used by one layer at
shivanisha 2017/06/12 18:51:14 Removed the comment.
239 base::ResetAndReturn(&callback_).Run(rv);
240 }
241 return rv;
242 }
243
244 int HttpCache::Writers::DoNetworkRead() {
245 next_state_ = State::NETWORK_READ_COMPLETE;
246 io_callback_ =
247 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
248 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback_);
249 }
250
251 int HttpCache::Writers::DoNetworkReadComplete(int result) {
252 if (result < 0) {
253 OnNetworkReadFailure(result);
254 return result;
255 }
256
257 next_state_ = State::CACHE_WRITE_DATA;
258 return result;
259 }
260
261 void HttpCache::Writers::OnNetworkReadFailure(int result) {
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 Why does this function take a |result| argument?
shivanisha 2017/06/07 15:19:19 A follow up CL will use result to invoke ProcessFa
262 // At this point active_transaction_ may or may not be alive.
263 // If no consumer, invoke entry processing here itself.
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 This comment doesn't seem easily relatable to the
shivanisha 2017/06/07 15:19:19 Yes, removed it.
264 cache_->DoneWithEntry(entry_, active_transaction_, true);
265 }
266
267 int HttpCache::Writers::DoCacheWriteData(int num_bytes) {
268 next_state_ = State::CACHE_WRITE_DATA_COMPLETE;
269 write_len_ = num_bytes;
270 if (!num_bytes || network_read_only_)
271 return num_bytes;
272
273 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
274 io_callback_ =
275 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
276 return WriteToEntry(kResponseContentIndex, current_size, read_buf_.get(),
277 num_bytes, io_callback_);
278 }
279
280 int HttpCache::Writers::WriteToEntry(int index,
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 Given that this is a private function only called
shivanisha 2017/06/07 15:19:18 done
281 int offset,
282 IOBuffer* data,
283 int data_len,
284 const CompletionCallback& callback) {
285 int rv = 0;
286
287 PartialData* partial = nullptr;
288 // The active transaction must be alive if this is a partial request, as
289 // partial requests are exclusive and hence will always be the active
290 // transaction.
291 // Todo(shivanisha): When partial requests support parallel writing, this
292 // assumption will not be true.
293 if (active_transaction_)
294 partial = active_transaction_->partial();
295
296 if (!partial || !data_len) {
297 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback,
298 true);
299 } else {
300 rv = partial->CacheWrite(entry_->disk_entry, data, data_len, callback);
301 }
302 return rv;
303 }
304
305 int HttpCache::Writers::DoCacheWriteDataComplete(int result) {
306 if (result != write_len_) {
307 OnCacheWriteFailure();
308
309 // |active_transaction_| can continue reading from the network.
310 result = write_len_;
311 } else {
312 OnDataReceived(result);
313 }
314 return result;
315 }
316
317 int HttpCache::Writers::DoCacheWriteTruncatedResponse() {
318 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE;
319 const HttpResponseInfo* response = network_transaction_->GetResponseInfo();
320 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer());
321 response->Persist(data->pickle(), true /* skip_transient_headers*/, true);
322 data->Done();
323 io_buf_len_ = data->pickle()->size();
324 io_callback_ =
325 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
326 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
327 io_buf_len_, io_callback_, true);
328 }
329
330 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) {
331 if (result != io_buf_len_) {
332 DLOG(ERROR) << "failed to write response info to cache";
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 What state does this leave the cache in? If it's
shivanisha 2017/06/07 15:19:19 done
333 }
334 truncated_ = true;
335 return OK;
336 }
337
338 void HttpCache::Writers::OnDataReceived(int result) {
339 if (result == 0) {
340 // Check if the response is actually completed or if not, attempt to mark
341 // the entry as truncated.
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 I don't see (here or in OnNetworkReadFailure()) an
shivanisha 2017/06/07 15:19:19 OnNetworkReadFailure() => DoneWithEntry() => Invok
Randy Smith (Not in Mondays) 2017/06/08 17:10:00 Gotchat, and that's fine. But I find the comment
shivanisha 2017/06/12 18:51:14 Added in the comment that the truncation attempt w
342 bool completed = false;
343 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
344 const HttpResponseInfo* response_info =
345 network_transaction_->GetResponseInfo();
346 int64_t content_length = response_info->headers->GetContentLength();
347 if ((content_length >= 0 && content_length <= current_size) ||
348 content_length < 0) {
349 completed = true;
350 }
351 if (!completed) {
352 OnNetworkReadFailure(result);
353 return;
354 }
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 Why not combine these two conditionals? |complete
shivanisha 2017/06/07 15:19:19 done
355 }
356
357 // Notify waiting_for_read_. Tasks will be posted for all the
358 // transactions.
359 ProcessWaitingForReadTransactions(write_len_);
360
361 active_transaction_ = nullptr;
362
363 // TODO(shivanisha) Invoke cache_->DoneWritingToEntry if result is 0 implying
364 // response completion.
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 What gets in the way of doing this now?
shivanisha 2017/06/07 15:19:18 Messes up with the assertions in HttpCache layer s
365 }
366
367 void HttpCache::Writers::OnCacheWriteFailure() {
368 network_read_only_ = true;
369
370 // Call the cache_ function here even if active_transaction_ is alive because
371 // it wouldn't know if this was an error case, since it gets a positive result
372 // back. This will in turn ProcessFailure.
373 // TODO(shivanisha) : Also pass active_transaction_ and
374 // ERR_CACHE_WRITE_FAILURE to DoneWritingToEntry on integration so that it can
375 // be passed back in ProcessFailure.
376 cache_->DoneWritingToEntry(entry_, false);
377 }
378
379 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) {
380 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end();
381 it++) {
Randy Smith (Not in Mondays) 2017/06/06 12:54:57 nit, suggestion: Why not use the new C++ range syn
shivanisha 2017/06/07 15:19:19 done
382 Transaction* transaction = it->transaction;
383 int callback_result = result;
384
385 if (result >= 0) { // success
386 // Save the data in all the waiting transactions' read buffers.
387 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end();
Randy Smith (Not in Mondays) 2017/06/06 12:54:58 I've read over this code a couple of times, and I'
shivanisha 2017/06/07 15:19:18 Oops, corrected.
388 it++) {
389 it->write_len = std::min(it->read_buf_len, result);
390 memcpy(it->read_buf->data(), read_buf_->data(), it->write_len);
391 }
392 callback_result = it->write_len;
393 }
394
395 // If its response completion or failure, this transaction needs to be
396 // removed.
397 if (result <= 0)
398 all_writers_.erase(transaction);
399
400 // Post task to notify transaction.
401 base::ThreadTaskRunnerHandle::Get()->PostTask(
402 FROM_HERE, base::Bind(it->callback, callback_result));
403 }
404
405 waiting_for_read_.clear();
406 }
407
408 void HttpCache::Writers::SetIdleWritersFailState(int result) {
409 // Since this is only for idle transactions, all waiting_for_read_ and
410 // active_transaction_ should be empty.
411 DCHECK(waiting_for_read_.empty());
412 DCHECK(!active_transaction_);
413 for (auto* transaction : all_writers_)
414 transaction->SetSharedWritingFailState(result);
415 all_writers_.clear();
416 ResetStateForEmptyWriters();
417 }
418
419 void HttpCache::Writers::ResetStateForEmptyWriters() {
420 DCHECK(all_writers_.empty());
421 network_read_only_ = false;
422 network_transaction_.reset();
423 }
424
425 void HttpCache::Writers::OnIOComplete(int result) {
426 DoLoop(result);
427 }
428
429 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698