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

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

Powered by Google App Engine
This is Rietveld 408576698