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

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

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: DoneReadingFromEntry replaced with DoneWithEntry Created 3 years, 7 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 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 true, 88 true,
89 thread_, 89 thread_,
90 net_log, 90 net_log,
91 backend, 91 backend,
92 callback); 92 callback);
93 } 93 }
94 94
95 //----------------------------------------------------------------------------- 95 //-----------------------------------------------------------------------------
96 96
97 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry) 97 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
98 : disk_entry(entry), 98 : disk_entry(entry) {}
99 writer(NULL),
100 will_process_pending_queue(false),
101 doomed(false) {
102 }
103 99
104 HttpCache::ActiveEntry::~ActiveEntry() { 100 HttpCache::ActiveEntry::~ActiveEntry() {
105 if (disk_entry) { 101 if (disk_entry) {
106 disk_entry->Close(); 102 disk_entry->Close();
107 disk_entry = NULL; 103 disk_entry = nullptr;
108 } 104 }
109 } 105 }
110 106
111 size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const { 107 size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const {
112 // Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers| 108 // Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers|
113 // and |pending_queue| because the Transactions are owned by their respective 109 // and |add_to_entry_queue| because the Transactions are owned by their
114 // URLRequestHttpJobs. 110 // respective URLRequestHttpJobs.
115 return 0; 111 return 0;
116 } 112 }
117 113
118 bool HttpCache::ActiveEntry::HasNoTransactions() { 114 bool HttpCache::ActiveEntry::HasNoTransactions() {
119 return !writer && readers.empty() && pending_queue.empty(); 115 return !writer && readers.empty() && add_to_entry_queue.empty() &&
116 done_headers_queue.empty() && !headers_transaction;
117 }
118
119 bool HttpCache::ActiveEntry::HasNoActiveTransactions() {
120 return !writer && readers.empty() && !headers_transaction;
120 } 121 }
121 122
122 //----------------------------------------------------------------------------- 123 //-----------------------------------------------------------------------------
123 124
124 // This structure keeps track of work items that are attempting to create or 125 // This structure keeps track of work items that are attempting to create or
125 // open cache entries or the backend itself. 126 // open cache entries or the backend itself.
126 struct HttpCache::PendingOp { 127 struct HttpCache::PendingOp {
127 PendingOp() : disk_entry(NULL) {} 128 PendingOp() : disk_entry(NULL) {}
128 ~PendingOp() {} 129 ~PendingOp() {}
129 130
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 disk_cache::Backend** backend_; 212 disk_cache::Backend** backend_;
212 }; 213 };
213 214
214 //----------------------------------------------------------------------------- 215 //-----------------------------------------------------------------------------
215 216
216 // This class encapsulates a transaction whose only purpose is to write metadata 217 // This class encapsulates a transaction whose only purpose is to write metadata
217 // to a given entry. 218 // to a given entry.
218 class HttpCache::MetadataWriter { 219 class HttpCache::MetadataWriter {
219 public: 220 public:
220 explicit MetadataWriter(HttpCache::Transaction* trans) 221 explicit MetadataWriter(HttpCache::Transaction* trans)
221 : transaction_(trans), 222 : verified_(false), buf_len_(0), transaction_(trans) {}
222 verified_(false),
223 buf_len_(0) {
224 }
225 223
226 ~MetadataWriter() {} 224 ~MetadataWriter() {}
227 225
228 // Implements the bulk of HttpCache::WriteMetadata. 226 // Implements the bulk of HttpCache::WriteMetadata.
229 void Write(const GURL& url, 227 void Write(const GURL& url,
230 base::Time expected_response_time, 228 base::Time expected_response_time,
231 IOBuffer* buf, 229 IOBuffer* buf,
232 int buf_len); 230 int buf_len);
233 231
234 private: 232 private:
235 void VerifyResponse(int result); 233 void VerifyResponse(int result);
236 void SelfDestroy(); 234 void SelfDestroy();
237 void OnIOComplete(int result); 235 void OnIOComplete(int result);
238 236
239 std::unique_ptr<HttpCache::Transaction> transaction_;
240 bool verified_; 237 bool verified_;
241 scoped_refptr<IOBuffer> buf_; 238 scoped_refptr<IOBuffer> buf_;
242 int buf_len_; 239 int buf_len_;
243 base::Time expected_response_time_; 240 base::Time expected_response_time_;
244 HttpRequestInfo request_info_; 241 HttpRequestInfo request_info_;
242
243 // |transaction_| to come after |request_info_| so that |request_info_| is not
244 // destroyed earlier.
245 std::unique_ptr<HttpCache::Transaction> transaction_;
245 DISALLOW_COPY_AND_ASSIGN(MetadataWriter); 246 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
246 }; 247 };
247 248
248 void HttpCache::MetadataWriter::Write(const GURL& url, 249 void HttpCache::MetadataWriter::Write(const GURL& url,
249 base::Time expected_response_time, 250 base::Time expected_response_time,
250 IOBuffer* buf, 251 IOBuffer* buf,
251 int buf_len) { 252 int buf_len) {
252 DCHECK_GT(buf_len, 0); 253 DCHECK_GT(buf_len, 0);
253 DCHECK(buf); 254 DCHECK(buf);
254 DCHECK(buf->data()); 255 DCHECK(buf->data());
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 session->SetServerPushDelegate( 335 session->SetServerPushDelegate(
335 base::MakeUnique<HttpCacheLookupManager>(this)); 336 base::MakeUnique<HttpCacheLookupManager>(this));
336 } 337 }
337 338
338 HttpCache::~HttpCache() { 339 HttpCache::~HttpCache() {
339 // Transactions should see an invalid cache after this point; otherwise they 340 // Transactions should see an invalid cache after this point; otherwise they
340 // could see an inconsistent object (half destroyed). 341 // could see an inconsistent object (half destroyed).
341 weak_factory_.InvalidateWeakPtrs(); 342 weak_factory_.InvalidateWeakPtrs();
342 343
343 // If we have any active entries remaining, then we need to deactivate them. 344 // If we have any active entries remaining, then we need to deactivate them.
344 // We may have some pending calls to OnProcessPendingQueue, but since those 345 // We may have some pending tasks to process queued transactions ,but since
345 // won't run (due to our destruction), we can simply ignore the corresponding 346 // those won't run (due to our destruction), we can simply ignore the
346 // will_process_pending_queue flag. 347 // corresponding flags.
347 while (!active_entries_.empty()) { 348 while (!active_entries_.empty()) {
348 ActiveEntry* entry = active_entries_.begin()->second.get(); 349 ActiveEntry* entry = active_entries_.begin()->second.get();
349 entry->will_process_pending_queue = false; 350 entry->will_process_queued_transactions = false;
350 entry->pending_queue.clear(); 351 entry->add_to_entry_queue.clear();
351 entry->readers.clear(); 352 entry->readers.clear();
352 entry->writer = NULL; 353 entry->done_headers_queue.clear();
354 entry->headers_transaction = nullptr;
355 entry->writer = nullptr;
353 DeactivateEntry(entry); 356 DeactivateEntry(entry);
354 } 357 }
355 358
356 doomed_entries_.clear(); 359 doomed_entries_.clear();
357 360
358 // Before deleting pending_ops_, we have to make sure that the disk cache is 361 // Before deleting pending_ops_, we have to make sure that the disk cache is
359 // done with said operations, or it will attempt to use deleted data. 362 // done with said operations, or it will attempt to use deleted data.
360 disk_cache_.reset(); 363 disk_cache_.reset();
361 364
362 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end(); 365 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end();
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 // We keep track of doomed entries so that we can ensure that they are 606 // We keep track of doomed entries so that we can ensure that they are
604 // cleaned up properly when the cache is destroyed. 607 // cleaned up properly when the cache is destroyed.
605 ActiveEntry* entry_ptr = entry.get(); 608 ActiveEntry* entry_ptr = entry.get();
606 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr)); 609 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr));
607 doomed_entries_[entry_ptr] = std::move(entry); 610 doomed_entries_[entry_ptr] = std::move(entry);
608 611
609 entry_ptr->disk_entry->Doom(); 612 entry_ptr->disk_entry->Doom();
610 entry_ptr->doomed = true; 613 entry_ptr->doomed = true;
611 614
612 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() || 615 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
613 entry_ptr->will_process_pending_queue); 616 entry_ptr->headers_transaction ||
617 entry_ptr->will_process_queued_transactions);
614 return OK; 618 return OK;
615 } 619 }
616 620
617 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { 621 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
618 std::unique_ptr<WorkItem> item = 622 std::unique_ptr<WorkItem> item =
619 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr); 623 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr);
620 PendingOp* pending_op = GetPendingOp(key); 624 PendingOp* pending_op = GetPendingOp(key);
621 if (pending_op->writer) { 625 if (pending_op->writer) {
622 pending_op->pending_queue.push_back(std::move(item)); 626 pending_op->pending_queue.push_back(std::move(item));
623 return ERR_IO_PENDING; 627 return ERR_IO_PENDING;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
659 DCHECK(entry->doomed); 663 DCHECK(entry->doomed);
660 DCHECK(entry->HasNoTransactions()); 664 DCHECK(entry->HasNoTransactions());
661 665
662 auto it = doomed_entries_.find(entry); 666 auto it = doomed_entries_.find(entry);
663 DCHECK(it != doomed_entries_.end()); 667 DCHECK(it != doomed_entries_.end());
664 doomed_entries_.erase(it); 668 doomed_entries_.erase(it);
665 } 669 }
666 670
667 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 671 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
668 auto it = active_entries_.find(key); 672 auto it = active_entries_.find(key);
669 return it != active_entries_.end() ? it->second.get() : NULL; 673 return it != active_entries_.end() ? it->second.get() : nullptr;
670 } 674 }
671 675
672 HttpCache::ActiveEntry* HttpCache::ActivateEntry( 676 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
673 disk_cache::Entry* disk_entry) { 677 disk_cache::Entry* disk_entry) {
674 DCHECK(!FindActiveEntry(disk_entry->GetKey())); 678 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
675 ActiveEntry* entry = new ActiveEntry(disk_entry); 679 ActiveEntry* entry = new ActiveEntry(disk_entry);
676 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry); 680 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry);
677 return entry; 681 return entry;
678 } 682 }
679 683
680 void HttpCache::DeactivateEntry(ActiveEntry* entry) { 684 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
681 DCHECK(!entry->will_process_pending_queue); 685 DCHECK(!entry->will_process_queued_transactions);
682 DCHECK(!entry->doomed); 686 DCHECK(!entry->doomed);
683 DCHECK(entry->disk_entry); 687 DCHECK(entry->disk_entry);
684 DCHECK(entry->HasNoTransactions()); 688 DCHECK(entry->HasNoTransactions());
685 689
686 std::string key = entry->disk_entry->GetKey(); 690 std::string key = entry->disk_entry->GetKey();
687 if (key.empty()) 691 if (key.empty())
688 return SlowDeactivateEntry(entry); 692 return SlowDeactivateEntry(entry);
689 693
690 auto it = active_entries_.find(key); 694 auto it = active_entries_.find(key);
691 DCHECK(it != active_entries_.end()); 695 DCHECK(it != active_entries_.end());
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
801 } 805 }
802 806
803 void HttpCache::DestroyEntry(ActiveEntry* entry) { 807 void HttpCache::DestroyEntry(ActiveEntry* entry) {
804 if (entry->doomed) { 808 if (entry->doomed) {
805 FinalizeDoomedEntry(entry); 809 FinalizeDoomedEntry(entry);
806 } else { 810 } else {
807 DeactivateEntry(entry); 811 DeactivateEntry(entry);
808 } 812 }
809 } 813 }
810 814
811 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 815 int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
816 Transaction* transaction) {
812 DCHECK(entry); 817 DCHECK(entry);
813 DCHECK(entry->disk_entry); 818 DCHECK(entry->disk_entry);
814 819 // Always add a new transaction to the queue to maintain FIFO order.
815 // We implement a basic reader/writer lock for the disk cache entry. If 820 entry->add_to_entry_queue.push_back(transaction);
816 // there is already a writer, then everyone has to wait for the writer to 821 ProcessQueuedTransactions(entry);
817 // finish before they can access the cache entry. There can be multiple 822 return ERR_IO_PENDING;
818 // readers. 823 }
819 // 824
820 // NOTE: If the transaction can only write, then the entry should not be in 825 int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
821 // use (since any existing entry should have already been doomed). 826 Transaction* transaction,
822 827 bool is_partial) {
823 if (entry->writer || entry->will_process_pending_queue) { 828 // If |transaction| is the current writer, do nothing. This can happen for
824 entry->pending_queue.push_back(trans); 829 // range requests since they can go back to headers phase after starting to
825 return ERR_IO_PENDING; 830 // write.
826 } 831 if (entry->writer == transaction)
827 832 return OK;
828 if (trans->mode() & Transaction::WRITE) { 833
829 // transaction needs exclusive access to the entry 834 // TODO(shivanisha): Convert the CHECKs in this function to DCHECKs once
830 if (entry->readers.empty()) { 835 // canary is clear of any crashes.
831 entry->writer = trans; 836 CHECK_EQ(entry->headers_transaction, transaction);
832 } else { 837
833 entry->pending_queue.push_back(trans); 838 entry->headers_transaction = nullptr;
834 return ERR_IO_PENDING; 839
840 // If transaction is responsible for writing the response body, then do not go
841 // through done_headers_queue for performance benefit. (Also, in case of
842 // writer transaction, the consumer sometimes depend on synchronous behaviour
843 // e.g. while computing raw headers size. (crbug.com/711766))
844 if (transaction->mode() & Transaction::WRITE) {
845 // Partial requests may have write mode even when there is a writer present
846 // since they may be reader for a particular range and writer for another
847 // range.
848 CHECK(is_partial || (!entry->writer && entry->done_headers_queue.empty()));
849
850 if (!entry->writer) {
851 entry->writer = transaction;
852 ProcessQueuedTransactions(entry);
853 return OK;
835 } 854 }
836 } else { 855 }
837 // transaction needs read access to the entry 856
838 entry->readers.insert(trans); 857 // If this is not the first transaction in done_headers_queue, it should be a
839 } 858 // read-mode transaction except if it is a partial request.
840 859 CHECK(is_partial || (entry->done_headers_queue.empty() ||
841 // We do this before calling EntryAvailable to force any further calls to 860 !(transaction->mode() & Transaction::WRITE)));
842 // AddTransactionToEntry to add their transaction to the pending queue, which 861
843 // ensures FIFO ordering. 862 entry->done_headers_queue.push_back(transaction);
844 if (!entry->writer && !entry->pending_queue.empty()) 863 ProcessQueuedTransactions(entry);
845 ProcessPendingQueue(entry); 864 return ERR_IO_PENDING;
846 865 }
847 return OK; 866
848 } 867 void HttpCache::DoneWithEntry(ActiveEntry* entry,
Randy Smith (Not in Mondays) 2017/05/21 21:32:02 Suggestion: Given that this routine appears to do
shivanisha 2017/05/26 16:26:17 I am inclined to leave the last call in HttpCache:
849 868 Transaction* transaction,
850 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 869 bool process_cancel,
851 bool cancel) { 870 bool is_partial) {
852 // If we already posted a task to move on to the next transaction and this was 871 bool cancel = false;
853 // the writer, there is nothing to cancel. 872 if (process_cancel) {
854 if (entry->will_process_pending_queue && entry->readers.empty()) 873 cancel = IsTransactionExclusivelyWriting(entry, transaction, is_partial);
855 return; 874 if (cancel && is_partial)
856 875 entry->disk_entry->CancelSparseIO();
857 if (entry->writer) { 876 }
858 DCHECK(trans == entry->writer); 877
859 878 // Transaction is waiting in the done_headers_queue.
879 auto it = std::find(entry->done_headers_queue.begin(),
880 entry->done_headers_queue.end(), transaction);
881 if (it != entry->done_headers_queue.end()) {
882 entry->done_headers_queue.erase(it);
883 if (cancel)
884 ProcessEntryFailure(entry);
885 return;
886 }
887
888 // Transaction is removed in the headers phase.
889 if (transaction == entry->headers_transaction) {
890 // If the response is not written (cancel is true), consider it a failure.
891 DoneWritingToEntry(entry, !cancel, transaction);
892 return;
893 }
894
895 // Transaction is removed in the writing phase.
896 if (transaction == entry->writer) {
860 // Assume there was a failure. 897 // Assume there was a failure.
861 bool success = false; 898 bool success = false;
862 if (cancel) { 899 if (cancel) {
863 DCHECK(entry->disk_entry); 900 DCHECK(entry->disk_entry);
864 // This is a successful operation in the sense that we want to keep the 901 // This is a successful operation in the sense that we want to keep the
865 // entry. 902 // entry.
866 success = trans->AddTruncatedFlag(); 903 success = transaction->AddTruncatedFlag();
867 // The previous operation may have deleted the entry. 904 // The previous operation may have deleted the entry.
868 if (!trans->entry()) 905 if (!transaction->entry())
869 return; 906 return;
870 } 907 }
871 DoneWritingToEntry(entry, success); 908 DoneWritingToEntry(entry, success, transaction);
909 return;
910 }
911
912 // Transaction is reading from the entry.
913 DoneReadingFromEntry(entry, transaction);
Randy Smith (Not in Mondays) 2017/05/21 21:32:02 Suggestion, low priority: Is it possible to put a
shivanisha 2017/05/26 16:26:17 I am slightly nervous about putting that DCHECK he
914 }
915
916 void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
917 bool success,
918 Transaction* transaction) {
919 DCHECK(transaction == entry->writer ||
920 transaction == entry->headers_transaction);
921
922 if (transaction == entry->writer)
923 entry->writer = nullptr;
924 else
925 entry->headers_transaction = nullptr;
926
927 // If writer fails, restart the headers_transaction by setting its state.
928 // Since the headers_transactions is awaiting an asynchronous operation
929 // completion, when it's IO callback is invoked, it will be restarted.
930 if (!success && entry->headers_transaction) {
931 entry->headers_transaction->SetValidatingCannotProceed();
932 entry->headers_transaction = nullptr;
933 DCHECK(entry->HasNoActiveTransactions());
934 }
935 if (!success)
936 ProcessEntryFailure(entry);
937 else
938 ProcessQueuedTransactions(entry);
939 }
940
941 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry,
942 Transaction* transaction) {
943 DCHECK(!entry->writer);
944 auto it = entry->readers.find(transaction);
945 DCHECK(it != entry->readers.end());
946 entry->readers.erase(it);
947
948 ProcessQueuedTransactions(entry);
949 }
950
951 void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
952 TransactionList* list) {
953 // Process done_headers_queue before add_to_entry_queue to maintain FIFO
954 // order.
955 for (auto* transaction : entry->done_headers_queue)
956 list->push_back(transaction);
957 entry->done_headers_queue.clear();
958
959 for (auto* transaction : entry->add_to_entry_queue)
960 list->push_back(transaction);
961 entry->add_to_entry_queue.clear();
962 }
963
964 void HttpCache::ProcessEntryFailure(ActiveEntry* entry) {
965 // Failure case is either writer failing to completely write the response to
966 // the cache or validating transaction received a non-304 response.
967 TransactionList list;
968 if (entry->HasNoActiveTransactions() &&
969 !entry->will_process_queued_transactions) {
970 entry->disk_entry->Doom();
971 RemoveAllQueuedTransactions(entry, &list);
972 DestroyEntry(entry);
872 } else { 973 } else {
873 DoneReadingFromEntry(entry, trans); 974 DoomActiveEntry(entry->disk_entry->GetKey());
874 } 975 RemoveAllQueuedTransactions(entry, &list);
875 } 976 }
876 977 // ERR_CACHE_RACE causes the transaction to restart the whole process.
877 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 978 for (auto* transaction : list)
878 DCHECK(entry->readers.empty()); 979 transaction->io_callback().Run(net::ERR_CACHE_RACE);
879 980 }
880 entry->writer = NULL; 981
881 982 void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
882 if (success) { 983 // Multiple readers may finish with an entry at once, so we want to batch up
883 ProcessPendingQueue(entry); 984 // calls to OnProcessQueuedTransactions. This flag also tells us that we
985 // should not delete the entry before OnProcessQueuedTransactions runs.
986 if (entry->will_process_queued_transactions)
987 return;
988
989 entry->will_process_queued_transactions = true;
990
991 // Post a task instead of invoking the io callback of another transaction here
992 // to avoid re-entrancy.
993 base::ThreadTaskRunnerHandle::Get()->PostTask(
994 FROM_HERE,
995 base::Bind(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(), entry));
996 }
997
998 void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
999 DCHECK(!entry->add_to_entry_queue.empty());
1000
1001 // Note the entry may be new or may already have a response body written to
1002 // it. In both cases, a transaction needs to wait since only one transaction
1003 // can be in the headers phase at a time.
1004 if (entry->headers_transaction) {
1005 return;
1006 }
1007 Transaction* transaction = entry->add_to_entry_queue.front();
1008 entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
1009 entry->headers_transaction = transaction;
1010
1011 transaction->io_callback().Run(OK);
1012 }
1013
1014 void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
1015 DCHECK(!entry->writer);
1016 DCHECK(!entry->done_headers_queue.empty());
1017
1018 Transaction* transaction = entry->done_headers_queue.front();
1019
1020 // If this transaction is responsible for writing the response body.
1021 if (transaction->mode() & Transaction::WRITE) {
1022 entry->writer = transaction;
884 } else { 1023 } else {
885 DCHECK(!entry->will_process_pending_queue); 1024 // If a transaction is in front of this queue with only read mode set and
886 1025 // there is no writer, it implies response body is already written, convert
887 // We failed to create this entry. 1026 // to a reader.
888 TransactionList pending_queue; 1027 auto return_val = entry->readers.insert(transaction);
889 pending_queue.swap(entry->pending_queue); 1028 DCHECK_EQ(return_val.second, true);
890 1029 }
891 entry->disk_entry->Doom(); 1030
892 DestroyEntry(entry); 1031 // Post another task to give a chance to more transactions to either join
893 1032 // readers or another transaction to start parallel validation.
894 // We need to do something about these pending entries, which now need to 1033 ProcessQueuedTransactions(entry);
895 // be added to a new entry. 1034
896 while (!pending_queue.empty()) { 1035 entry->done_headers_queue.erase(entry->done_headers_queue.begin());
897 // ERR_CACHE_RACE causes the transaction to restart the whole process. 1036 transaction->io_callback().Run(OK);
898 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE); 1037 }
899 pending_queue.pop_front(); 1038
900 } 1039 bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
901 } 1040 Transaction* transaction,
902 } 1041 bool is_match) const {
903 1042 if (transaction != entry->headers_transaction)
904 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { 1043 return false;
905 DCHECK(!entry->writer); 1044
906 1045 if (!(transaction->mode() & Transaction::WRITE))
907 auto it = entry->readers.find(trans); 1046 return false;
908 DCHECK(it != entry->readers.end()); 1047
909 1048 // If its not a match then check if it is the transaction responsible for
910 entry->readers.erase(it); 1049 // writing the response body.
911 1050 if (!is_match) {
912 ProcessPendingQueue(entry); 1051 return !entry->writer && entry->done_headers_queue.empty() &&
913 } 1052 entry->readers.empty();
914 1053 }
915 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { 1054
916 DCHECK(entry->writer); 1055 return true;
917 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); 1056 }
918 DCHECK(entry->readers.empty()); 1057
919 1058 bool HttpCache::IsTransactionExclusivelyWriting(ActiveEntry* entry,
920 Transaction* trans = entry->writer; 1059 Transaction* transaction,
921 1060 bool is_partial) const {
922 entry->writer = NULL; 1061 if (transaction == entry->writer)
923 entry->readers.insert(trans); 1062 return true;
924 1063
925 ProcessPendingQueue(entry); 1064 if (transaction->method() == "HEAD" || transaction->method() == "DELETE")
1065 return false;
1066
1067 // Check if transaction is about to start writing to the cache.
1068
1069 // Transaction's mode may have been set to NONE if StopCaching was invoked.
1070 if (!(transaction->mode() & Transaction::WRITE ||
1071 transaction->mode() == Transaction::NONE)) {
1072 return false;
1073 }
1074
1075 if (!is_partial) {
1076 // If a transaction is completing headers or done with headers phase with
1077 // write mode then it should be the future writer. Just checking the front
1078 // of done_headers_queue since the rest should anyways be READ mode
1079 // transactions as they would be a result of validation match (for non-range
1080 // requests).
1081 return transaction == entry->headers_transaction ||
1082 transaction == entry->done_headers_queue.front();
1083 }
1084
1085 // Range requests specific cases.
1086
1087 // For range requests, it is possible to have WRITE mode set even if another
1088 // transaction is the writer.
1089 if (entry->writer)
1090 return false;
1091
1092 if (transaction == entry->headers_transaction ||
1093 transaction == entry->done_headers_queue.front()) {
1094 return true;
1095 }
1096
1097 // If transaction is not in front of done_headers_queue, see if its the first
1098 // writer transaction in the queue.
1099 for (auto* done_headers : entry->done_headers_queue) {
1100 if (transaction == done_headers)
1101 return true;
1102 if (done_headers->mode() & Transaction::WRITE)
1103 return false;
1104 }
1105
1106 return false;
1107 }
1108
1109 bool HttpCache::IsWritingInProgress(ActiveEntry* entry) const {
1110 return entry->writer != nullptr;
926 } 1111 }
927 1112
928 LoadState HttpCache::GetLoadStateForPendingTransaction( 1113 LoadState HttpCache::GetLoadStateForPendingTransaction(
929 const Transaction* trans) { 1114 const Transaction* trans) {
930 auto i = active_entries_.find(trans->key()); 1115 auto i = active_entries_.find(trans->key());
931 if (i == active_entries_.end()) { 1116 if (i == active_entries_.end()) {
932 // If this is really a pending transaction, and it is not part of 1117 // If this is really a pending transaction, and it is not part of
933 // active_entries_, we should be creating the backend or the entry. 1118 // active_entries_, we should be creating the backend or the entry.
934 return LOAD_STATE_WAITING_FOR_CACHE; 1119 return LOAD_STATE_WAITING_FOR_CACHE;
935 } 1120 }
(...skipping 29 matching lines...) Expand all
965 1150
966 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found; 1151 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found;
967 ++k) { 1152 ++k) {
968 found = RemovePendingTransactionFromEntry(k->first, trans); 1153 found = RemovePendingTransactionFromEntry(k->first, trans);
969 } 1154 }
970 1155
971 DCHECK(found) << "Pending transaction not found"; 1156 DCHECK(found) << "Pending transaction not found";
972 } 1157 }
973 1158
974 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 1159 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
975 Transaction* trans) { 1160 Transaction* transaction) {
976 TransactionList& pending_queue = entry->pending_queue; 1161 TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
977 1162
978 auto j = find(pending_queue.begin(), pending_queue.end(), trans); 1163 auto j =
979 if (j == pending_queue.end()) 1164 find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
1165 if (j == add_to_entry_queue.end())
980 return false; 1166 return false;
981 1167
982 pending_queue.erase(j); 1168 add_to_entry_queue.erase(j);
983 return true; 1169 return true;
984 } 1170 }
985 1171
986 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, 1172 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
987 Transaction* trans) { 1173 Transaction* trans) {
988 if (pending_op->writer->Matches(trans)) { 1174 if (pending_op->writer->Matches(trans)) {
989 pending_op->writer->ClearTransaction(); 1175 pending_op->writer->ClearTransaction();
990 pending_op->writer->ClearEntry(); 1176 pending_op->writer->ClearEntry();
991 return true; 1177 return true;
992 } 1178 }
993 WorkItemList& pending_queue = pending_op->pending_queue; 1179 WorkItemList& pending_queue = pending_op->pending_queue;
994 1180
995 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) { 1181 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) {
996 if ((*it)->Matches(trans)) { 1182 if ((*it)->Matches(trans)) {
997 pending_queue.erase(it); 1183 pending_queue.erase(it);
998 return true; 1184 return true;
999 } 1185 }
1000 } 1186 }
1001 return false; 1187 return false;
1002 } 1188 }
1003 1189
1004 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { 1190 void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
1005 // Multiple readers may finish with an entry at once, so we want to batch up 1191 entry->will_process_queued_transactions = false;
1006 // calls to OnProcessPendingQueue. This flag also tells us that we should
1007 // not delete the entry before OnProcessPendingQueue runs.
1008 if (entry->will_process_pending_queue)
1009 return;
1010 entry->will_process_pending_queue = true;
1011 1192
1012 base::ThreadTaskRunnerHandle::Get()->PostTask( 1193 // Note that this function should only invoke one transaction's IO callback
1013 FROM_HERE, 1194 // since its possible for IO callbacks' consumers to destroy the cache/entry.
1014 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1015 }
1016
1017 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1018 entry->will_process_pending_queue = false;
1019 DCHECK(!entry->writer);
1020 1195
1021 // If no one is interested in this entry, then we can deactivate it. 1196 // If no one is interested in this entry, then we can deactivate it.
1022 if (entry->HasNoTransactions()) { 1197 if (entry->HasNoTransactions()) {
1023 DestroyEntry(entry); 1198 DestroyEntry(entry);
1024 return; 1199 return;
1025 } 1200 }
1026 1201
1027 if (entry->pending_queue.empty()) 1202 if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty())
1028 return; 1203 return;
1029 1204
1030 // Promote next transaction from the pending queue. 1205 // To maintain FIFO order of transactions, done_headers_queue should be
1031 Transaction* next = entry->pending_queue.front(); 1206 // checked for processing before add_to_entry_queue.
1032 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1033 return; // Have to wait.
1034 1207
1035 entry->pending_queue.erase(entry->pending_queue.begin()); 1208 // If another transaction is writing the response, let validated transactions
1209 // wait till the response is complete. If the response is not yet started, the
1210 // done_headers_queue transaction should start writing it.
1211 if (!entry->writer && !entry->done_headers_queue.empty()) {
1212 ProcessDoneHeadersQueue(entry);
1213 return;
1214 }
1036 1215
1037 int rv = AddTransactionToEntry(entry, next); 1216 if (!entry->add_to_entry_queue.empty())
1038 if (rv != ERR_IO_PENDING) { 1217 ProcessAddToEntryQueue(entry);
1039 next->io_callback().Run(rv);
1040 }
1041 } 1218 }
1042 1219
1043 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { 1220 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1044 WorkItemOperation op = pending_op->writer->operation(); 1221 WorkItemOperation op = pending_op->writer->operation();
1045 1222
1046 // Completing the creation of the backend is simpler than the other cases. 1223 // Completing the creation of the backend is simpler than the other cases.
1047 if (op == WI_CREATE_BACKEND) 1224 if (op == WI_CREATE_BACKEND)
1048 return OnBackendCreated(result, pending_op); 1225 return OnBackendCreated(result, pending_op);
1049 1226
1050 std::unique_ptr<WorkItem> item = std::move(pending_op->writer); 1227 std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 building_backend_ = false; 1354 building_backend_ = false;
1178 DeletePendingOp(pending_op); 1355 DeletePendingOp(pending_op);
1179 } 1356 }
1180 1357
1181 // The cache may be gone when we return from the callback. 1358 // The cache may be gone when we return from the callback.
1182 if (!item->DoCallback(result, disk_cache_.get())) 1359 if (!item->DoCallback(result, disk_cache_.get()))
1183 item->NotifyTransaction(result, NULL); 1360 item->NotifyTransaction(result, NULL);
1184 } 1361 }
1185 1362
1186 } // namespace net 1363 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698