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

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

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: Rebased till refs/heads/master@{#462134} Created 3 years, 8 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_cache.h" 5 #include "net/http/http_cache.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 true, 89 true,
90 thread_, 90 thread_,
91 net_log, 91 net_log,
92 backend, 92 backend,
93 callback); 93 callback);
94 } 94 }
95 95
96 //----------------------------------------------------------------------------- 96 //-----------------------------------------------------------------------------
97 97
98 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry) 98 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
99 : disk_entry(entry), 99 : disk_entry(entry) {}
100 writer(NULL),
101 will_process_pending_queue(false),
102 doomed(false) {
103 }
104 100
105 HttpCache::ActiveEntry::~ActiveEntry() { 101 HttpCache::ActiveEntry::~ActiveEntry() {
106 if (disk_entry) { 102 if (disk_entry) {
107 disk_entry->Close(); 103 disk_entry->Close();
108 disk_entry = NULL; 104 disk_entry = nullptr;
109 } 105 }
110 } 106 }
111 107
112 size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const { 108 size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const {
113 // Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers| 109 // Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers|
114 // and |pending_queue| because the Transactions are owned by their respective 110 // and |add_to_entry_queue| because the Transactions are owned by their
115 // URLRequestHttpJobs. 111 // respective URLRequestHttpJobs.
116 return 0; 112 return 0;
117 } 113 }
118 114
119 bool HttpCache::ActiveEntry::HasNoTransactions() { 115 bool HttpCache::ActiveEntry::HasNoTransactions() {
120 return !writer && readers.empty() && pending_queue.empty(); 116 return !writer && readers.empty() && add_to_entry_queue.empty() &&
117 done_headers_queue.empty() && !headers_transaction;
118 }
119
120 bool HttpCache::ActiveEntry::HasNoActiveTransactions() {
121 return !writer && readers.empty() && !headers_transaction;
121 } 122 }
122 123
123 //----------------------------------------------------------------------------- 124 //-----------------------------------------------------------------------------
124 125
125 // This structure keeps track of work items that are attempting to create or 126 // This structure keeps track of work items that are attempting to create or
126 // open cache entries or the backend itself. 127 // open cache entries or the backend itself.
127 struct HttpCache::PendingOp { 128 struct HttpCache::PendingOp {
128 PendingOp() : disk_entry(NULL) {} 129 PendingOp() : disk_entry(NULL) {}
129 ~PendingOp() {} 130 ~PendingOp() {}
130 131
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 } 354 }
354 } 355 }
355 } 356 }
356 357
357 HttpCache::~HttpCache() { 358 HttpCache::~HttpCache() {
358 // Transactions should see an invalid cache after this point; otherwise they 359 // Transactions should see an invalid cache after this point; otherwise they
359 // could see an inconsistent object (half destroyed). 360 // could see an inconsistent object (half destroyed).
360 weak_factory_.InvalidateWeakPtrs(); 361 weak_factory_.InvalidateWeakPtrs();
361 362
362 // If we have any active entries remaining, then we need to deactivate them. 363 // If we have any active entries remaining, then we need to deactivate them.
363 // We may have some pending calls to OnProcessPendingQueue, but since those 364 // We may have some pending tasks to process queued transactions ,but since
364 // won't run (due to our destruction), we can simply ignore the corresponding 365 // those won't run (due to our destruction), we can simply ignore the
365 // will_process_pending_queue flag. 366 // corresponding flags.
366 while (!active_entries_.empty()) { 367 while (!active_entries_.empty()) {
367 ActiveEntry* entry = active_entries_.begin()->second.get(); 368 ActiveEntry* entry = active_entries_.begin()->second.get();
368 entry->will_process_pending_queue = false; 369 entry->will_process_queued_transactions = false;
369 entry->pending_queue.clear(); 370 entry->add_to_entry_queue.clear();
370 entry->readers.clear(); 371 entry->readers.clear();
371 entry->writer = NULL; 372 entry->done_headers_queue.clear();
373 entry->headers_transaction = nullptr;
374 entry->writer = nullptr;
372 DeactivateEntry(entry); 375 DeactivateEntry(entry);
373 } 376 }
374 377
375 doomed_entries_.clear(); 378 doomed_entries_.clear();
376 379
377 // Before deleting pending_ops_, we have to make sure that the disk cache is 380 // Before deleting pending_ops_, we have to make sure that the disk cache is
378 // done with said operations, or it will attempt to use deleted data. 381 // done with said operations, or it will attempt to use deleted data.
379 disk_cache_.reset(); 382 disk_cache_.reset();
380 383
381 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end(); 384 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end();
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
622 // We keep track of doomed entries so that we can ensure that they are 625 // We keep track of doomed entries so that we can ensure that they are
623 // cleaned up properly when the cache is destroyed. 626 // cleaned up properly when the cache is destroyed.
624 ActiveEntry* entry_ptr = entry.get(); 627 ActiveEntry* entry_ptr = entry.get();
625 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr)); 628 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr));
626 doomed_entries_[entry_ptr] = std::move(entry); 629 doomed_entries_[entry_ptr] = std::move(entry);
627 630
628 entry_ptr->disk_entry->Doom(); 631 entry_ptr->disk_entry->Doom();
629 entry_ptr->doomed = true; 632 entry_ptr->doomed = true;
630 633
631 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() || 634 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
632 entry_ptr->will_process_pending_queue); 635 entry_ptr->headers_transaction ||
636 entry_ptr->will_process_queued_transactions);
633 return OK; 637 return OK;
634 } 638 }
635 639
636 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { 640 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
637 std::unique_ptr<WorkItem> item = 641 std::unique_ptr<WorkItem> item =
638 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr); 642 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr);
639 PendingOp* pending_op = GetPendingOp(key); 643 PendingOp* pending_op = GetPendingOp(key);
640 if (pending_op->writer) { 644 if (pending_op->writer) {
641 pending_op->pending_queue.push_back(std::move(item)); 645 pending_op->pending_queue.push_back(std::move(item));
642 return ERR_IO_PENDING; 646 return ERR_IO_PENDING;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
678 DCHECK(entry->doomed); 682 DCHECK(entry->doomed);
679 DCHECK(entry->HasNoTransactions()); 683 DCHECK(entry->HasNoTransactions());
680 684
681 auto it = doomed_entries_.find(entry); 685 auto it = doomed_entries_.find(entry);
682 DCHECK(it != doomed_entries_.end()); 686 DCHECK(it != doomed_entries_.end());
683 doomed_entries_.erase(it); 687 doomed_entries_.erase(it);
684 } 688 }
685 689
686 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 690 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
687 auto it = active_entries_.find(key); 691 auto it = active_entries_.find(key);
688 return it != active_entries_.end() ? it->second.get() : NULL; 692 return it != active_entries_.end() ? it->second.get() : nullptr;
689 } 693 }
690 694
691 HttpCache::ActiveEntry* HttpCache::ActivateEntry( 695 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
692 disk_cache::Entry* disk_entry) { 696 disk_cache::Entry* disk_entry) {
693 DCHECK(!FindActiveEntry(disk_entry->GetKey())); 697 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
694 ActiveEntry* entry = new ActiveEntry(disk_entry); 698 ActiveEntry* entry = new ActiveEntry(disk_entry);
695 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry); 699 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry);
696 return entry; 700 return entry;
697 } 701 }
698 702
699 void HttpCache::DeactivateEntry(ActiveEntry* entry) { 703 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
700 DCHECK(!entry->will_process_pending_queue); 704 DCHECK(!entry->will_process_queued_transactions);
701 DCHECK(!entry->doomed); 705 DCHECK(!entry->doomed);
702 DCHECK(entry->disk_entry); 706 DCHECK(entry->disk_entry);
703 DCHECK(entry->HasNoTransactions()); 707 DCHECK(entry->HasNoTransactions());
704 708
705 std::string key = entry->disk_entry->GetKey(); 709 std::string key = entry->disk_entry->GetKey();
706 if (key.empty()) 710 if (key.empty())
707 return SlowDeactivateEntry(entry); 711 return SlowDeactivateEntry(entry);
708 712
709 auto it = active_entries_.find(key); 713 auto it = active_entries_.find(key);
710 DCHECK(it != active_entries_.end()); 714 DCHECK(it != active_entries_.end());
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 } 824 }
821 825
822 void HttpCache::DestroyEntry(ActiveEntry* entry) { 826 void HttpCache::DestroyEntry(ActiveEntry* entry) {
823 if (entry->doomed) { 827 if (entry->doomed) {
824 FinalizeDoomedEntry(entry); 828 FinalizeDoomedEntry(entry);
825 } else { 829 } else {
826 DeactivateEntry(entry); 830 DeactivateEntry(entry);
827 } 831 }
828 } 832 }
829 833
830 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 834 int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
835 Transaction* transaction) {
831 DCHECK(entry); 836 DCHECK(entry);
832 DCHECK(entry->disk_entry); 837 DCHECK(entry->disk_entry);
833 838 // Always add a new transaction to the queue to maintain FIFO order.
834 // We implement a basic reader/writer lock for the disk cache entry. If 839 entry->add_to_entry_queue.push_back(transaction);
835 // there is already a writer, then everyone has to wait for the writer to 840 ProcessQueuedTransactions(entry);
836 // finish before they can access the cache entry. There can be multiple 841 return ERR_IO_PENDING;
837 // readers. 842 }
838 // 843
839 // NOTE: If the transaction can only write, then the entry should not be in 844 int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
840 // use (since any existing entry should have already been doomed). 845 Transaction* transaction) {
841 846 // If |transaction| is the current writer, do nothing. This can happen for
842 if (entry->writer || entry->will_process_pending_queue) { 847 // range requests since they can go back to headers phase after starting to
843 entry->pending_queue.push_back(trans); 848 // write.
844 return ERR_IO_PENDING; 849 if (entry->writer == transaction)
845 } 850 return OK;
846 851
847 if (trans->mode() & Transaction::WRITE) { 852 // Proceed only if |transaction| is a headers_transaction and not already a
848 // transaction needs exclusive access to the entry 853 // reader.
849 if (entry->readers.empty()) { 854 if (entry->headers_transaction != transaction)
850 entry->writer = trans; 855 return OK;
jkarlin 2017/04/06 17:37:13 Once read-only transactions follow the normal path
shivanisha 2017/04/07 21:11:15 Yes, converted to DCHECK_EQ(entry->headers_transac
851 } else { 856
jkarlin 2017/04/06 17:37:13 DCHECK_EQ(entry->headers_transaction, transaction)
shivanisha 2017/04/07 21:11:16 done
852 entry->pending_queue.push_back(trans); 857 entry->headers_transaction = nullptr;
853 return ERR_IO_PENDING; 858
854 } 859 entry->done_headers_queue.push_back(transaction);
860 ProcessQueuedTransactions(entry);
861 return ERR_IO_PENDING;
862 }
863
864 void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
865 TransactionList* list) {
866 // Process done_headers_queue before add_to_entry_queue to maintain FIFO
867 // order.
868 for (auto* transaction : entry->done_headers_queue)
869 list->push_back(transaction);
870 entry->done_headers_queue.clear();
871
872 for (auto* transaction : entry->add_to_entry_queue)
873 list->push_back(transaction);
874 entry->add_to_entry_queue.clear();
875 }
876
877 void HttpCache::ProcessEntryFailure(ActiveEntry* entry) {
878 // Failure case is either writer failing to completely write the response to
879 // the cache or validating transaction received a non-304 response.
880 TransactionList list;
881 if (entry->HasNoActiveTransactions() &&
882 !entry->will_process_queued_transactions) {
883 entry->disk_entry->Doom();
884 RemoveAllQueuedTransactions(entry, &list);
885 DestroyEntry(entry);
855 } else { 886 } else {
856 // transaction needs read access to the entry 887 DoomActiveEntry(entry->disk_entry->GetKey());
857 entry->readers.insert(trans); 888 RemoveAllQueuedTransactions(entry, &list);
858 } 889 }
859 890 // ERR_CACHE_RACE causes the transaction to restart the whole process.
860 // We do this before calling EntryAvailable to force any further calls to 891 for (auto* transaction : list)
861 // AddTransactionToEntry to add their transaction to the pending queue, which 892 transaction->io_callback().Run(net::ERR_CACHE_RACE);
862 // ensures FIFO ordering. 893 }
863 if (!entry->writer && !entry->pending_queue.empty()) 894
864 ProcessPendingQueue(entry); 895 void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
865 896 // Multiple readers may finish with an entry at once, so we want to batch up
866 return OK; 897 // calls to OnProcessQueuedTransactions. This flag also tells us that we
867 } 898 // should not delete the entry before OnProcessQueuedTransactions runs.
868 899 if (entry->will_process_queued_transactions)
869 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 900 return;
901
902 entry->will_process_queued_transactions = true;
903
904 // Post a task instead of invoking the io callback of another transaction here
905 // to avoid re-entrancy.
906 base::ThreadTaskRunnerHandle::Get()->PostTask(
907 FROM_HERE,
908 base::Bind(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(), entry));
909 }
910
911 void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
912 entry->will_process_queued_transactions = false;
913
914 // If no one is interested in this entry, then we can deactivate it.
915 if (entry->HasNoTransactions()) {
916 DestroyEntry(entry);
917 return;
918 }
919
920 if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty())
921 return;
922
923 // To maintain FIFO order of transactions, done_headers_queue should be
924 // processed first, and then add_to_entry_queue.
925 // If another transaction is writing the response, let validated transactions
926 // wait till the response is complete. If the response is not yet started, the
927 // done_headers_queue transaction should start writing it.
928 if (!entry->writer && !entry->done_headers_queue.empty()) {
929 ProcessDoneHeadersQueue(entry);
930 return;
jkarlin 2017/04/06 17:37:13 If you posttask the io callbacks (as mentioned in
shivanisha 2017/04/07 21:11:15 Removed the return
931 }
932
933 if (!entry->add_to_entry_queue.empty())
934 ProcessAddToEntryQueue(entry);
935 }
936
937 void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
jkarlin 2017/04/06 17:37:13 Can you add a comment above this function definiti
shivanisha 2017/04/07 21:11:16 done
938 if (entry->headers_transaction) {
939 // Note the entry may be new or may already have a response body written to
940 // it. In both cases, a transaction with write mode set, needs to wait
941 // because only one transaction is allowed to be validating at a time.
942 // If transaction is read-only and entry is not yet complete, then it
jkarlin 2017/04/06 17:37:13 Can remove the read-only comment.
shivanisha 2017/04/07 21:11:15 done
943 // needs to wait. If entry is complete, it needs to wait because its
944 // possible that the headers_transaction dooms the entry if its not a match.
945 return;
946 }
947
948 Transaction* transaction = entry->add_to_entry_queue.front();
949
950 // If a writer is present, read-only transactions cannot proceed.
951 if (entry->writer && !(transaction->mode() & Transaction::WRITE))
952 return;
jkarlin 2017/04/06 17:37:13 This condition can go once we stop special-casing
shivanisha 2017/04/07 21:11:16 Yes, removed
953
954 entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
955
956 // This state may be reached either during the start of an entry or when the
957 // response is written or being written.
958 if (transaction->mode() & Transaction::WRITE) {
959 entry->headers_transaction = transaction;
960 } else {
961 // A read-only transaction can start reading the response.
jkarlin 2017/04/06 17:37:13 This condition can also go.
shivanisha 2017/04/07 21:11:15 Removed
962 // If a read transaction is here, then it has to be an already created
963 // entry as the other case is already handled in the Transaction state
964 // machine.
965 entry->readers.insert(transaction);
966
967 // More readers can join too.
968 // TODO (shivanisha@) Consider a loop instead of posting a task.
969 ProcessQueuedTransactions(entry);
970 }
971
972 transaction->io_callback().Run(OK);
973 }
974
975 void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
976 DCHECK(!entry->writer && !entry->done_headers_queue.empty());
977 Transaction* transaction = entry->done_headers_queue.front();
978 // If this transaction is responsible for writing the response body.
979 if (transaction->mode() & Transaction::WRITE) {
980 entry->writer = transaction;
981 } else {
982 // Response body is already written, convert to a reader.
983 auto return_val = entry->readers.insert(transaction);
984 DCHECK_EQ(return_val.second, true);
985 }
986 // Post another task to give a chance to more transactions to either join
987 // readers or another transaction to start parallel validation.
988 // TODO (shivanisha@) Consider a loop instead of posting a task.
jkarlin 2017/04/06 17:37:13 I assume the concern with looping is reentrancy?
shivanisha 2017/04/07 21:11:16 Introduced a loop instead of posting a task. As di
989 ProcessQueuedTransactions(entry);
990 entry->done_headers_queue.erase(entry->done_headers_queue.begin());
991 transaction->io_callback().Run(OK);
992 }
993
994 bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
995 Transaction* transaction,
996 int response_code) const {
997 if (transaction != entry->headers_transaction)
998 return false;
999
1000 // If its not a match then it should be the transaction responsible for
1001 // writing the response body.
1002 if (response_code != 304) {
1003 return !entry->writer && entry->done_headers_queue.empty();
1004 }
1005
1006 return true;
1007 }
1008
1009 bool HttpCache::CanTransactionWriteResponseBody(
1010 ActiveEntry* entry,
1011 Transaction* transaction,
1012 const std::string& method) const {
1013 if (transaction == entry->writer)
1014 return true;
1015
1016 if (method == "HEAD" || method == "DELETE")
1017 return false;
1018
1019 // Check if transaction is about to start writing to the cache.
1020
1021 // Transaction's mode may have been set to NONE if StopCaching was invoked.
1022 if (!(transaction->mode() & Transaction::WRITE ||
1023 transaction->mode() == Transaction::NONE))
jkarlin 2017/04/06 17:37:13 I think you want: if ( !(transaction>mode() & Tra
shivanisha 2017/04/07 21:11:16 Not sure of the feedback here.
1024 return false;
1025
1026 if (transaction == entry->headers_transaction)
1027 return !entry->writer && entry->done_headers_queue.empty();
jkarlin 2017/04/06 17:37:13 I don't understand this conditional. Can you add a
shivanisha 2017/04/07 21:11:16 This and the below conditions are actually doing m
1028
1029 // Check the first of done_headers_queue, since the rest should anyways be
1030 // READ mode transactions as they would be a result of validation match.
1031 if (transaction == entry->done_headers_queue.front())
1032 return !entry->writer;
1033
1034 return false;
1035 }
1036
1037 void HttpCache::DoneWithEntry(ActiveEntry* entry,
1038 Transaction* transaction,
870 bool cancel) { 1039 bool cancel) {
871 // If we already posted a task to move on to the next transaction and this was 1040 // Transaction is waiting in the done_headers_queue.
872 // the writer, there is nothing to cancel. 1041 auto i = std::find(entry->done_headers_queue.begin(),
jkarlin 2017/04/06 17:37:13 s/i/it/
shivanisha 2017/04/07 21:11:16 done
873 if (entry->will_process_pending_queue && entry->readers.empty()) 1042 entry->done_headers_queue.end(), transaction);
874 return; 1043 if (i != entry->done_headers_queue.end()) {
875 1044 entry->done_headers_queue.erase(i);
876 if (entry->writer) { 1045 if (cancel)
877 DCHECK(trans == entry->writer); 1046 ProcessEntryFailure(entry);
878 1047 return;
1048 }
1049
1050 // Transaction is removed in the headers phase.
1051 if (transaction == entry->headers_transaction) {
1052 // If the response is not written (cancel is true), consider it a failure.
1053 DoneWritingToEntry(entry, !cancel, transaction);
1054 return;
1055 }
1056
1057 // Transaction is removed in the writing phase.
1058 if (transaction == entry->writer) {
879 // Assume there was a failure. 1059 // Assume there was a failure.
880 bool success = false; 1060 bool success = false;
881 if (cancel) { 1061 if (cancel) {
882 DCHECK(entry->disk_entry); 1062 DCHECK(entry->disk_entry);
883 // This is a successful operation in the sense that we want to keep the 1063 // This is a successful operation in the sense that we want to keep the
884 // entry. 1064 // entry.
885 success = trans->AddTruncatedFlag(); 1065 success = transaction->AddTruncatedFlag();
886 // The previous operation may have deleted the entry. 1066 // The previous operation may have deleted the entry.
887 if (!trans->entry()) 1067 if (!transaction->entry())
888 return; 1068 return;
889 } 1069 }
890 DoneWritingToEntry(entry, success); 1070 DoneWritingToEntry(entry, success, transaction);
891 } else { 1071 return;
892 DoneReadingFromEntry(entry, trans); 1072 }
893 } 1073
894 } 1074 // Transaction is reading from the entry.
895 1075 DoneReadingFromEntry(entry, transaction);
896 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 1076 }
897 DCHECK(entry->readers.empty()); 1077
898 1078 void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
899 entry->writer = NULL; 1079 bool success,
900 1080 Transaction* transaction) {
901 if (success) { 1081 DCHECK(transaction == entry->writer ||
902 ProcessPendingQueue(entry); 1082 transaction == entry->headers_transaction);
903 } else { 1083
904 DCHECK(!entry->will_process_pending_queue); 1084 if (transaction == entry->writer)
905 1085 entry->writer = nullptr;
906 // We failed to create this entry. 1086 else
907 TransactionList pending_queue; 1087 entry->headers_transaction = nullptr;
908 pending_queue.swap(entry->pending_queue); 1088
909 1089 // If writer fails, all transactions restart the headers_transaction.
910 entry->disk_entry->Doom(); 1090 if (!success && entry->headers_transaction) {
911 DestroyEntry(entry); 1091 entry->headers_transaction->SetValidatingCannotProceed();
912 1092 entry->headers_transaction = nullptr;
913 // We need to do something about these pending entries, which now need to 1093 DCHECK(entry->HasNoActiveTransactions());
914 // be added to a new entry. 1094 }
915 while (!pending_queue.empty()) { 1095 if (!success)
916 // ERR_CACHE_RACE causes the transaction to restart the whole process. 1096 ProcessEntryFailure(entry);
917 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE); 1097 else
918 pending_queue.pop_front(); 1098 ProcessQueuedTransactions(entry);
919 } 1099 }
920 } 1100
921 } 1101 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry,
922 1102 Transaction* transaction) {
923 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
924 DCHECK(!entry->writer); 1103 DCHECK(!entry->writer);
925 1104 auto it = entry->readers.find(transaction);
jkarlin 2017/04/06 17:37:13 Can you add a TODO that this is a linear operation
shivanisha 2017/04/07 21:11:15 readers is an unordered_set so average case is not
926 auto it = entry->readers.find(trans);
927 DCHECK(it != entry->readers.end()); 1105 DCHECK(it != entry->readers.end());
928
929 entry->readers.erase(it); 1106 entry->readers.erase(it);
930 1107
931 ProcessPendingQueue(entry); 1108 ProcessQueuedTransactions(entry);
932 }
933
934 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
935 DCHECK(entry->writer);
936 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
937 DCHECK(entry->readers.empty());
938
939 Transaction* trans = entry->writer;
940
941 entry->writer = NULL;
942 entry->readers.insert(trans);
943
944 ProcessPendingQueue(entry);
945 } 1109 }
946 1110
947 LoadState HttpCache::GetLoadStateForPendingTransaction( 1111 LoadState HttpCache::GetLoadStateForPendingTransaction(
948 const Transaction* trans) { 1112 const Transaction* trans) {
949 auto i = active_entries_.find(trans->key()); 1113 auto i = active_entries_.find(trans->key());
950 if (i == active_entries_.end()) { 1114 if (i == active_entries_.end()) {
951 // If this is really a pending transaction, and it is not part of 1115 // If this is really a pending transaction, and it is not part of
952 // active_entries_, we should be creating the backend or the entry. 1116 // active_entries_, we should be creating the backend or the entry.
953 return LOAD_STATE_WAITING_FOR_CACHE; 1117 return LOAD_STATE_WAITING_FOR_CACHE;
954 } 1118 }
(...skipping 29 matching lines...) Expand all
984 1148
985 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found; 1149 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found;
986 ++k) { 1150 ++k) {
987 found = RemovePendingTransactionFromEntry(k->first, trans); 1151 found = RemovePendingTransactionFromEntry(k->first, trans);
988 } 1152 }
989 1153
990 DCHECK(found) << "Pending transaction not found"; 1154 DCHECK(found) << "Pending transaction not found";
991 } 1155 }
992 1156
993 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 1157 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
994 Transaction* trans) { 1158 Transaction* transaction) {
995 TransactionList& pending_queue = entry->pending_queue; 1159 TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
996 1160
997 auto j = find(pending_queue.begin(), pending_queue.end(), trans); 1161 auto j =
998 if (j == pending_queue.end()) 1162 find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
1163 if (j == add_to_entry_queue.end())
999 return false; 1164 return false;
1000 1165
1001 pending_queue.erase(j); 1166 add_to_entry_queue.erase(j);
1002 return true; 1167 return true;
1003 } 1168 }
1004 1169
1005 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, 1170 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
1006 Transaction* trans) { 1171 Transaction* trans) {
1007 if (pending_op->writer->Matches(trans)) { 1172 if (pending_op->writer->Matches(trans)) {
1008 pending_op->writer->ClearTransaction(); 1173 pending_op->writer->ClearTransaction();
1009 pending_op->writer->ClearEntry(); 1174 pending_op->writer->ClearEntry();
1010 return true; 1175 return true;
1011 } 1176 }
1012 WorkItemList& pending_queue = pending_op->pending_queue; 1177 WorkItemList& pending_queue = pending_op->pending_queue;
1013 1178
1014 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) { 1179 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) {
1015 if ((*it)->Matches(trans)) { 1180 if ((*it)->Matches(trans)) {
1016 pending_queue.erase(it); 1181 pending_queue.erase(it);
1017 return true; 1182 return true;
1018 } 1183 }
1019 } 1184 }
1020 return false; 1185 return false;
1021 } 1186 }
1022 1187
1023 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1024 // Multiple readers may finish with an entry at once, so we want to batch up
1025 // calls to OnProcessPendingQueue. This flag also tells us that we should
1026 // not delete the entry before OnProcessPendingQueue runs.
1027 if (entry->will_process_pending_queue)
1028 return;
1029 entry->will_process_pending_queue = true;
1030
1031 base::ThreadTaskRunnerHandle::Get()->PostTask(
1032 FROM_HERE,
1033 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1034 }
1035
1036 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1037 entry->will_process_pending_queue = false;
1038 DCHECK(!entry->writer);
1039
1040 // If no one is interested in this entry, then we can deactivate it.
1041 if (entry->HasNoTransactions()) {
1042 DestroyEntry(entry);
1043 return;
1044 }
1045
1046 if (entry->pending_queue.empty())
1047 return;
1048
1049 // Promote next transaction from the pending queue.
1050 Transaction* next = entry->pending_queue.front();
1051 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1052 return; // Have to wait.
1053
1054 entry->pending_queue.erase(entry->pending_queue.begin());
1055
1056 int rv = AddTransactionToEntry(entry, next);
1057 if (rv != ERR_IO_PENDING) {
1058 next->io_callback().Run(rv);
1059 }
1060 }
1061
1062 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { 1188 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1063 WorkItemOperation op = pending_op->writer->operation(); 1189 WorkItemOperation op = pending_op->writer->operation();
1064 1190
1065 // Completing the creation of the backend is simpler than the other cases. 1191 // Completing the creation of the backend is simpler than the other cases.
1066 if (op == WI_CREATE_BACKEND) 1192 if (op == WI_CREATE_BACKEND)
1067 return OnBackendCreated(result, pending_op); 1193 return OnBackendCreated(result, pending_op);
1068 1194
1069 std::unique_ptr<WorkItem> item = std::move(pending_op->writer); 1195 std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
1070 bool fail_requests = false; 1196 bool fail_requests = false;
1071 1197
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
1196 building_backend_ = false; 1322 building_backend_ = false;
1197 DeletePendingOp(pending_op); 1323 DeletePendingOp(pending_op);
1198 } 1324 }
1199 1325
1200 // The cache may be gone when we return from the callback. 1326 // The cache may be gone when we return from the callback.
1201 if (!item->DoCallback(result, disk_cache_.get())) 1327 if (!item->DoCallback(result, disk_cache_.get()))
1202 item->NotifyTransaction(result, NULL); 1328 item->NotifyTransaction(result, NULL);
1203 } 1329 }
1204 1330
1205 } // namespace net 1331 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698