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

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: Fix test class memory leak Created 3 years, 5 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>
jkarlin 2017/06/28 15:50:09 newline between groups of includes
shivanisha 2017/06/28 19:38:19 Added
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(disk_cache::Entry* disk_entry)
15 : disk_entry_(disk_entry), weak_factory_(this) {}
16
17 HttpCache::Writers::~Writers() {}
18
19 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf,
20 int buf_len,
21 const CompletionCallback& callback,
22 Transaction* transaction) {
23 DCHECK(buf);
jkarlin 2017/06/28 15:50:09 include file missing for DCHECK
shivanisha 2017/06/28 19:38:19 Added logging.h
24 DCHECK_GT(buf_len, 0);
25 DCHECK(!callback.is_null());
26 DCHECK(transaction);
27
28 // If another transaction invoked a Read which is currently ongoing, then
29 // this transaction waits for the read to complete and gets its buffer filled
30 // with the data returned from that read.
31 if (next_state_ != State::NONE) {
32 WaitingForRead waiting_transaction(transaction, buf, buf_len, callback);
33 waiting_for_read_.push_back(waiting_transaction);
34 return ERR_IO_PENDING;
jkarlin 2017/06/28 15:50:09 need include file for ERR_IO_PENDING
shivanisha 2017/06/28 19:38:19 Added
35 }
36
37 DCHECK_EQ(next_state_, State::NONE);
38 DCHECK(callback_.is_null());
39 DCHECK_EQ(nullptr, active_transaction_);
40 DCHECK(IsPresent(transaction));
41 active_transaction_ = transaction;
42
43 read_buf_ = std::move(buf);
44 io_buf_len_ = buf_len;
45 next_state_ = State::NETWORK_READ;
46
47 int rv = DoLoop(OK);
48 if (rv == ERR_IO_PENDING)
49 callback_ = callback;
50
51 return rv;
52 }
53
54 bool HttpCache::Writers::StopCaching(Transaction* transaction) {
55 // If this is the only transaction in Writers, then stopping will be
56 // successful. If not, then we will not stop caching since there are
57 // other consumers waiting to read from the cache.
58 if (all_writers_.size() == 1) {
59 DCHECK(all_writers_.count(transaction));
60 network_read_only_ = true;
61 return true;
62 }
63 return false;
64 }
65
66 void HttpCache::Writers::AddTransaction(
67 Transaction* transaction,
68 std::unique_ptr<HttpTransaction> network_transaction,
69 bool is_exclusive) {
70 DCHECK(transaction);
71 DCHECK(CanAddWriters());
72 DCHECK(network_transaction_ || network_transaction);
73
74 std::pair<TransactionSet::iterator, bool> return_val =
75 all_writers_.insert(transaction);
76 DCHECK_EQ(return_val.second, true);
77
78 if (is_exclusive) {
79 DCHECK_EQ(1u, all_writers_.size());
80 is_exclusive_ = true;
81 }
82
83 if (network_transaction) {
84 DCHECK(!network_transaction_);
85 network_transaction_ = std::move(network_transaction);
86 }
87
88 priority_ = std::max(transaction->priority(), priority_);
89 network_transaction_->SetPriority(priority_);
90 }
91
92 void HttpCache::Writers::RemoveTransaction(Transaction* transaction) {
93 if (!transaction)
jkarlin 2017/06/28 15:50:09 Huh, do we really expect to have nullptr here?
shivanisha 2017/06/28 19:38:19 Yes, it will be clearer in the integration CL. But
94 return;
95
96 // The transaction should be part of all_writers.
97 auto it = all_writers_.find(transaction);
98 DCHECK(it != all_writers_.end());
jkarlin 2017/06/28 15:50:09 DCHECK_NE
shivanisha 2017/06/28 19:38:19 Gives a compile time error due to iterators being
jkarlin 2017/06/30 18:20:38 Good point, we don't actually need to print out th
99 all_writers_.erase(transaction);
jkarlin 2017/06/28 15:50:09 Better to erase the iterator.
shivanisha 2017/06/28 19:38:20 done
100 if (all_writers_.empty() && next_state_ == State::NONE)
101 ResetStateForEmptyWriters();
102 else
103 UpdatePriority();
104
105 if (active_transaction_ == transaction) {
106 active_transaction_ = nullptr;
107 callback_.Reset();
108 return;
109 }
110
111 auto waiting_it = waiting_for_read_.begin();
112 for (; waiting_it != waiting_for_read_.end(); waiting_it++) {
113 if (transaction == waiting_it->transaction) {
114 waiting_for_read_.erase(waiting_it);
115 // If a waiting transaction existed, there should have been an
116 // active_transaction_.
117 DCHECK(active_transaction_);
118 return;
119 }
120 }
121 }
122
123 void HttpCache::Writers::UpdatePriority() {
124 // Get the current highest priority.
125 RequestPriority current_highest = MINIMUM_PRIORITY;
126 for (auto* transaction : all_writers_)
127 current_highest = std::max(transaction->priority(), current_highest);
128
129 if (priority_ != current_highest) {
130 network_transaction_->SetPriority(current_highest);
131 priority_ = current_highest;
132 }
133 }
134
135 bool HttpCache::Writers::ContainsOnlyIdleWriters() const {
136 return waiting_for_read_.empty() && !active_transaction_;
137 }
138
139 HttpCache::TransactionSet HttpCache::Writers::RemoveAllIdleWriters() {
140 // Should be invoked after |waiting_for_read_| transactions and
141 // |active_transaction_| are processed so that all_writers_ only contains idle
142 // writers.
143 DCHECK(ContainsOnlyIdleWriters());
144
145 TransactionSet idle_writers;
146 idle_writers.insert(all_writers_.begin(), all_writers_.end());
147 all_writers_.clear();
148 ResetStateForEmptyWriters();
149 return idle_writers;
150 }
151
152 bool HttpCache::Writers::CanAddWriters() {
153 if (all_writers_.empty())
154 return true;
155
156 return !is_exclusive_ && !network_read_only_;
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 fail when Read is invoked on them.
167 SetIdleWritersFailState(error);
168
169 if (all_writers_.empty())
170 ResetStateForEmptyWriters();
171 }
172
173 void HttpCache::Writers::TruncateEntry() {
174 // TODO(shivanisha) On integration, see if the entry really needs to be
175 // truncated on the lines of Transaction::AddTruncatedFlag and then proceed.
176 DCHECK_EQ(next_state_, State::NONE);
177 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE;
178 DoLoop(OK);
179 }
180
181 LoadState HttpCache::Writers::GetWriterLoadState() {
182 DCHECK(network_transaction_);
183 return network_transaction_->GetLoadState();
184 }
185
186 HttpCache::Writers::WaitingForRead::WaitingForRead(
187 Transaction* cache_transaction,
188 scoped_refptr<IOBuffer> buf,
189 int len,
190 const CompletionCallback& consumer_callback)
191 : transaction(cache_transaction),
192 read_buf(std::move(buf)),
193 read_buf_len(len),
194 write_len(0),
195 callback(consumer_callback) {
196 DCHECK(cache_transaction);
197 DCHECK(read_buf);
198 DCHECK_GT(len, 0);
199 DCHECK(!consumer_callback.is_null());
200 }
201
202 HttpCache::Writers::WaitingForRead::~WaitingForRead() {}
203 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) =
204 default;
205
206 int HttpCache::Writers::DoLoop(int result) {
207 DCHECK(next_state_ != State::NONE);
jkarlin 2017/06/28 15:50:09 DCHECK_NE
shivanisha 2017/06/28 19:38:20 done
208 int rv = result;
209 do {
210 State state = next_state_;
211 next_state_ = State::NONE;
jkarlin 2017/06/28 15:50:09 Can you use the same STATE::INVALID as we do in HT
shivanisha 2017/06/28 19:38:19 done
212 switch (state) {
213 case State::NETWORK_READ:
214 DCHECK_EQ(OK, rv);
215 rv = DoNetworkRead();
216 break;
217 case State::NETWORK_READ_COMPLETE:
218 rv = DoNetworkReadComplete(rv);
219 break;
220 case State::CACHE_WRITE_DATA:
221 rv = DoCacheWriteData(rv);
222 break;
223 case State::CACHE_WRITE_DATA_COMPLETE:
224 rv = DoCacheWriteDataComplete(rv);
225 break;
226 case State::CACHE_WRITE_TRUNCATED_RESPONSE:
227 rv = DoCacheWriteTruncatedResponse();
228 break;
229 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE:
230 rv = DoCacheWriteTruncatedResponseComplete(rv);
231 break;
232 default:
jkarlin 2017/06/28 15:50:08 Better not to have a default case in this switch s
shivanisha 2017/06/28 19:38:19 done
233 NOTREACHED() << "bad state";
234 rv = ERR_FAILED;
235 break;
236 }
237 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING);
238
239 if (rv != ERR_IO_PENDING && !callback_.is_null()) {
240 read_buf_ = NULL;
241 base::ResetAndReturn(&callback_).Run(rv);
242 }
243 return rv;
244 }
245
246 int HttpCache::Writers::DoNetworkRead() {
247 next_state_ = State::NETWORK_READ_COMPLETE;
248 CompletionCallback io_callback =
249 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
250 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback);
251 }
252
253 int HttpCache::Writers::DoNetworkReadComplete(int result) {
254 if (result < 0) {
255 OnNetworkReadFailure(result);
256 return result;
257 }
258
259 next_state_ = State::CACHE_WRITE_DATA;
260 return result;
261 }
262
263 void HttpCache::Writers::OnNetworkReadFailure(int result) {
264 ProcessFailure(active_transaction_, result);
265
266 active_transaction_ = nullptr;
267
268 // TODO(shivanisha): Invoke DoneWithEntry here while
269 // integrating this class with HttpCache. That will also invoke truncation of
270 // the entry.
271 }
272
273 int HttpCache::Writers::DoCacheWriteData(int num_bytes) {
274 next_state_ = State::CACHE_WRITE_DATA_COMPLETE;
275 write_len_ = num_bytes;
276 if (!num_bytes || network_read_only_)
277 return num_bytes;
278
279 int current_size = disk_entry_->GetDataSize(kResponseContentIndex);
280 CompletionCallback io_callback =
281 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
282
283 int rv = 0;
284
285 PartialData* partial = nullptr;
286 // The active transaction must be alive if this is a partial request, as
287 // partial requests are exclusive and hence will always be the active
288 // transaction.
289 // Todo(shivanisha): When partial requests support parallel writing, this
jkarlin 2017/06/28 15:50:09 s/Todo/TODO
shivanisha 2017/06/28 19:38:19 done
290 // assumption will not be true.
291 if (active_transaction_)
292 partial = active_transaction_->partial();
293
294 if (!partial) {
295 rv = disk_entry_->WriteData(kResponseContentIndex, current_size,
296 read_buf_.get(), num_bytes, io_callback, true);
297 } else {
298 rv = partial->CacheWrite(disk_entry_, read_buf_.get(), num_bytes,
299 io_callback);
300 }
301 return rv;
302 }
303
304 int HttpCache::Writers::DoCacheWriteDataComplete(int result) {
305 if (result != write_len_) {
306 OnCacheWriteFailure();
307
308 // |active_transaction_| can continue reading from the network.
309 result = write_len_;
310 } else {
311 OnDataReceived(result);
312 }
313 return result;
314 }
315
316 int HttpCache::Writers::DoCacheWriteTruncatedResponse() {
317 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE;
318 const HttpResponseInfo* response = network_transaction_->GetResponseInfo();
319 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer());
320 response->Persist(data->pickle(), true /* skip_transient_headers*/, true);
321 data->Done();
322 io_buf_len_ = data->pickle()->size();
323 CompletionCallback io_callback =
324 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr());
325 return disk_entry_->WriteData(kResponseInfoIndex, 0, data.get(), io_buf_len_,
326 io_callback, true);
327 }
328
329 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) {
330 if (result != io_buf_len_) {
331 DLOG(ERROR) << "failed to write response info to cache";
332
333 // TODO(shivanisha): Invoke DoneWritingToEntry so that this entry is doomed.
334 }
335 truncated_ = true;
336 return OK;
337 }
338
339 void HttpCache::Writers::OnDataReceived(int result) {
340 if (result == 0) {
341 // Check if the response is actually completed or if not, attempt to mark
342 // the entry as truncated in OnNetworkReadFailure.
343 int current_size = 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 OnNetworkReadFailure(result);
349 return;
350 }
351 // TODO(shivanisha) Invoke cache_->DoneWritingToEntry() with success after
352 // integration with HttpCache layer.
353 }
354
355 // Notify waiting_for_read_. Tasks will be posted for all the
356 // transactions.
357 ProcessWaitingForReadTransactions(write_len_);
358
359 active_transaction_ = nullptr;
360
361 if (all_writers_.empty())
362 ResetStateForEmptyWriters();
363 }
364
365 void HttpCache::Writers::OnCacheWriteFailure() {
jkarlin 2017/06/30 18:20:39 This is interesting. We actually expect the cache
shivanisha 2017/07/05 18:55:42 As discussed f2f, since only around 0.003% of simp
jkarlin 2017/07/06 18:50:03 Acknowledged.
366 DLOG(ERROR) << "failed to write response data to cache";
367
368 // Now writers will only be reading from the network.
369 network_read_only_ = true;
370
371 ProcessFailure(active_transaction_, ERR_CACHE_WRITE_FAILURE);
372
373 active_transaction_ = nullptr;
374
375 // Call the cache_ function here even if |active_transaction_| is alive
376 // because it wouldn't know if this was an error case, since it gets a
377 // positive result back.
378 // TODO(shivanisha) : Invoke DoneWritingToEntry on integration. Since the
379 // active_transaction_ continues to read from the network, invoke
380 // DoneWritingToEntry with nullptr as transaction so that it is not removed
381 // from |this|.
382 }
383
384 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) {
385 for (auto& waiting : waiting_for_read_) {
386 Transaction* transaction = waiting.transaction;
387 int callback_result = result;
388
389 if (result >= 0) { // success
390 // Save the data in the waiting transaction's read buffer.
391 waiting.write_len = std::min(waiting.read_buf_len, result);
392 memcpy(waiting.read_buf->data(), read_buf_->data(), waiting.write_len);
393 callback_result = waiting.write_len;
394 }
395
396 // If its response completion or failure, this transaction needs to be
397 // removed.
398 if (result <= 0)
399 all_writers_.erase(transaction);
400
401 // Post task to notify transaction.
402 base::ThreadTaskRunnerHandle::Get()->PostTask(
403 FROM_HERE, base::Bind(waiting.callback, callback_result));
404 }
405
406 waiting_for_read_.clear();
407 }
408
409 void HttpCache::Writers::SetIdleWritersFailState(int result) {
410 // Since this is only for idle transactions, waiting_for_read_
411 // should be empty.
412 DCHECK(waiting_for_read_.empty());
413 for (auto* transaction : all_writers_) {
414 if (transaction == active_transaction_)
415 continue;
416 transaction->SetSharedWritingFailState(result);
jkarlin 2017/06/28 15:50:08 I'm not sure that this new method is necessary. Wh
shivanisha 2017/06/28 19:38:19 That would need the HC::T::Read() to invoke Writer
jkarlin 2017/06/30 18:20:38 Just to make sure, but the idea is that HC::T::Rea
shivanisha 2017/07/05 18:55:42 There are multiple scenarios in HC::T::Read() to d
jkarlin 2017/07/06 18:50:04 This will fail because the transaction isn't part
jkarlin 2017/07/06 21:17:49 Ignore my comments above, I meant to just post the
417 all_writers_.erase(transaction);
418 }
419 }
420
421 void HttpCache::Writers::ResetStateForEmptyWriters() {
422 DCHECK(all_writers_.empty());
423 network_read_only_ = false;
424 network_transaction_.reset();
425 }
426
427 void HttpCache::Writers::OnIOComplete(int result) {
428 DoLoop(result);
429 }
430
431 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698