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

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

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: Rough patch to discuss if ActiveEntry would be better designed with a state machine Created 3 years, 9 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 validated_queue.empty() && !validating_transaction;
118 }
119
120 bool HttpCache::ActiveEntry::HasNoActiveTransactions() {
121 return !writer && readers.empty() && !validating_transaction;
122 }
123
124 bool HttpCache::ActiveEntry::IsReader(Transaction* transaction) {
125 return readers.count(transaction);
126 }
127
128 int HttpCache::ActiveEntry::AdvanceState(Transaction* transaction) {
129 switch (state) {
130 case State::NONE:
131 return DoAdvanceStateNone(transaction);
132 case State::VALIDATING:
133 return DoAdvanceStateValidating(transaction);
134 case State::VALIDATING_WRITE:
135 return DoAdvanceStateValidatingWrite(transaction);
136 case State::READ:
137 return DoAdvanceStateRead(transaction);
138 case State::VALIDATING_READ:
139 return DoAdvanceStateValidatingRead(transaction);
140 }
141 }
142
143 int HttpCache::ActiveEntry::RegressState(Transaction* transaction) {
144 switch (state) {
145 case State::NONE:
146 NOTREACHED() << "bad state";
147 return ERR_FAILURE;
148 case State::VALIDATING:
149 DoRegressStateValidating(transaction);
150 break;
151 case State::VALIDATING_WRITE:
152 DoRegressStateValidatingWrite(transaction);
153 break;
154 case State::WRITE:
155
156 case State::READ:
157 DoRegressStateRead(transaction);
158 break;
159 case State::VALIDATING_READ:
160 DoRegressStateValidatingRead(transaction);
161 break;
162 }
163 }
164
165 int HttpCache::ActiveEntry::DoAdvanceStateNone(Transaction* transaction) {
166 if (transaction->mode() & Transaction::WRITE) {
167 validating_transaction = transaction;
168 // entry->writer = transaction;
169 state = State::VALIDATING;
170 } else {
171 entry->readers.insert(transaction);
172 state = State::READ;
173 }
174 return OK;
175 }
176
177 int HttpCache::ActiveEntry::DoAdvanceStateValidating(Transaction* transaction) {
178 if (transaction == validating_transaction) {
179 writer = transaction;
180 state = STATE::WRITE;
181 return OK;
182 }
183 // Note the entry may be new or may already have a response body written to
184 // it. In both cases, for a |transaction| with write mode set needs to wait
185 // because only one transaction is allowed to be validating at a time.
186 // If |transaction| is read-only, then it needs to wait because its possible
187 // that the validating_transaction dooms the entry if its not a match or it
188 // needs to wait anyways if the entry is not completely written yet.
189 entry->add_to_entry_queue.push_back(transaction);
190 return ERR_IO_PENDING;
191 }
192
193 int HttpCache::ActiveEntry::DoAdvanceStateWrite(Transaction* transaction) {
194 if (transaction->mode() & Transaction::WRITE) {
195 DCHECK_NE(transaction, validating_transaction);
196 validating_transaction = transaction;
197 state = State::VALIDATING_WRITE;
198 return OK;
199 }
200
201 entry->add_to_entry_queue.push_back(transaction);
202 return ERR_IO_PENDING;
203 }
204
205 int HttpCache::ActiveEntry::DoAdvanceStateValidatingWrite(
206 Transaction* transaction) {
207 if (transaction == validating_transaction) {
208 validated_queue.push_back(transaction);
209 validating_transaction = nullptr;
210 state = State::WRITE;
211 return OK;
212 }
213
214 entry->add_to_entry_queue.push_back(transaction);
215 return ERR_IO_PENDING;
216 }
217
218 int HttpCache::ActiveEntry::DoAdvanceStateRead(Transaction* transaction) {
219 if (transaction->mode() & Transaction::WRITE) {
220 DCHECK_EQ(validating_transaction, nullptr);
221 validating_transaction = transaction;
222 state = State::VALIDATING_READ;
223 } else {
224 entry->readers.insert(transaction);
225 }
226 return OK;
227 }
228
229 int HttpCache::ActiveEntry::DoAdvanceStateValidatingRead(
230 Transaction* transaction) {
231 if ((transaction->mode() & Transaction::WRITE) &&
232 (transaction != validating_transaction)) {
233 entry->add_to_entry_queue.push_back(transaction);
234 return ERR_IO_PENDING;
235 } else {
236 entry->readers.insert(transaction);
237 if (transaction == validating_transaction) {
238 validating_transaction = nullptr;
239 state = State::READ;
240 }
241 }
242 return OK;
243 }
244
245 int HttpCache::ActiveEntry::DoRegressStateValidating(Transaction* transaction) {
246 DCHECK_EQ(validating_transaction, transaction);
247 validating_transaction = nullptr;
248 state = State::NONE;
249 }
250
251 int HttpCache::ActiveEntry::DoRegressStateValidatingWrite(
252 Transaction* transaction) {
253 DCHECK(transaction == validating_transaction || transaction == writer);
254 if (transaction == validating_transaction) {
255 validating_transaction = nullptr;
256 state = State::WRITE;
257 }
258 writer = nullptr;
259 state = State::VALIDATING;
260 }
261
262 int HttpCache::ActiveEntry::DoRegressStateWrite(Transaction* transaction) {
263 DCHECK_EQ(transaction, writer);
264 writer = nullptr;
265 state = State::NONE;
266 }
267
268 int HttpCache::ActiveEntry::DoRegressStateRead(Transaction* transaction) {
269 auto it = entry->readers.find(trans);
270 DCHECK(it != entry->readers.end());
271
272 entry->readers.erase(it);
273 state = State::NONE;
274 }
275
276 int HttpCache::ActiveEntry::DoRegressStateValidatingRead(
277 Transaction* transaction) {
278 if (transaction == validating_transaction) {
279 validating_transaction = nullptr;
280 state = State::READ;
281 return;
282 }
283 auto it = entry->readers.find(trans);
284 DCHECK(it != entry->readers.end());
285
286 entry->readers.erase(it);
287 state = State
121 } 288 }
122 289
123 //----------------------------------------------------------------------------- 290 //-----------------------------------------------------------------------------
124 291
125 // This structure keeps track of work items that are attempting to create or 292 // This structure keeps track of work items that are attempting to create or
126 // open cache entries or the backend itself. 293 // open cache entries or the backend itself.
127 struct HttpCache::PendingOp { 294 struct HttpCache::PendingOp {
128 PendingOp() : disk_entry(NULL) {} 295 PendingOp() : disk_entry(NULL) {}
129 ~PendingOp() {} 296 ~PendingOp() {}
130 297
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 } 520 }
354 } 521 }
355 } 522 }
356 523
357 HttpCache::~HttpCache() { 524 HttpCache::~HttpCache() {
358 // Transactions should see an invalid cache after this point; otherwise they 525 // Transactions should see an invalid cache after this point; otherwise they
359 // could see an inconsistent object (half destroyed). 526 // could see an inconsistent object (half destroyed).
360 weak_factory_.InvalidateWeakPtrs(); 527 weak_factory_.InvalidateWeakPtrs();
361 528
362 // If we have any active entries remaining, then we need to deactivate them. 529 // 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 530 // We may have some pending tasks to process pending queue for reading or
364 // won't run (due to our destruction), we can simply ignore the corresponding 531 // parallel validation, but since those won't run (due to our destruction),
365 // will_process_pending_queue flag. 532 // we can simply ignore the corresponding flags.
366 while (!active_entries_.empty()) { 533 while (!active_entries_.empty()) {
367 ActiveEntry* entry = active_entries_.begin()->second.get(); 534 ActiveEntry* entry = active_entries_.begin()->second.get();
368 entry->will_process_pending_queue = false; 535 entry->will_process_waiting_transactions = false;
369 entry->pending_queue.clear(); 536 entry->will_process_parallel_validation = false;
537 entry->add_to_entry_queue.clear();
370 entry->readers.clear(); 538 entry->readers.clear();
371 entry->writer = NULL; 539 entry->validated_queue.clear();
540 entry->validating_transaction = nullptr;
541 entry->writer = nullptr;
372 DeactivateEntry(entry); 542 DeactivateEntry(entry);
373 } 543 }
374 544
375 doomed_entries_.clear(); 545 doomed_entries_.clear();
376 546
377 // Before deleting pending_ops_, we have to make sure that the disk cache is 547 // 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. 548 // done with said operations, or it will attempt to use deleted data.
379 disk_cache_.reset(); 549 disk_cache_.reset();
380 550
381 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end(); 551 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end();
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 // We keep track of doomed entries so that we can ensure that they are 791 // We keep track of doomed entries so that we can ensure that they are
622 // cleaned up properly when the cache is destroyed. 792 // cleaned up properly when the cache is destroyed.
623 ActiveEntry* entry_ptr = entry.get(); 793 ActiveEntry* entry_ptr = entry.get();
624 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr)); 794 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr));
625 doomed_entries_[entry_ptr] = std::move(entry); 795 doomed_entries_[entry_ptr] = std::move(entry);
626 796
627 entry_ptr->disk_entry->Doom(); 797 entry_ptr->disk_entry->Doom();
628 entry_ptr->doomed = true; 798 entry_ptr->doomed = true;
629 799
630 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() || 800 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
631 entry_ptr->will_process_pending_queue); 801 !entry_ptr->validated_queue.empty() ||
802 entry_ptr->validating_transaction ||
803 entry_ptr->will_process_waiting_transactions ||
804 entry_ptr->will_process_parallel_validation);
632 return OK; 805 return OK;
633 } 806 }
634 807
635 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { 808 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
636 std::unique_ptr<WorkItem> item = 809 std::unique_ptr<WorkItem> item =
637 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr); 810 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr);
638 PendingOp* pending_op = GetPendingOp(key); 811 PendingOp* pending_op = GetPendingOp(key);
639 if (pending_op->writer) { 812 if (pending_op->writer) {
640 pending_op->pending_queue.push_back(std::move(item)); 813 pending_op->pending_queue.push_back(std::move(item));
641 return ERR_IO_PENDING; 814 return ERR_IO_PENDING;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
677 DCHECK(entry->doomed); 850 DCHECK(entry->doomed);
678 DCHECK(entry->HasNoTransactions()); 851 DCHECK(entry->HasNoTransactions());
679 852
680 auto it = doomed_entries_.find(entry); 853 auto it = doomed_entries_.find(entry);
681 DCHECK(it != doomed_entries_.end()); 854 DCHECK(it != doomed_entries_.end());
682 doomed_entries_.erase(it); 855 doomed_entries_.erase(it);
683 } 856 }
684 857
685 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 858 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
686 auto it = active_entries_.find(key); 859 auto it = active_entries_.find(key);
687 return it != active_entries_.end() ? it->second.get() : NULL; 860 return it != active_entries_.end() ? it->second.get() : nullptr;
861 }
862
863 bool HttpCache::IsEntryAlive(const std::string& key, ActiveEntry* entry) {
864 ActiveEntry* active_entry = FindActiveEntry(key);
865 if (active_entry && active_entry == entry)
866 return true;
867 return doomed_entries_.count(entry);
688 } 868 }
689 869
690 HttpCache::ActiveEntry* HttpCache::ActivateEntry( 870 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
691 disk_cache::Entry* disk_entry) { 871 disk_cache::Entry* disk_entry) {
692 DCHECK(!FindActiveEntry(disk_entry->GetKey())); 872 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
693 ActiveEntry* entry = new ActiveEntry(disk_entry); 873 ActiveEntry* entry = new ActiveEntry(disk_entry);
694 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry); 874 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry);
695 return entry; 875 return entry;
696 } 876 }
697 877
698 void HttpCache::DeactivateEntry(ActiveEntry* entry) { 878 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
699 DCHECK(!entry->will_process_pending_queue);
700 DCHECK(!entry->doomed); 879 DCHECK(!entry->doomed);
701 DCHECK(entry->disk_entry); 880 DCHECK(entry->disk_entry);
702 DCHECK(entry->HasNoTransactions()); 881 DCHECK(entry->HasNoTransactions());
703 882
704 std::string key = entry->disk_entry->GetKey(); 883 std::string key = entry->disk_entry->GetKey();
705 if (key.empty()) 884 if (key.empty())
706 return SlowDeactivateEntry(entry); 885 return SlowDeactivateEntry(entry);
707 886
708 auto it = active_entries_.find(key); 887 auto it = active_entries_.find(key);
709 DCHECK(it != active_entries_.end()); 888 DCHECK(it != active_entries_.end());
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
819 } 998 }
820 999
821 void HttpCache::DestroyEntry(ActiveEntry* entry) { 1000 void HttpCache::DestroyEntry(ActiveEntry* entry) {
822 if (entry->doomed) { 1001 if (entry->doomed) {
823 FinalizeDoomedEntry(entry); 1002 FinalizeDoomedEntry(entry);
824 } else { 1003 } else {
825 DeactivateEntry(entry); 1004 DeactivateEntry(entry);
826 } 1005 }
827 } 1006 }
828 1007
829 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 1008 int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
1009 Transaction* transaction) {
830 DCHECK(entry); 1010 DCHECK(entry);
831 DCHECK(entry->disk_entry); 1011 DCHECK(entry->disk_entry);
832 1012
833 // We implement a basic reader/writer lock for the disk cache entry. If 1013 // If a pending queue processing task is posted, then this transaction
834 // there is already a writer, then everyone has to wait for the writer to 1014 // should be added after the queue task is processed, to maintain FIFO
835 // finish before they can access the cache entry. There can be multiple 1015 // ordering.
836 // readers. 1016 if (entry->will_process_waiting_transactions ||
837 // 1017 entry->will_process_parallel_validation) {
838 // NOTE: If the transaction can only write, then the entry should not be in 1018 entry->add_to_entry_queue.push_back(transaction);
839 // use (since any existing entry should have already been doomed).
840
841 if (entry->writer || entry->will_process_pending_queue) {
842 entry->pending_queue.push_back(trans);
843 return ERR_IO_PENDING; 1019 return ERR_IO_PENDING;
844 } 1020 }
845 1021
846 if (trans->mode() & Transaction::WRITE) { 1022 int rv = AddTransactionToEntryImpl(entry, transaction);
847 // transaction needs exclusive access to the entry 1023 if (rv == ERR_IO_PENDING)
848 if (entry->readers.empty()) { 1024 entry->add_to_entry_queue.push_back(transaction);
849 entry->writer = trans; 1025 return rv;
850 } else { 1026 }
851 entry->pending_queue.push_back(trans); 1027
852 return ERR_IO_PENDING; 1028 // We implement a basic reader/writer lock for the disk cache entry. If there
853 } 1029 // is a writer, then all read-only transactions must wait. Non read-only
854 } else { 1030 // transactions can proceed to their validation phase. Validation is allowed
855 // transaction needs read access to the entry 1031 // for one transaction at a time so that we do not end up with wasted network
856 entry->readers.insert(trans); 1032 // requests.
1033 int HttpCache::AddTransactionToEntryImpl(ActiveEntry* entry,
1034 Transaction* transaction) {
1035 if (transaction->mode() & Transaction::WRITE)
1036 return AddTransactionToEntryForWrite(entry, transaction);
1037 return AddTransactionToEntryForRead(entry, transaction);
1038 }
1039
1040 int HttpCache::AddTransactionToEntryForRead(ActiveEntry* entry,
1041 Transaction* transaction) {
1042 if (entry->writer) {
1043 return ERR_IO_PENDING;
857 } 1044 }
858 1045 entry->readers.insert(transaction);
859 // We do this before calling EntryAvailable to force any further calls to 1046 if (!entry->add_to_entry_queue.empty())
860 // AddTransactionToEntry to add their transaction to the pending queue, which 1047 ProcessWaitingTransactions(entry);
861 // ensures FIFO ordering.
862 if (!entry->writer && !entry->pending_queue.empty())
863 ProcessPendingQueue(entry);
864
865 return OK; 1048 return OK;
866 } 1049 }
867 1050
868 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 1051 int HttpCache::AddTransactionToEntryForWrite(ActiveEntry* entry,
1052 Transaction* transaction) {
1053 // A transaction that has the write mode can become the next validating
1054 // transaction. It can also become the writer if there is no active writer at
1055 // this time.
1056 if (entry->validating_transaction) {
1057 return ERR_IO_PENDING;
1058 }
1059 entry->validating_transaction = transaction;
1060 if (!entry->writer && entry->readers.empty()) {
1061 entry->writer = transaction;
1062 }
1063
1064 // If there is no writer, we can add more readers.
1065 if (!entry->writer && !entry->add_to_entry_queue.empty())
1066 ProcessWaitingTransactions(entry);
1067 return OK;
1068 }
1069
1070 int HttpCache::DoneValidationWithEntry(ActiveEntry* entry,
1071 Transaction* transaction,
1072 bool is_match) {
1073 DCHECK(entry->writer == transaction ||
1074 entry->validating_transaction == transaction);
1075 entry->validating_transaction = nullptr;
1076
1077 if (!is_match) {
1078 if (entry->writer == transaction)
1079 entry->writer = nullptr;
1080 if (entry->HasNoActiveTransactions())
1081 DestroyEntryRestartPendingTransactions(entry);
1082 else
1083 DoomEntryRestartPendingTransactions(entry);
1084 return OK;
1085 }
1086
1087 int rv = OK;
1088 // If another transaction is writing the response, let this transaction wait
1089 // till the response is complete.
1090 if (entry->writer != transaction) {
1091 entry->validated_queue.push_back(transaction);
1092 rv = ERR_IO_PENDING;
1093 }
1094 // Else the transaction should either be the writer if its responsible for
1095 // writing the response or should have been converted to a reader.
1096
1097 // In all cases another transaction can start its validation phase.
1098 ProcessParallelValidation(entry);
1099 return rv;
1100 }
1101
1102 void HttpCache::DoneValidationWriteEntry(ActiveEntry* entry,
1103 Transaction* transaction) {
1104 entry->validating_transaction = nullptr;
1105 ProcessParallelValidation(entry);
1106 }
1107
1108 void HttpCache::DoneWithEntry(ActiveEntry* entry,
1109 Transaction* transaction,
869 bool cancel) { 1110 bool cancel) {
870 // If we already posted a task to move on to the next transaction and this was 1111 // If we already posted a task to move on to the next transaction and this was
871 // the writer, there is nothing to cancel. 1112 // the writer, there is nothing to cancel.
872 if (entry->will_process_pending_queue && entry->readers.empty()) 1113 if (entry->will_process_waiting_transactions && entry->writer == transaction)
873 return; 1114 return;
874 1115
875 if (entry->writer) { 1116 // Transaction is waiting in the validated_queue.
876 DCHECK(trans == entry->writer); 1117 auto i = std::find(entry->validated_queue.begin(),
1118 entry->validated_queue.end(), transaction);
1119 if (i != entry->validated_queue.end()) {
1120 entry->validated_queue.erase(i);
1121 return;
1122 }
877 1123
878 // Assume there was a failure. 1124 // Assume there was a failure.
879 bool success = false; 1125 bool success = false;
880 if (cancel) { 1126 if (transaction == entry->writer && cancel) {
881 DCHECK(entry->disk_entry); 1127 DCHECK(entry->disk_entry);
882 // This is a successful operation in the sense that we want to keep the 1128 // This is a successful operation in the sense that we want to keep the
883 // entry. 1129 // entry.
884 success = trans->AddTruncatedFlag(); 1130 success = transaction->AddTruncatedFlag();
885 // The previous operation may have deleted the entry. 1131 // The previous operation may have deleted the entry.
886 if (!trans->entry()) 1132 if (!transaction->entry())
887 return; 1133 return;
888 }
889 DoneWritingToEntry(entry, success);
890 } else {
891 DoneReadingFromEntry(entry, trans);
892 } 1134 }
1135
1136 // Transaction is writing to the entry, either in the validation phase or
1137 // response-body phase.
1138 if (transaction == entry->writer ||
1139 transaction == entry->validating_transaction) {
1140 DoneWritingToEntry(entry, success, transaction);
1141 return;
1142 }
1143
1144 // Transaction is reading from the entry.
1145 DoneReadingFromEntry(entry, transaction);
893 } 1146 }
894 1147
895 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 1148 void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
896 DCHECK(entry->readers.empty()); 1149 bool success,
1150 Transaction* transaction) {
1151 DCHECK(transaction == entry->writer ||
1152 transaction == entry->validating_transaction);
897 1153
898 entry->writer = NULL; 1154 if (transaction == entry->writer)
1155 entry->writer = nullptr;
899 1156
1157 // Transaction is done writing to entry in the validation phase.
1158 if (transaction == entry->validating_transaction) {
1159 entry->validating_transaction = nullptr;
1160
1161 if (!entry->writer)
1162 DoneWritingToEntryProcessOtherTransactions(entry, success);
1163 return;
1164 }
1165
1166 // Transaction is done writing to entry in the response body phase.
1167 // Whether its a failure or success, validating transaction can be the new
1168 // writer.
1169 if (entry->validating_transaction) {
1170 entry->writer = entry->validating_transaction;
1171 return;
1172 }
1173
1174 DoneWritingToEntryProcessOtherTransactions(entry, success);
1175 }
1176
1177 void HttpCache::DoneWritingToEntryProcessOtherTransactions(ActiveEntry* entry,
1178 bool success) {
900 if (success) { 1179 if (success) {
901 ProcessPendingQueue(entry); 1180 DCHECK(entry->readers.empty());
902 } else { 1181 ProcessWaitingTransactions(entry);
903 DCHECK(!entry->will_process_pending_queue); 1182 return;
1183 }
904 1184
905 // We failed to create this entry. 1185 DestroyEntryRestartPendingTransactions(entry);
906 TransactionList pending_queue;
907 pending_queue.swap(entry->pending_queue);
908
909 entry->disk_entry->Doom();
910 DestroyEntry(entry);
911
912 // We need to do something about these pending entries, which now need to
913 // be added to a new entry.
914 while (!pending_queue.empty()) {
915 // ERR_CACHE_RACE causes the transaction to restart the whole process.
916 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
917 pending_queue.pop_front();
918 }
919 }
920 } 1186 }
921 1187
922 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { 1188 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
923 DCHECK(!entry->writer); 1189 DCHECK(!entry->writer);
924 1190
925 auto it = entry->readers.find(trans); 1191 auto it = entry->readers.find(trans);
926 DCHECK(it != entry->readers.end()); 1192 DCHECK(it != entry->readers.end());
927 1193
928 entry->readers.erase(it); 1194 entry->readers.erase(it);
929 1195
930 ProcessPendingQueue(entry); 1196 ProcessWaitingTransactions(entry);
931 } 1197 }
932 1198
933 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { 1199 bool HttpCache::ConvertWriterToReader(ActiveEntry* entry,
934 DCHECK(entry->writer); 1200 Transaction* transaction) {
935 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); 1201 DCHECK_EQ(entry->validating_transaction, transaction);
936 DCHECK(entry->readers.empty()); 1202 DCHECK_EQ(transaction->mode(), Transaction::READ_WRITE);
937 1203
938 Transaction* trans = entry->writer; 1204 // We need to differentiate between the scenarios when the entry is already
1205 // written to the cache vs. when the entry is not completely written and this
1206 // is a validating transaction. In the latter case, it cannot become the
1207 // reader and will be added to the validated_queue when
1208 // DoneValidationWithEntry will be called for this transaction.
939 1209
940 entry->writer = NULL; 1210 // Entry is not completely written yet.
941 entry->readers.insert(trans); 1211 if (entry->writer && entry->writer != transaction)
1212 return false;
942 1213
943 ProcessPendingQueue(entry); 1214 entry->validating_transaction = nullptr;
1215 entry->writer = nullptr;
1216 entry->readers.insert(transaction);
1217 ProcessWaitingTransactions(entry);
1218 return true;
944 } 1219 }
945 1220
946 LoadState HttpCache::GetLoadStateForPendingTransaction( 1221 LoadState HttpCache::GetLoadStateForPendingTransaction(
947 const Transaction* trans) { 1222 const Transaction* trans) {
948 auto i = active_entries_.find(trans->key()); 1223 auto i = active_entries_.find(trans->key());
949 if (i == active_entries_.end()) { 1224 if (i == active_entries_.end()) {
950 // If this is really a pending transaction, and it is not part of 1225 // If this is really a pending transaction, and it is not part of
951 // active_entries_, we should be creating the backend or the entry. 1226 // active_entries_, we should be creating the backend or the entry.
952 return LOAD_STATE_WAITING_FOR_CACHE; 1227 return LOAD_STATE_WAITING_FOR_CACHE;
953 } 1228 }
(...skipping 29 matching lines...) Expand all
983 1258
984 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found; 1259 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found;
985 ++k) { 1260 ++k) {
986 found = RemovePendingTransactionFromEntry(k->first, trans); 1261 found = RemovePendingTransactionFromEntry(k->first, trans);
987 } 1262 }
988 1263
989 DCHECK(found) << "Pending transaction not found"; 1264 DCHECK(found) << "Pending transaction not found";
990 } 1265 }
991 1266
992 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 1267 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
993 Transaction* trans) { 1268 Transaction* transaction) {
994 TransactionList& pending_queue = entry->pending_queue; 1269 TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
995 1270
996 auto j = find(pending_queue.begin(), pending_queue.end(), trans); 1271 auto j =
997 if (j == pending_queue.end()) 1272 find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
1273 if (j == add_to_entry_queue.end())
998 return false; 1274 return false;
999 1275
1000 pending_queue.erase(j); 1276 add_to_entry_queue.erase(j);
1001 return true; 1277 return true;
1002 } 1278 }
1003 1279
1004 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, 1280 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
1005 Transaction* trans) { 1281 Transaction* trans) {
1006 if (pending_op->writer->Matches(trans)) { 1282 if (pending_op->writer->Matches(trans)) {
1007 pending_op->writer->ClearTransaction(); 1283 pending_op->writer->ClearTransaction();
1008 pending_op->writer->ClearEntry(); 1284 pending_op->writer->ClearEntry();
1009 return true; 1285 return true;
1010 } 1286 }
1011 WorkItemList& pending_queue = pending_op->pending_queue; 1287 WorkItemList& pending_queue = pending_op->pending_queue;
1012 1288
1013 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) { 1289 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) {
1014 if ((*it)->Matches(trans)) { 1290 if ((*it)->Matches(trans)) {
1015 pending_queue.erase(it); 1291 pending_queue.erase(it);
1016 return true; 1292 return true;
1017 } 1293 }
1018 } 1294 }
1019 return false; 1295 return false;
1020 } 1296 }
1021 1297
1022 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { 1298 void HttpCache::ProcessWaitingTransactions(ActiveEntry* entry) {
1023 // Multiple readers may finish with an entry at once, so we want to batch up 1299 // Multiple readers may finish with an entry at once, so we want to batch up
1024 // calls to OnProcessPendingQueue. This flag also tells us that we should 1300 // calls to OnProcessWaitingTransactions. This flag also tells us that we
1025 // not delete the entry before OnProcessPendingQueue runs. 1301 // should not delete the entry before OnProcessWaitingTransactions runs.
1026 if (entry->will_process_pending_queue) 1302 if (entry->will_process_waiting_transactions)
1027 return; 1303 return;
1028 entry->will_process_pending_queue = true;
1029 1304
1305 entry->will_process_waiting_transactions = true;
1306
1307 // Post a task instead of invoking the io callback of another transaction here
1308 // to avoid re-entrancy.
1030 base::ThreadTaskRunnerHandle::Get()->PostTask( 1309 base::ThreadTaskRunnerHandle::Get()->PostTask(
1031 FROM_HERE, 1310 FROM_HERE, base::Bind(&HttpCache::OnProcessWaitingTransactions,
1032 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); 1311 GetWeakPtr(), entry->disk_entry->GetKey(), entry));
1033 } 1312 }
1034 1313
1035 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { 1314 void HttpCache::OnProcessWaitingTransactions(const std::string& key,
1036 entry->will_process_pending_queue = false; 1315 ActiveEntry* entry) {
1037 DCHECK(!entry->writer); 1316 // Check if the entry is still alive either as an active entry or doomed
1317 // entry.
1318 if (!IsEntryAlive(key, entry))
1319 return;
1320
1321 entry->will_process_waiting_transactions = false;
1038 1322
1039 // If no one is interested in this entry, then we can deactivate it. 1323 // If no one is interested in this entry, then we can deactivate it.
1040 if (entry->HasNoTransactions()) { 1324 if (entry->HasNoTransactions()) {
1325 DestroyEntry(entry);
1326 return;
1327 }
1328
1329 if (entry->validated_queue.empty() && entry->add_to_entry_queue.empty())
1330 return;
1331
1332 Transaction* next = nullptr;
1333 TransactionList* list = nullptr;
1334 int rv = OK;
1335
1336 // To maintain FIFO order of transactions, validated_queue should be processed
1337 // first and then add_to_entry_queue.
1338 if (!entry->validated_queue.empty()) {
1339 next = entry->validated_queue.front();
1340 list = &entry->validated_queue;
1341 rv = AddTransactionToEntryForRead(entry, next);
1342 } else {
1343 // Promote next transaction from the pending queue.
1344 next = entry->add_to_entry_queue.front();
1345 list = &entry->add_to_entry_queue;
1346 rv = AddTransactionToEntryImpl(entry, next);
1347 }
1348
1349 if (rv != ERR_IO_PENDING) {
1350 list->erase(list->begin());
1351 next->io_callback().Run(rv);
1352 }
1353 }
1354
1355 void HttpCache::ProcessParallelValidation(ActiveEntry* entry) {
1356 if (entry->will_process_parallel_validation)
1357 return;
1358
1359 entry->will_process_parallel_validation = true;
1360
1361 // Post a task instead of invoking the io callback of another transaction here
1362 // to avoid re-entrancy.
1363 base::ThreadTaskRunnerHandle::Get()->PostTask(
1364 FROM_HERE, base::Bind(&HttpCache::OnProcessParallelValidation,
1365 GetWeakPtr(), entry->disk_entry->GetKey(), entry));
1366 }
1367
1368 void HttpCache::OnProcessParallelValidation(const std::string& key,
1369 ActiveEntry* entry) {
1370 // Check if the entry is still alive either as an active entry or doomed
1371 // entry.
1372 if (!IsEntryAlive(key, entry))
1373 return;
1374
1375 entry->will_process_parallel_validation = false;
1376
1377 // A ProcessWaitingTransactions task before this may add a
1378 // validating_transaction. In that case, do nothing.
1379 if (entry->validating_transaction)
1380 return;
1381
1382 // If no one is interested in this entry, then we can deactivate it.
1383 if (entry->HasNoTransactions()) {
1041 DestroyEntry(entry); 1384 DestroyEntry(entry);
1042 return; 1385 return;
1043 } 1386 }
1044 1387
1045 if (entry->pending_queue.empty()) 1388 if (entry->add_to_entry_queue.empty())
1046 return; 1389 return;
1047 1390
1048 // Promote next transaction from the pending queue. 1391 Transaction* next = entry->add_to_entry_queue.front();
1049 Transaction* next = entry->pending_queue.front();
1050 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1051 return; // Have to wait.
1052 1392
1053 entry->pending_queue.erase(entry->pending_queue.begin()); 1393 int rv = AddTransactionToEntryImpl(entry, next);
1054 1394
1055 int rv = AddTransactionToEntry(entry, next); 1395 // Read only transaction cannot validate the entry.
1396 if (!(next->mode() & Transaction::WRITE))
1397 return;
1398
1056 if (rv != ERR_IO_PENDING) { 1399 if (rv != ERR_IO_PENDING) {
1400 entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
1057 next->io_callback().Run(rv); 1401 next->io_callback().Run(rv);
1058 } 1402 }
1059 } 1403 }
1060 1404
1405 void HttpCache::DoomEntryRestartPendingTransactions(ActiveEntry* entry) {
1406 DoomActiveEntry(entry->disk_entry->GetKey());
1407 TransactionList list = GetAllPendingTransactions(entry);
1408 RestartPendingTransactions(list);
1409 }
1410
1411 void HttpCache::DestroyEntryRestartPendingTransactions(ActiveEntry* entry) {
1412 entry->disk_entry->Doom();
1413 TransactionList list = GetAllPendingTransactions(entry);
1414 DestroyEntry(entry);
1415 RestartPendingTransactions(list);
1416 }
1417
1418 HttpCache::TransactionList HttpCache::GetAllPendingTransactions(
1419 ActiveEntry* entry) {
1420 TransactionList list;
1421 for (auto* transaction : entry->validated_queue)
1422 list.push_back(transaction);
1423 entry->validated_queue.clear();
1424
1425 for (auto* transaction : entry->add_to_entry_queue)
1426 list.push_back(transaction);
1427 entry->add_to_entry_queue.clear();
1428
1429 return list;
1430 }
1431
1432 void HttpCache::RestartPendingTransactions(const TransactionList& list) {
1433 // ERR_CACHE_RACE causes the transaction to restart the whole process.
1434 for (auto* transaction : list)
1435 transaction->io_callback().Run(ERR_CACHE_RACE);
1436 }
1437
1061 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { 1438 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1062 WorkItemOperation op = pending_op->writer->operation(); 1439 WorkItemOperation op = pending_op->writer->operation();
1063 1440
1064 // Completing the creation of the backend is simpler than the other cases. 1441 // Completing the creation of the backend is simpler than the other cases.
1065 if (op == WI_CREATE_BACKEND) 1442 if (op == WI_CREATE_BACKEND)
1066 return OnBackendCreated(result, pending_op); 1443 return OnBackendCreated(result, pending_op);
1067 1444
1068 std::unique_ptr<WorkItem> item = std::move(pending_op->writer); 1445 std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
1069 bool fail_requests = false; 1446 bool fail_requests = false;
1070 1447
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
1195 building_backend_ = false; 1572 building_backend_ = false;
1196 DeletePendingOp(pending_op); 1573 DeletePendingOp(pending_op);
1197 } 1574 }
1198 1575
1199 // The cache may be gone when we return from the callback. 1576 // The cache may be gone when we return from the callback.
1200 if (!item->DoCallback(result, disk_cache_.get())) 1577 if (!item->DoCallback(result, disk_cache_.get()))
1201 item->NotifyTransaction(result, NULL); 1578 item->NotifyTransaction(result, NULL);
1202 } 1579 }
1203 1580
1204 } // namespace net 1581 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698