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

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

Issue 2519473002: Fixes the cache lock issue. (Closed)
Patch Set: Redesigned the fix using DataAccess class for eliminating Orphan API.(Rebased till refs/heads/master@{#442607}) Created 3 years, 11 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) 2016 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 "build/build_config.h" // For OS_POSIX
6
7 #if defined(OS_POSIX)
8 #include <unistd.h>
9 #endif
10
11 #include <algorithm>
12 #include <memory>
13 #include <utility>
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/compiler_specific.h"
17 #include "base/format_macros.h"
18 #include "base/location.h"
19 #include "base/macros.h"
20 #include "base/single_thread_task_runner.h"
21 #include "net/http/http_cache_data_access.h"
22 #include "net/http/http_cache_shared_writers.h"
23 #include "net/http/http_cache_transaction.h"
24
25 namespace net {
26
27 HttpCache::SharedWriters::SharedWriters(
28 HttpCache* cache,
29 ActiveEntry* entry,
30 Transaction* cache_transaction,
31 RequestPriority priority,
32 std::unique_ptr<HttpTransaction> network_transaction)
33 : cache_(cache->GetWeakPtr()),
34 entry_(entry),
35 priority_(priority),
36 weak_factory_(this) {
37 cache_transaction->SetShared();
38 all_writers_.insert(cache_transaction);
39 data_access_ =
40 base::MakeUnique<DataAccess>(std::move(network_transaction), entry);
41 io_callback_ = base::Bind(&HttpCache::SharedWriters::OnIOComplete,
42 weak_factory_.GetWeakPtr());
43 }
44
45 HttpCache::SharedWriters::~SharedWriters() {}
46
47 bool HttpCache::SharedWriters::AddTransaction(Transaction* transaction) {
48 transaction->SetShared();
49
50 if (!validating_transaction_) {
51 validating_transaction_ = transaction;
Randy Smith (Not in Mondays) 2017/01/19 00:53:43 How hard would it be to move the validation work i
shivanisha 2017/01/19 21:13:06 Moving the validation work would mean moving the l
Randy Smith (Not in Mondays) 2017/01/20 19:54:31 Ok .... I can only repeat that this feels weird to
shivanisha 2017/01/25 19:46:13 I hear your concerns and that this means SW has an
shivanisha 2017/01/25 19:50:09 Other reviewers, any thoughts on this?
52 return true;
53 }
54
55 waiting_for_validation_.push_back(transaction);
56 return false;
57 }
58
59 bool HttpCache::SharedWriters::empty() {
60 int count = all_writers_.size() + waiting_for_validation_.size() +
61 (validating_transaction_ ? 1 : 0);
62 return count ? false : true;
63 }
64
65 int HttpCache::SharedWriters::DoLoop(int result) {
66 DCHECK(next_state_ != STATE_NONE);
67
68 int rv = result;
69
70 do {
71 State state = next_state_;
72 next_state_ = STATE_NONE;
73 switch (state) {
74 case STATE_NETWORK_READ:
75 DCHECK_EQ(OK, rv);
76 rv = DoNetworkRead();
77 break;
78 case STATE_NETWORK_READ_COMPLETE:
79 rv = DoNetworkReadComplete(rv);
80 break;
81 case STATE_CACHE_WRITE_DATA:
82 rv = DoCacheWriteData(rv);
83 break;
84 case STATE_CACHE_WRITE_DATA_COMPLETE:
85 rv = DoCacheWriteDataComplete(rv);
86 break;
87 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE:
88 rv = DoCacheWriteTruncatedResponse();
89 break;
90 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE:
91 rv = DoCacheWriteTruncatedResponseComplete(rv);
92 break;
93 default:
94 NOTREACHED() << "bad state";
95 rv = ERR_FAILED;
96 break;
97 }
98 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
99
100 if (rv != ERR_IO_PENDING && !callback_.is_null()) {
101 read_buf_ = NULL; // Release the buffer before invoking the callback.
102 base::ResetAndReturn(&callback_).Run(rv);
103 }
104
105 return rv;
106 }
107
108 int HttpCache::SharedWriters::Read(scoped_refptr<IOBuffer> buf,
109 int buf_len,
110 const CompletionCallback& callback,
111 Transaction* transaction,
112 bool* read_in_progress) {
113 DCHECK(buf);
114 DCHECK_GT(buf_len, 0);
115 DCHECK(!callback.is_null());
116
117 // If another transaction is already reading from the network, then this
118 // transaction waits for the read to complete and gets its buffer filled
119 // with the data returned from that read.
120 if (current_writer_) {
121 WaitingWriter waiting_writer(transaction, buf, buf_len);
122 waiting_writers_.push_back(waiting_writer);
123 *read_in_progress = true;
Randy Smith (Not in Mondays) 2017/01/19 00:53:43 So this strikes me as problematic API design--alon
shivanisha 2017/01/19 21:13:06 The difference in the design between the 2 cases i
Randy Smith (Not in Mondays) 2017/01/20 19:54:31 Let's talk this through in a VC--I'm having a lot
shivanisha 2017/01/25 19:46:13 Done. Discussed this with Randy over VC that it is
124 return ERR_IO_PENDING;
125 }
126
127 DCHECK_EQ(next_state_, STATE_NONE);
128 DCHECK(callback_.is_null());
129
130 current_writer_ = transaction;
131
132 read_buf_ = std::move(buf);
133 io_buf_len_ = buf_len;
134
135 next_state_ = STATE_NETWORK_READ;
136 int rv = DoLoop(OK);
137
138 if (rv == ERR_IO_PENDING) {
139 DCHECK(callback_.is_null());
140 callback_ = callback;
141 }
142 return rv;
143 }
144
145 int HttpCache::SharedWriters::DoNetworkRead() {
146 next_state_ = STATE_NETWORK_READ_COMPLETE;
147 return data_access_->Read(read_buf_, io_buf_len_, io_callback_);
148 }
149
150 int HttpCache::SharedWriters::DoNetworkReadComplete(int result) {
151 // Remember at this point current_writer_ may or may not be alive.
152 if (result < 0) {
153 // Empty SharedWriters of all transactions.
154 OnNetworkReadFailure(result);
155 return result;
156 }
157
158 if (result == 0) {
159 // Check if the response is actually completed or if not, attempt to mark
160 // the entry as truncated.
161 if (cache_->IsResponseCompleted(
162 entry_, data_access_->network_transaction_->GetResponseInfo())) {
163 ProcessWaitingWriters(result);
164 ResponseDataComplete();
165 } else {
166 OnNetworkReadFailure(result);
167 }
168 } else { // Successful non zero response.
169 // if no consumer exists, then invoke cache write itself.
170 if (!current_writer_)
171 next_state_ = STATE_CACHE_WRITE_DATA;
Randy Smith (Not in Mondays) 2017/01/19 00:53:42 Thought: It occurs to me that it might be useful f
shivanisha 2017/01/20 16:00:09 I remember talking to others about it and that Sim
172 }
173
174 return result;
175 }
176
177 void HttpCache::SharedWriters::OnNetworkReadFailure(int result) {
178 FailureCleanup(result, false);
179
180 if (cache_->CanResumeEntry(
181 true, "GET", data_access_->network_transaction_->GetResponseInfo(),
182 entry_)) {
183 next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE;
184 network_read_rv_ = result;
185 } else {
186 cache_->ResponseFailedSharedWriters(false, entry_);
187 }
188 }
189
190 int HttpCache::SharedWriters::DoCacheWriteTruncatedResponse() {
191 next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE;
192 return cache_->WriteResponseInfo(
193 entry_, data_access_->network_transaction_->GetResponseInfo(),
194 io_callback_, true, &io_buf_len_);
195 }
196
197 int HttpCache::SharedWriters::DoCacheWriteTruncatedResponseComplete(
198 int result) {
199 bool success = true;
200 if (result != io_buf_len_) {
201 DLOG(ERROR) << "failed to write response info to cache";
202 success = false;
203 }
204 // This object should be destroyed in the end of this flow.
205 destroy_ = true;
Randy Smith (Not in Mondays) 2017/01/19 00:53:43 Hmmm. Setting destroy_ to true means that on the
shivanisha 2017/01/25 19:46:13 Yup, simplified the destruction logic s.t. destroy
206
207 cache_->ResponseFailedSharedWriters(success, entry_);
208
209 // Return the return value returned from the network read to the comsumer.
210 return network_read_rv_;
211 }
212
213 int HttpCache::SharedWriters::CacheWrite(scoped_refptr<IOBuffer> buf,
214 int write_len,
215 const CompletionCallback& callback,
216 Transaction* transaction) {
217 DCHECK_EQ(next_state_, STATE_NONE);
218 DCHECK(buf);
219 DCHECK_GE(write_len, 0);
220 DCHECK(callback_.is_null());
221 DCHECK(!callback.is_null());
222 DCHECK_EQ(current_writer_, transaction);
223
224 read_buf_ = std::move(buf);
225 next_state_ = STATE_CACHE_WRITE_DATA;
226 int rv = DoLoop(write_len);
227
228 if (rv == ERR_IO_PENDING) {
229 DCHECK(callback_.is_null());
230 callback_ = callback;
231 }
232
233 return rv;
234 }
235
236 int HttpCache::SharedWriters::DoCacheWriteData(int num_bytes) {
237 next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE;
238 write_len_ = num_bytes;
239 return data_access_->CacheWrite(read_buf_, num_bytes, io_callback_);
240 }
241
242 int HttpCache::SharedWriters::DoCacheWriteDataComplete(int result) {
243 if (result != write_len_)
244 // Need to take care of all the transactions in SharedWriters and
245 // delete SharedWriters as without the cache, we cannot continue the shared
246 // logic.
247 OnCacheWriteFailure();
248 else
249 OnCacheWriteSuccess(result);
250
251 return result;
252 }
253
254 void HttpCache::SharedWriters::OnCacheWriteSuccess(int result) {
255 // Save the data in all the waiting transactions' read buffers.
256 for (auto it = waiting_writers_.begin(); it != waiting_writers_.end(); it++) {
257 it->write_len = std::min(it->read_buf_len, result);
258 memcpy(it->read_buf->data(), read_buf_->data(), it->write_len);
259 }
260 // Notify waiting_writers_. Tasks will be posted for all the
261 // transactions.
262 ProcessWaitingWriters(write_len_);
263
264 if (result > 0) { // not the end of response
265 current_writer_ = nullptr;
266 return;
267 }
268
269 DCHECK_EQ(result, 0);
270
271 ResponseDataComplete();
272 }
273
274 void HttpCache::SharedWriters::ResponseDataComplete() {
275 ResetCurrentWriter();
276
277 // If there is a transaction validating currently, return.
278 if (validating_transaction_)
279 return;
280
281 // Else empty the SharedWriters object.
282 MoveIdleWritersToReaders();
283 DCHECK(all_writers_.empty());
284
285 MoveToPendingQueue();
286
287 destroy_ = true;
288
289 // Inform cache_ so it can take care of entry_.
290 cache_->ResponseCompleteSharedWriters(entry_);
291 }
292
293 void HttpCache::SharedWriters::OnCacheWriteFailure() {
294 Transaction* current_writer = current_writer_;
295
296 // Needs data_access_ to be valid so needs to be invoked before
297 // ContinueWithoutSharedWriting.
298 FailureCleanup(ERR_CACHE_WRITE_FAILURE, true);
299
300 destroy_ = true;
301
302 if (current_writer) // If the transaction is still alive in this callback.
303 current_writer->ContinueWithoutSharedWriting(std::move(data_access_),
304 false);
305
306 // Inform cache_ so it can take care of entry_.
307 cache_->ResponseFailedSharedWriters(false, entry_);
308 }
309
310 void HttpCache::SharedWriters::MoveIdleWritersToReaders() {
311 // Should be invoked after waiting_writers_ are all processed so that
312 // all_writers_ only contains the idle writers.
313 DCHECK(waiting_writers_.empty());
314 DCHECK(!current_writer_);
315 for (auto idle_writer : all_writers_) {
316 entry_->readers.insert(idle_writer);
317 idle_writer->ResetShared(false, true);
318 }
319 all_writers_.clear();
320 }
321
322 void HttpCache::SharedWriters::ProcessWaitingWriters(int result) {
323 for (auto it = waiting_writers_.begin(); it != waiting_writers_.end(); it++) {
324 Transaction* transaction = it->transaction;
325
326 if (result > 0) { // success
327 // Fill result with the length of buffer filled for this transaction which
328 // may be different from the transaction that actually wrote to the cache
329 // based on the buffer size.
330 result = it->write_len;
331 } else {
332 // If its response completion or failure, this transaction needs to be
333 // removed.
334 transaction->ResetShared();
335 all_writers_.erase(transaction);
336 }
337
338 // Post task to notify transaction.
339 base::ThreadTaskRunnerHandle::Get()->PostTask(
340 FROM_HERE,
341 base::Bind(&HttpCache::NotifyTransaction, cache_->GetWeakPtr(),
342 transaction->GetWeakPtr(), result));
343 }
344
345 waiting_writers_.clear();
346 }
347
348 void HttpCache::SharedWriters::FailureCleanup(int error,
349 bool continue_network_reading) {
350 ResetCurrentWriter(continue_network_reading);
351
352 // Notify waiting_writers_ of the failure. Tasks will be posted for all the
353 // transactions.
354 ProcessWaitingWriters(error);
355
356 // Idle readers should know to fail when Read is invoked by their consumers.
357 SetIdleWritersFailState(error);
358 DCHECK(all_writers_.empty());
359
360 // If there exists a validating_transaction_, it may be waiting
361 // to read response headers from the cache or waiting for receiving
362 // validation response from the network. In both scenarios, it should be safe
363 // to fail.
364 if (validating_transaction_) {
365 validating_transaction_->SetSharedWritingFailState(error);
366 validating_transaction_->ResetShared(true);
367 validating_transaction_ = nullptr;
368 }
369
370 MoveToPendingQueue();
371 }
372
373 bool HttpCache::SharedWriters::StopCaching(Transaction* transaction) {
374 // If this is the only transaction in SharedWriters either in validation or
375 // reading stage, then stopping will be successful. If not, then we will not
376 // stop caching since there are other consumers waiting to read from the
377 // cache.
378 bool result = false;
379 if (transaction == validating_transaction_) {
380 if (all_writers_.empty()) {
381 result = true;
382 validating_transaction_ = nullptr;
383 }
384 } else if (all_writers_.size() == 1 && all_writers_.count(transaction) &&
385 !validating_transaction_) {
386 if (current_writer_ == transaction) {
387 current_writer_ = nullptr;
388 }
389 all_writers_.erase(transaction);
390 result = true;
391 }
392 if (result) {
393 transaction->ContinueWithoutSharedWriting(std::move(data_access_), true);
394 entry_->writer = transaction;
395 MoveToPendingQueue();
396 }
397 return result;
398 }
399
400 void HttpCache::SharedWriters::ResetCurrentWriter(
401 bool continue_network_reading) {
402 // If current_writer_ is already destroyed, return.
403 if (!current_writer_)
404 return;
405 current_writer_->ResetShared(continue_network_reading);
406 all_writers_.erase(current_writer_);
407 current_writer_ = nullptr;
408 }
409
410 void HttpCache::SharedWriters::SetIdleWritersFailState(int result) {
411 // Since this is only for idle transactions, all waiting_writers_ and
412 // current_writer_ should be empty.
413 DCHECK(waiting_writers_.empty());
414 DCHECK(!current_writer_);
415
416 for (auto transaction : all_writers_) {
417 transaction->SetSharedWritingFailState(result);
418 transaction->ResetShared();
419 }
420
421 all_writers_.clear();
422 }
423
424 void HttpCache::SharedWriters::OnValidationMatch(Transaction* transaction,
425 RequestPriority priority) {
426 DCHECK_EQ(validating_transaction_, transaction);
427 ValidationDoneContinue(transaction, priority);
428 }
429
430 void HttpCache::SharedWriters::ValidationDoneContinue(
431 Transaction* transaction,
432 RequestPriority priority) {
433 validating_transaction_ = nullptr;
434 if (priority > priority_) {
435 data_access_->network_transaction_->SetPriority(priority);
436 priority_ = priority;
437 }
438 all_writers_.insert(transaction);
439 ProcessFirstWaitingValidation();
440 }
441
442 void HttpCache::SharedWriters::ProcessFirstWaitingValidation() {
443 if (!waiting_for_validation_.empty() || validating_transaction_)
444 base::ThreadTaskRunnerHandle::Get()->PostTask(
445 FROM_HERE,
446 base::Bind(&HttpCache::SharedWriters::OnProcessFirstWaitingValidation,
447 weak_factory_.GetWeakPtr()));
448 }
449
450 void HttpCache::SharedWriters::OnProcessFirstWaitingValidation() {
451 if (waiting_for_validation_.empty() && !validating_transaction_)
452 return;
453
454 Transaction* transaction = nullptr;
455 if (validating_transaction_) {
456 transaction = validating_transaction_;
457 } else {
458 transaction = waiting_for_validation_.front();
459 waiting_for_validation_.erase(waiting_for_validation_.begin());
460 validating_transaction_ = transaction;
461 }
462 transaction->io_callback().Run(OK);
463 }
464
465 std::unique_ptr<HttpTransaction> HttpCache::SharedWriters::OnValidationNoMatch(
466 Transaction* transaction,
467 std::unique_ptr<HttpTransaction> network_transaction,
468 RequestPriority priority) {
469 DCHECK_EQ(validating_transaction_, transaction);
470 // If there is no transaction in all_writers_, its ok to rewrite the entry
471 // response.
472 if (all_writers_.empty()) {
473 data_access_.reset(new DataAccess(std::move(network_transaction), entry_));
474 ValidationDoneContinue(transaction, priority);
475 return std::unique_ptr<HttpTransaction>();
476 }
477
478 transaction->ResetShared();
479 validating_transaction_ = nullptr;
480 MoveToPendingQueue();
481 return network_transaction;
482 }
483
484 void HttpCache::SharedWriters::DoneReading(Transaction* transaction) {
485 // If current_writer_ is set, then wait for current_writer_ to detect the end
486 // of stream.
487 if (current_writer_) {
488 return;
489 }
490 DCHECK(waiting_writers_.empty());
491 RemoveIdleWriter(transaction);
492 // If there is a transaction validating currently, return.
493 if (validating_transaction_) {
494 return;
495 }
496
497 // Else empty the SharedWriters object.
498 MoveIdleWritersToReaders();
499 DCHECK(all_writers_.empty());
500
501 MoveToPendingQueue();
502 }
503
504 void HttpCache::SharedWriters::RemoveIdleWriter(Transaction* transaction) {
505 // The transaction should be part of all_writers.
506 auto it = all_writers_.find(transaction);
507 DCHECK(it != all_writers_.end());
508 all_writers_.erase(transaction);
509 transaction->ResetShared();
510 }
511
512 void HttpCache::SharedWriters::RemoveWaitingWriter(Transaction* transaction) {
513 auto it = waiting_writers_.begin();
514 for (; it != waiting_writers_.end(); it++) {
515 if (transaction == it->transaction) {
516 waiting_writers_.erase(it);
517 all_writers_.erase(transaction);
518 transaction->ResetShared();
519 // If a waiting_writer_ existed, there should have been a current_writer_.
520 DCHECK(current_writer_);
521 break;
522 }
523 }
524 }
525
526 void HttpCache::SharedWriters::RemoveValidatingTransaction(
527 Transaction* transaction) {
528 DCHECK_EQ(validating_transaction_, transaction);
529 validating_transaction_ = nullptr;
530 transaction->ResetShared();
531 ProcessFirstWaitingValidation();
532 }
533
534 bool HttpCache::SharedWriters::RemoveWaitingTransaction(
535 Transaction* transaction) {
536 auto it = std::find(waiting_for_validation_.begin(),
537 waiting_for_validation_.end(), transaction);
538 if (it != waiting_for_validation_.end()) {
539 transaction->ResetShared();
540 waiting_for_validation_.erase(it);
541 return true;
542 }
543 return false;
544 }
545
546 void HttpCache::SharedWriters::RemoveCurrentWriter(Transaction* transaction) {
547 DCHECK_EQ(current_writer_, transaction);
548 ResetCurrentWriter();
549 callback_.Reset();
550 }
551
552 void HttpCache::SharedWriters::MoveFromPendingQueue() {
553 auto it = entry_->pending_queue.begin();
554 while (it != entry_->pending_queue.end()) {
555 Transaction* transaction = *it;
556 if (transaction->IsEligibleForSharedWriting()) {
557 transaction->SetShared();
558 waiting_for_validation_.push_back(transaction);
559 it = entry_->pending_queue.erase(it);
560 } else {
561 ++it;
562 }
563 }
564 }
565
566 void HttpCache::SharedWriters::MoveToPendingQueue() {
567 // For maintaining the order of the transactions as they arrived, append
568 // these to the front of the pending_queue. Note that the order is preserved
569 // only among the transactions that are eligible for sharing. For others, they
570 // may have arrived earlier but may be processed later which is fair since
571 // they have to anyways wait till the entry is written to the cache.
572 while (!waiting_for_validation_.empty()) {
573 Transaction* transaction = waiting_for_validation_.back();
574 transaction->ResetShared(true);
575 entry_->pending_queue.push_front(transaction);
576 waiting_for_validation_.pop_back();
577 }
578 }
579
580 bool HttpCache::SharedWriters::CanReset() {
581 return !destroy_;
582 }
583
584 HttpCache::SharedWriters::WaitingWriter::WaitingWriter(
585 Transaction* cache_transaction,
586 scoped_refptr<IOBuffer> buf,
587 int len)
588 : transaction(cache_transaction),
589 read_buf(std::move(buf)),
590 read_buf_len(len),
591 write_len(0) {}
592
593 HttpCache::SharedWriters::WaitingWriter::~WaitingWriter() {}
594
595 HttpCache::SharedWriters::WaitingWriter::WaitingWriter(const WaitingWriter&) =
596 default;
597
598 void HttpCache::SharedWriters::OnIOComplete(int result) {
599 bool destroy = destroy_;
600 DoLoop(result);
601
602 // Only check the local variable as this object may have been destroyed if
603 // destroy_ was not set to true.
604 if (destroy)
605 SelfDestroy();
Randy Smith (Not in Mondays) 2017/01/19 00:53:43 Worth a DCHECK that DoLoop() didn't return ERR_IO_
shivanisha 2017/01/25 19:46:13 N/A now that self destruction is not needed.
606 }
607
608 void HttpCache::SharedWriters::SelfDestroy() {
Randy Smith (Not in Mondays) 2017/01/19 00:53:43 I don't think there's a need for a separate functi
shivanisha 2017/01/25 19:46:13 N/A now that self destruction is not needed.
609 delete this;
610 }
611
612 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698