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

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

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: Feedback addressed 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 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 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 session->SetServerPushDelegate( 336 session->SetServerPushDelegate(
336 base::MakeUnique<HttpCacheLookupManager>(this)); 337 base::MakeUnique<HttpCacheLookupManager>(this));
337 } 338 }
338 339
339 HttpCache::~HttpCache() { 340 HttpCache::~HttpCache() {
340 // Transactions should see an invalid cache after this point; otherwise they 341 // Transactions should see an invalid cache after this point; otherwise they
341 // could see an inconsistent object (half destroyed). 342 // could see an inconsistent object (half destroyed).
342 weak_factory_.InvalidateWeakPtrs(); 343 weak_factory_.InvalidateWeakPtrs();
343 344
344 // If we have any active entries remaining, then we need to deactivate them. 345 // If we have any active entries remaining, then we need to deactivate them.
345 // We may have some pending calls to OnProcessPendingQueue, but since those 346 // We may have some pending tasks to process queued transactions ,but since
346 // won't run (due to our destruction), we can simply ignore the corresponding 347 // those won't run (due to our destruction), we can simply ignore the
347 // will_process_pending_queue flag. 348 // corresponding flags.
348 while (!active_entries_.empty()) { 349 while (!active_entries_.empty()) {
349 ActiveEntry* entry = active_entries_.begin()->second.get(); 350 ActiveEntry* entry = active_entries_.begin()->second.get();
350 entry->will_process_pending_queue = false; 351 entry->will_process_queued_transactions = false;
351 entry->pending_queue.clear(); 352 entry->add_to_entry_queue.clear();
352 entry->readers.clear(); 353 entry->readers.clear();
353 entry->writer = NULL; 354 entry->done_headers_queue.clear();
355 entry->headers_transaction = nullptr;
356 entry->writer = nullptr;
354 DeactivateEntry(entry); 357 DeactivateEntry(entry);
355 } 358 }
356 359
357 doomed_entries_.clear(); 360 doomed_entries_.clear();
358 361
359 // Before deleting pending_ops_, we have to make sure that the disk cache is 362 // Before deleting pending_ops_, we have to make sure that the disk cache is
360 // done with said operations, or it will attempt to use deleted data. 363 // done with said operations, or it will attempt to use deleted data.
361 disk_cache_.reset(); 364 disk_cache_.reset();
362 365
363 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end(); 366 for (auto pending_it = pending_ops_.begin(); pending_it != pending_ops_.end();
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 // We keep track of doomed entries so that we can ensure that they are 607 // We keep track of doomed entries so that we can ensure that they are
605 // cleaned up properly when the cache is destroyed. 608 // cleaned up properly when the cache is destroyed.
606 ActiveEntry* entry_ptr = entry.get(); 609 ActiveEntry* entry_ptr = entry.get();
607 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr)); 610 DCHECK_EQ(0u, doomed_entries_.count(entry_ptr));
608 doomed_entries_[entry_ptr] = std::move(entry); 611 doomed_entries_[entry_ptr] = std::move(entry);
609 612
610 entry_ptr->disk_entry->Doom(); 613 entry_ptr->disk_entry->Doom();
611 entry_ptr->doomed = true; 614 entry_ptr->doomed = true;
612 615
613 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() || 616 DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
614 entry_ptr->will_process_pending_queue); 617 entry_ptr->headers_transaction ||
618 entry_ptr->will_process_queued_transactions);
615 return OK; 619 return OK;
616 } 620 }
617 621
618 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { 622 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
619 std::unique_ptr<WorkItem> item = 623 std::unique_ptr<WorkItem> item =
620 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr); 624 base::MakeUnique<WorkItem>(WI_DOOM_ENTRY, trans, nullptr);
621 PendingOp* pending_op = GetPendingOp(key); 625 PendingOp* pending_op = GetPendingOp(key);
622 if (pending_op->writer) { 626 if (pending_op->writer) {
623 pending_op->pending_queue.push_back(std::move(item)); 627 pending_op->pending_queue.push_back(std::move(item));
624 return ERR_IO_PENDING; 628 return ERR_IO_PENDING;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
660 DCHECK(entry->doomed); 664 DCHECK(entry->doomed);
661 DCHECK(entry->HasNoTransactions()); 665 DCHECK(entry->HasNoTransactions());
662 666
663 auto it = doomed_entries_.find(entry); 667 auto it = doomed_entries_.find(entry);
664 DCHECK(it != doomed_entries_.end()); 668 DCHECK(it != doomed_entries_.end());
665 doomed_entries_.erase(it); 669 doomed_entries_.erase(it);
666 } 670 }
667 671
668 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 672 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
669 auto it = active_entries_.find(key); 673 auto it = active_entries_.find(key);
670 return it != active_entries_.end() ? it->second.get() : NULL; 674 return it != active_entries_.end() ? it->second.get() : nullptr;
671 } 675 }
672 676
673 HttpCache::ActiveEntry* HttpCache::ActivateEntry( 677 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
674 disk_cache::Entry* disk_entry) { 678 disk_cache::Entry* disk_entry) {
675 DCHECK(!FindActiveEntry(disk_entry->GetKey())); 679 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
676 ActiveEntry* entry = new ActiveEntry(disk_entry); 680 ActiveEntry* entry = new ActiveEntry(disk_entry);
677 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry); 681 active_entries_[disk_entry->GetKey()] = base::WrapUnique(entry);
678 return entry; 682 return entry;
679 } 683 }
680 684
681 void HttpCache::DeactivateEntry(ActiveEntry* entry) { 685 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
682 DCHECK(!entry->will_process_pending_queue); 686 DCHECK(!entry->will_process_queued_transactions);
683 DCHECK(!entry->doomed); 687 DCHECK(!entry->doomed);
684 DCHECK(entry->disk_entry); 688 DCHECK(entry->disk_entry);
685 DCHECK(entry->HasNoTransactions()); 689 DCHECK(entry->HasNoTransactions());
686 690
687 std::string key = entry->disk_entry->GetKey(); 691 std::string key = entry->disk_entry->GetKey();
688 if (key.empty()) 692 if (key.empty())
689 return SlowDeactivateEntry(entry); 693 return SlowDeactivateEntry(entry);
690 694
691 auto it = active_entries_.find(key); 695 auto it = active_entries_.find(key);
692 DCHECK(it != active_entries_.end()); 696 DCHECK(it != active_entries_.end());
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
802 } 806 }
803 807
804 void HttpCache::DestroyEntry(ActiveEntry* entry) { 808 void HttpCache::DestroyEntry(ActiveEntry* entry) {
805 if (entry->doomed) { 809 if (entry->doomed) {
806 FinalizeDoomedEntry(entry); 810 FinalizeDoomedEntry(entry);
807 } else { 811 } else {
808 DeactivateEntry(entry); 812 DeactivateEntry(entry);
809 } 813 }
810 } 814 }
811 815
812 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 816 int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
817 Transaction* transaction) {
813 DCHECK(entry); 818 DCHECK(entry);
814 DCHECK(entry->disk_entry); 819 DCHECK(entry->disk_entry);
815 820 // Always add a new transaction to the queue to maintain FIFO order.
816 // We implement a basic reader/writer lock for the disk cache entry. If 821 entry->add_to_entry_queue.push_back(transaction);
817 // there is already a writer, then everyone has to wait for the writer to 822 ProcessQueuedTransactions(entry);
818 // finish before they can access the cache entry. There can be multiple 823 return ERR_IO_PENDING;
819 // readers. 824 }
820 // 825
821 // NOTE: If the transaction can only write, then the entry should not be in 826 int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
822 // use (since any existing entry should have already been doomed). 827 Transaction* transaction) {
823 828 // If |transaction| is the current writer, do nothing. This can happen for
824 if (entry->writer || entry->will_process_pending_queue) { 829 // range requests since they can go back to headers phase after starting to
825 entry->pending_queue.push_back(trans); 830 // write.
826 return ERR_IO_PENDING; 831 if (entry->writer == transaction)
827 } 832 return OK;
828 833
829 if (trans->mode() & Transaction::WRITE) { 834 DCHECK_EQ(entry->headers_transaction, transaction);
830 // transaction needs exclusive access to the entry 835
831 if (entry->readers.empty()) { 836 entry->headers_transaction = nullptr;
832 entry->writer = trans; 837
833 } else { 838 // If transaction is responsible for writing the response body, then do not go
834 entry->pending_queue.push_back(trans); 839 // through done_headers_queue for performance benefit. (Also, in case of
835 return ERR_IO_PENDING; 840 // writer transaction, the consumer sometimes depend on synchronous behaviour
836 } 841 // e.g. while computing raw headers size. (crbug.com/711766))
837 } else { 842 if (transaction->mode() & Transaction::WRITE) {
838 // transaction needs read access to the entry 843 DCHECK(entry->done_headers_queue.empty());
839 entry->readers.insert(trans); 844 DCHECK(!entry->writer);
840 } 845 entry->writer = transaction;
841 846 ProcessQueuedTransactions(entry);
842 // We do this before calling EntryAvailable to force any further calls to 847 return OK;
843 // AddTransactionToEntry to add their transaction to the pending queue, which 848 }
844 // ensures FIFO ordering. 849
845 if (!entry->writer && !entry->pending_queue.empty()) 850 // If this is not the first transaction in done_headers_queue, it should be a
846 ProcessPendingQueue(entry); 851 // read-mode transaction.
847 852 DCHECK(entry->done_headers_queue.empty() ||
848 return OK; 853 !(transaction->mode() & Transaction::WRITE));
849 } 854
850 855 entry->done_headers_queue.push_back(transaction);
851 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 856 ProcessQueuedTransactions(entry);
857 return ERR_IO_PENDING;
858 }
859
860 void HttpCache::DoneWithEntry(ActiveEntry* entry,
861 Transaction* transaction,
852 bool cancel) { 862 bool cancel) {
853 // If we already posted a task to move on to the next transaction and this was 863 // Transaction is waiting in the done_headers_queue.
854 // the writer, there is nothing to cancel. 864 auto it = std::find(entry->done_headers_queue.begin(),
855 if (entry->will_process_pending_queue && entry->readers.empty()) 865 entry->done_headers_queue.end(), transaction);
856 return; 866 if (it != entry->done_headers_queue.end()) {
857 867 entry->done_headers_queue.erase(it);
858 if (entry->writer) { 868 if (cancel)
859 DCHECK(trans == entry->writer); 869 ProcessEntryFailure(entry);
860 870 return;
871 }
872
873 // Transaction is removed in the headers phase.
874 if (transaction == entry->headers_transaction) {
875 // If the response is not written (cancel is true), consider it a failure.
876 DoneWritingToEntry(entry, !cancel, transaction);
877 return;
878 }
879
880 // Transaction is removed in the writing phase.
881 if (transaction == entry->writer) {
861 // Assume there was a failure. 882 // Assume there was a failure.
862 bool success = false; 883 bool success = false;
863 if (cancel) { 884 if (cancel) {
864 DCHECK(entry->disk_entry); 885 DCHECK(entry->disk_entry);
865 // This is a successful operation in the sense that we want to keep the 886 // This is a successful operation in the sense that we want to keep the
866 // entry. 887 // entry.
867 success = trans->AddTruncatedFlag(); 888 success = transaction->AddTruncatedFlag();
868 // The previous operation may have deleted the entry. 889 // The previous operation may have deleted the entry.
869 if (!trans->entry()) 890 if (!transaction->entry())
870 return; 891 return;
871 } 892 }
872 DoneWritingToEntry(entry, success); 893 DoneWritingToEntry(entry, success, transaction);
894 return;
895 }
896
897 // Transaction is reading from the entry.
898 DoneReadingFromEntry(entry, transaction);
899 }
900
901 void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
902 bool success,
903 Transaction* transaction) {
904 DCHECK(transaction == entry->writer ||
905 transaction == entry->headers_transaction);
906
907 if (transaction == entry->writer)
908 entry->writer = nullptr;
909 else
910 entry->headers_transaction = nullptr;
911
912 // If writer fails, restart the headers_transaction by setting its state.
913 // Since the headers_transactions is awaiting an asynchronous operation
914 // completion, when it's IO callback is invoked, it will be restarted.
915 if (!success && entry->headers_transaction) {
916 entry->headers_transaction->SetValidatingCannotProceed();
917 entry->headers_transaction = nullptr;
918 DCHECK(entry->HasNoActiveTransactions());
919 }
920 if (!success)
921 ProcessEntryFailure(entry);
922 else
923 ProcessQueuedTransactions(entry);
924 }
925
926 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry,
927 Transaction* transaction) {
928 DCHECK(!entry->writer);
929 auto it = entry->readers.find(transaction);
930 DCHECK(it != entry->readers.end());
931 entry->readers.erase(it);
932
933 ProcessQueuedTransactions(entry);
934 }
935
936 void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
937 TransactionList* list) {
938 // Process done_headers_queue before add_to_entry_queue to maintain FIFO
939 // order.
940 for (auto* transaction : entry->done_headers_queue)
941 list->push_back(transaction);
942 entry->done_headers_queue.clear();
943
944 for (auto* transaction : entry->add_to_entry_queue)
945 list->push_back(transaction);
946 entry->add_to_entry_queue.clear();
947 }
948
949 void HttpCache::ProcessEntryFailure(ActiveEntry* entry) {
950 // Failure case is either writer failing to completely write the response to
951 // the cache or validating transaction received a non-304 response.
952 TransactionList list;
953 if (entry->HasNoActiveTransactions() &&
954 !entry->will_process_queued_transactions) {
955 entry->disk_entry->Doom();
956 RemoveAllQueuedTransactions(entry, &list);
957 DestroyEntry(entry);
873 } else { 958 } else {
874 DoneReadingFromEntry(entry, trans); 959 DoomActiveEntry(entry->disk_entry->GetKey());
875 } 960 RemoveAllQueuedTransactions(entry, &list);
876 } 961 }
877 962 // ERR_CACHE_RACE causes the transaction to restart the whole process.
878 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 963 for (auto* transaction : list)
879 DCHECK(entry->readers.empty()); 964 transaction->io_callback().Run(net::ERR_CACHE_RACE);
880 965 }
881 entry->writer = NULL; 966
882 967 void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
883 if (success) { 968 // Multiple readers may finish with an entry at once, so we want to batch up
884 ProcessPendingQueue(entry); 969 // calls to OnProcessQueuedTransactions. This flag also tells us that we
970 // should not delete the entry before OnProcessQueuedTransactions runs.
971 if (entry->will_process_queued_transactions)
972 return;
973
974 entry->will_process_queued_transactions = true;
975
976 // Post a task instead of invoking the io callback of another transaction here
977 // to avoid re-entrancy.
978 base::ThreadTaskRunnerHandle::Get()->PostTask(
979 FROM_HERE,
980 base::Bind(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(), entry));
981 }
982
983 void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
984 DCHECK(!entry->add_to_entry_queue.empty());
985
986 // Note the entry may be new or may already have a response body written to
987 // it. In both cases, a transaction needs to wait since only one transaction
988 // can be in the headers phase at a time.
989 if (entry->headers_transaction) {
990 return;
991 }
992 Transaction* transaction = entry->add_to_entry_queue.front();
993 entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
994 entry->headers_transaction = transaction;
995
996 transaction->io_callback().Run(OK);
997 }
998
999 void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
1000 DCHECK(!entry->writer);
1001 DCHECK(!entry->done_headers_queue.empty());
1002
1003 Transaction* transaction = entry->done_headers_queue.front();
1004
1005 // If this transaction is responsible for writing the response body.
1006 if (transaction->mode() & Transaction::WRITE) {
1007 entry->writer = transaction;
885 } else { 1008 } else {
886 DCHECK(!entry->will_process_pending_queue); 1009 // If a transaction is in front of this queue with only read mode set and
887 1010 // there is no writer, it implies response body is already written, convert
888 // We failed to create this entry. 1011 // to a reader.
889 TransactionList pending_queue; 1012 auto return_val = entry->readers.insert(transaction);
890 pending_queue.swap(entry->pending_queue); 1013 DCHECK_EQ(return_val.second, true);
891 1014 }
892 entry->disk_entry->Doom(); 1015
893 DestroyEntry(entry); 1016 // Post another task to give a chance to more transactions to either join
894 1017 // readers or another transaction to start parallel validation.
895 // We need to do something about these pending entries, which now need to 1018 ProcessQueuedTransactions(entry);
896 // be added to a new entry. 1019
897 while (!pending_queue.empty()) { 1020 entry->done_headers_queue.erase(entry->done_headers_queue.begin());
898 // ERR_CACHE_RACE causes the transaction to restart the whole process. 1021 transaction->io_callback().Run(OK);
899 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE); 1022 }
900 pending_queue.pop_front(); 1023
901 } 1024 bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
902 } 1025 Transaction* transaction,
903 } 1026 bool is_match) const {
904 1027 if (transaction != entry->headers_transaction)
905 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { 1028 return false;
906 DCHECK(!entry->writer); 1029
907 1030 if (!(transaction->mode() & Transaction::WRITE))
908 auto it = entry->readers.find(trans); 1031 return false;
909 DCHECK(it != entry->readers.end()); 1032
910 1033 // If its not a match then check if it is the transaction responsible for
911 entry->readers.erase(it); 1034 // writing the response body.
912 1035 if (!is_match) {
913 ProcessPendingQueue(entry); 1036 return !entry->writer && entry->done_headers_queue.empty() &&
914 } 1037 entry->readers.empty();
915 1038 }
916 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { 1039
917 DCHECK(entry->writer); 1040 return true;
918 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); 1041 }
919 DCHECK(entry->readers.empty()); 1042
920 1043 bool HttpCache::IsTransactionWritingIncomplete(
921 Transaction* trans = entry->writer; 1044 ActiveEntry* entry,
922 1045 Transaction* transaction,
923 entry->writer = NULL; 1046 const std::string& method) const {
924 entry->readers.insert(trans); 1047 if (transaction == entry->writer)
925 1048 return true;
926 ProcessPendingQueue(entry); 1049
1050 if (method == "HEAD" || method == "DELETE")
1051 return false;
1052
1053 // Check if transaction is about to start writing to the cache.
1054
1055 // Transaction's mode may have been set to NONE if StopCaching was invoked.
1056 if (!(transaction->mode() & Transaction::WRITE ||
1057 transaction->mode() == Transaction::NONE)) {
1058 return false;
1059 }
1060
1061 // If a transaction is completing headers or done with headers phase with
1062 // write mode then it should be the future writer. Just checking the front of
1063 // done_headers_queue since the rest should anyways be READ mode transactions
1064 // as they would be a result of validation match.
1065 return transaction == entry->headers_transaction ||
1066 transaction == entry->done_headers_queue.front();
1067 }
1068
1069 bool HttpCache::IsWritingInProgress(ActiveEntry* entry) const {
1070 return entry->writer != nullptr;
927 } 1071 }
928 1072
929 LoadState HttpCache::GetLoadStateForPendingTransaction( 1073 LoadState HttpCache::GetLoadStateForPendingTransaction(
930 const Transaction* trans) { 1074 const Transaction* trans) {
931 auto i = active_entries_.find(trans->key()); 1075 auto i = active_entries_.find(trans->key());
932 if (i == active_entries_.end()) { 1076 if (i == active_entries_.end()) {
933 // If this is really a pending transaction, and it is not part of 1077 // If this is really a pending transaction, and it is not part of
934 // active_entries_, we should be creating the backend or the entry. 1078 // active_entries_, we should be creating the backend or the entry.
935 return LOAD_STATE_WAITING_FOR_CACHE; 1079 return LOAD_STATE_WAITING_FOR_CACHE;
936 } 1080 }
(...skipping 29 matching lines...) Expand all
966 1110
967 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found; 1111 for (auto k = doomed_entries_.begin(); k != doomed_entries_.end() && !found;
968 ++k) { 1112 ++k) {
969 found = RemovePendingTransactionFromEntry(k->first, trans); 1113 found = RemovePendingTransactionFromEntry(k->first, trans);
970 } 1114 }
971 1115
972 DCHECK(found) << "Pending transaction not found"; 1116 DCHECK(found) << "Pending transaction not found";
973 } 1117 }
974 1118
975 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 1119 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
976 Transaction* trans) { 1120 Transaction* transaction) {
977 TransactionList& pending_queue = entry->pending_queue; 1121 TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
978 1122
979 auto j = find(pending_queue.begin(), pending_queue.end(), trans); 1123 auto j =
980 if (j == pending_queue.end()) 1124 find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
1125 if (j == add_to_entry_queue.end())
981 return false; 1126 return false;
982 1127
983 pending_queue.erase(j); 1128 add_to_entry_queue.erase(j);
984 return true; 1129 return true;
985 } 1130 }
986 1131
987 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, 1132 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
988 Transaction* trans) { 1133 Transaction* trans) {
989 if (pending_op->writer->Matches(trans)) { 1134 if (pending_op->writer->Matches(trans)) {
990 pending_op->writer->ClearTransaction(); 1135 pending_op->writer->ClearTransaction();
991 pending_op->writer->ClearEntry(); 1136 pending_op->writer->ClearEntry();
992 return true; 1137 return true;
993 } 1138 }
994 WorkItemList& pending_queue = pending_op->pending_queue; 1139 WorkItemList& pending_queue = pending_op->pending_queue;
995 1140
996 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) { 1141 for (auto it = pending_queue.begin(); it != pending_queue.end(); ++it) {
997 if ((*it)->Matches(trans)) { 1142 if ((*it)->Matches(trans)) {
998 pending_queue.erase(it); 1143 pending_queue.erase(it);
999 return true; 1144 return true;
1000 } 1145 }
1001 } 1146 }
1002 return false; 1147 return false;
1003 } 1148 }
1004 1149
1005 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { 1150 void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
1006 // Multiple readers may finish with an entry at once, so we want to batch up 1151 entry->will_process_queued_transactions = false;
1007 // calls to OnProcessPendingQueue. This flag also tells us that we should
1008 // not delete the entry before OnProcessPendingQueue runs.
1009 if (entry->will_process_pending_queue)
1010 return;
1011 entry->will_process_pending_queue = true;
1012 1152
1013 base::ThreadTaskRunnerHandle::Get()->PostTask( 1153 // Note that this function should only invoke one transaction's IO callback
1014 FROM_HERE, 1154 // since its possible for IO callbacks' consumers to destroy the cache/entry.
1015 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1016 }
1017
1018 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1019 entry->will_process_pending_queue = false;
1020 DCHECK(!entry->writer);
1021 1155
1022 // If no one is interested in this entry, then we can deactivate it. 1156 // If no one is interested in this entry, then we can deactivate it.
1023 if (entry->HasNoTransactions()) { 1157 if (entry->HasNoTransactions()) {
1024 DestroyEntry(entry); 1158 DestroyEntry(entry);
1025 return; 1159 return;
1026 } 1160 }
1027 1161
1028 if (entry->pending_queue.empty()) 1162 if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty())
1029 return; 1163 return;
1030 1164
1031 // Promote next transaction from the pending queue. 1165 // To maintain FIFO order of transactions, done_headers_queue should be
1032 Transaction* next = entry->pending_queue.front(); 1166 // checked for processing before add_to_entry_queue.
1033 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1034 return; // Have to wait.
1035 1167
1036 entry->pending_queue.erase(entry->pending_queue.begin()); 1168 // If another transaction is writing the response, let validated transactions
1169 // wait till the response is complete. If the response is not yet started, the
1170 // done_headers_queue transaction should start writing it.
1171 if (!entry->writer && !entry->done_headers_queue.empty()) {
1172 ProcessDoneHeadersQueue(entry);
1173 return;
1174 }
1037 1175
1038 int rv = AddTransactionToEntry(entry, next); 1176 if (!entry->add_to_entry_queue.empty())
1039 if (rv != ERR_IO_PENDING) { 1177 ProcessAddToEntryQueue(entry);
1040 next->io_callback().Run(rv);
1041 }
1042 } 1178 }
1043 1179
1044 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { 1180 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1045 WorkItemOperation op = pending_op->writer->operation(); 1181 WorkItemOperation op = pending_op->writer->operation();
1046 1182
1047 // Completing the creation of the backend is simpler than the other cases. 1183 // Completing the creation of the backend is simpler than the other cases.
1048 if (op == WI_CREATE_BACKEND) 1184 if (op == WI_CREATE_BACKEND)
1049 return OnBackendCreated(result, pending_op); 1185 return OnBackendCreated(result, pending_op);
1050 1186
1051 std::unique_ptr<WorkItem> item = std::move(pending_op->writer); 1187 std::unique_ptr<WorkItem> item = std::move(pending_op->writer);
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
1178 building_backend_ = false; 1314 building_backend_ = false;
1179 DeletePendingOp(pending_op); 1315 DeletePendingOp(pending_op);
1180 } 1316 }
1181 1317
1182 // The cache may be gone when we return from the callback. 1318 // The cache may be gone when we return from the callback.
1183 if (!item->DoCallback(result, disk_cache_.get())) 1319 if (!item->DoCallback(result, disk_cache_.get()))
1184 item->NotifyTransaction(result, NULL); 1320 item->NotifyTransaction(result, NULL);
1185 } 1321 }
1186 1322
1187 } // namespace net 1323 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698