PendingTransactionQueue.cpp (9742B)
1 /* vim:set ts=4 sw=2 sts=2 et cin: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // HttpLog.h should generally be included first 7 #include "HttpLog.h" 8 9 // Log on level :5, instead of default :4. 10 #undef LOG 11 #define LOG(args) LOG5(args) 12 #undef LOG_ENABLED 13 #define LOG_ENABLED() LOG5_ENABLED() 14 15 #include "PendingTransactionQueue.h" 16 #include "nsHttpHandler.h" 17 #include "mozilla/ChaosMode.h" 18 #include "mozilla/StaticPrefs_network.h" 19 20 namespace mozilla { 21 namespace net { 22 23 static uint64_t TabIdForQueuing(nsAHttpTransaction* transaction) { 24 return StaticPrefs::network_http_active_tab_priority() 25 ? transaction->BrowserId() 26 : 0; 27 } 28 29 // This function decides the transaction's order in the pending queue. 30 // Given two transactions t1 and t2, returning true means that t2 is 31 // more important than t1 and thus should be dispatched first. 32 static bool TransactionComparator(nsHttpTransaction* t1, 33 nsHttpTransaction* t2) { 34 bool t1Blocking = 35 t1->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED); 36 bool t2Blocking = 37 t2->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED); 38 39 if (t1Blocking > t2Blocking) { 40 return false; 41 } 42 43 if (t2Blocking > t1Blocking) { 44 return true; 45 } 46 47 return t1->Priority() >= t2->Priority(); 48 } 49 50 void PendingTransactionQueue::InsertTransactionNormal( 51 PendingTransactionInfo* info, 52 bool aInsertAsFirstForTheSamePriority /*= false*/) { 53 LOG( 54 ("PendingTransactionQueue::InsertTransactionNormal" 55 " trans=%p, bid=%" PRIu64 "\n", 56 info->Transaction(), info->Transaction()->BrowserId())); 57 58 uint64_t windowId = TabIdForQueuing(info->Transaction()); 59 nsTArray<RefPtr<PendingTransactionInfo>>* const infoArray = 60 mPendingTransactionTable.GetOrInsertNew(windowId); 61 62 // XXX At least if a new array was empty before, this isn't efficient, as it 63 // does an insert-sort. It would be better to just append all elements and 64 // then sort. 65 InsertTransactionSorted(*infoArray, info, aInsertAsFirstForTheSamePriority); 66 } 67 68 void PendingTransactionQueue::InsertTransactionSorted( 69 nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ, 70 PendingTransactionInfo* pendingTransInfo, 71 bool aInsertAsFirstForTheSamePriority /*= false*/) { 72 // insert the transaction into the front of the queue based on following 73 // rules: 74 // 1. The transaction has NS_HTTP_LOAD_AS_BLOCKING or NS_HTTP_LOAD_UNBLOCKED. 75 // 2. The transaction's priority is higher. 76 // 77 // search in reverse order under the assumption that many of the 78 // existing transactions will have the same priority (usually 0). 79 80 nsHttpTransaction* trans = pendingTransInfo->Transaction(); 81 82 for (int32_t i = pendingQ.Length() - 1; i >= 0; --i) { 83 nsHttpTransaction* t = pendingQ[i]->Transaction(); 84 if (TransactionComparator(trans, t)) { 85 if (ChaosMode::isActive(ChaosFeature::NetworkScheduling) || 86 aInsertAsFirstForTheSamePriority) { 87 int32_t samePriorityCount; 88 for (samePriorityCount = 0; i - samePriorityCount >= 0; 89 ++samePriorityCount) { 90 if (pendingQ[i - samePriorityCount]->Transaction()->Priority() != 91 trans->Priority()) { 92 break; 93 } 94 } 95 if (aInsertAsFirstForTheSamePriority) { 96 i -= samePriorityCount; 97 } else { 98 // skip over 0...all of the elements with the same priority. 99 i -= ChaosMode::randomUint32LessThan(samePriorityCount + 1); 100 } 101 } 102 pendingQ.InsertElementAt(i + 1, pendingTransInfo); 103 return; 104 } 105 } 106 pendingQ.InsertElementAt(0, pendingTransInfo); 107 } 108 109 void PendingTransactionQueue::InsertTransaction( 110 PendingTransactionInfo* pendingTransInfo, 111 bool aInsertAsFirstForTheSamePriority /* = false */) { 112 if (pendingTransInfo->Transaction()->Caps() & NS_HTTP_URGENT_START) { 113 LOG( 114 (" adding transaction to pending queue " 115 "[trans=%p urgent-start-count=%zu]\n", 116 pendingTransInfo->Transaction(), mUrgentStartQ.Length() + 1)); 117 // put this transaction on the urgent-start queue... 118 InsertTransactionSorted(mUrgentStartQ, pendingTransInfo); 119 } else { 120 LOG( 121 (" adding transaction to pending queue " 122 "[trans=%p pending-count=%zu]\n", 123 pendingTransInfo->Transaction(), PendingQueueLength() + 1)); 124 // put this transaction on the pending queue... 125 InsertTransactionNormal(pendingTransInfo); 126 } 127 } 128 129 nsTArray<RefPtr<PendingTransactionInfo>>* 130 PendingTransactionQueue::GetTransactionPendingQHelper( 131 nsAHttpTransaction* trans) { 132 nsTArray<RefPtr<PendingTransactionInfo>>* pendingQ = nullptr; 133 int32_t caps = trans->Caps(); 134 if (caps & NS_HTTP_URGENT_START) { 135 pendingQ = &(mUrgentStartQ); 136 } else { 137 pendingQ = mPendingTransactionTable.Get(TabIdForQueuing(trans)); 138 } 139 return pendingQ; 140 } 141 142 void PendingTransactionQueue::AppendPendingUrgentStartQ( 143 nsTArray<RefPtr<PendingTransactionInfo>>& result) { 144 result.InsertElementsAt(0, mUrgentStartQ.Elements(), mUrgentStartQ.Length()); 145 mUrgentStartQ.Clear(); 146 } 147 148 void PendingTransactionQueue::AppendPendingQForFocusedWindow( 149 uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result, 150 uint32_t maxCount) { 151 nsTArray<RefPtr<PendingTransactionInfo>>* infoArray = nullptr; 152 if (!mPendingTransactionTable.Get(windowId, &infoArray)) { 153 result.Clear(); 154 return; 155 } 156 157 uint32_t countToAppend = maxCount; 158 countToAppend = countToAppend > infoArray->Length() || countToAppend == 0 159 ? infoArray->Length() 160 : countToAppend; 161 162 result.InsertElementsAt(result.Length(), infoArray->Elements(), 163 countToAppend); 164 infoArray->RemoveElementsAt(0, countToAppend); 165 166 LOG( 167 ("PendingTransactionQueue::AppendPendingQForFocusedWindow, " 168 "pendingQ count=%zu window.count=%zu for focused window (id=%" PRIu64 169 ")\n", 170 result.Length(), infoArray->Length(), windowId)); 171 } 172 173 void PendingTransactionQueue::AppendPendingQForNonFocusedWindows( 174 uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result, 175 uint32_t maxCount) { 176 // XXX Adjust the order of transactions in a smarter manner. 177 uint32_t totalCount = 0; 178 for (const auto& entry : mPendingTransactionTable) { 179 if (windowId && entry.GetKey() == windowId) { 180 continue; 181 } 182 183 uint32_t count = 0; 184 for (; count < entry.GetWeak()->Length(); ++count) { 185 if (maxCount && totalCount == maxCount) { 186 break; 187 } 188 189 // Because elements in |result| could come from multiple penndingQ, 190 // call |InsertTransactionSorted| to make sure the order is correct. 191 InsertTransactionSorted(result, entry.GetWeak()->ElementAt(count)); 192 ++totalCount; 193 } 194 entry.GetWeak()->RemoveElementsAt(0, count); 195 196 if (maxCount && totalCount == maxCount) { 197 if (entry.GetWeak()->Length()) { 198 // There are still some pending transactions for background 199 // tabs but we limit their dispatch. This is considered as 200 // an active tab optimization. 201 nsHttp::NotifyActiveTabLoadOptimization(); 202 } 203 break; 204 } 205 } 206 } 207 208 void PendingTransactionQueue::ReschedTransaction(nsHttpTransaction* aTrans) { 209 nsTArray<RefPtr<PendingTransactionInfo>>* pendingQ = 210 GetTransactionPendingQHelper(aTrans); 211 212 int32_t index = 213 pendingQ ? pendingQ->IndexOf(aTrans, 0, PendingComparator()) : -1; 214 if (index >= 0) { 215 RefPtr<PendingTransactionInfo> pendingTransInfo = (*pendingQ)[index]; 216 pendingQ->RemoveElementAt(index); 217 InsertTransactionSorted(*pendingQ, pendingTransInfo); 218 } 219 } 220 221 void PendingTransactionQueue::RemoveEmptyPendingQ() { 222 for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) { 223 if (it.UserData()->IsEmpty()) { 224 it.Remove(); 225 } 226 } 227 } 228 229 size_t PendingTransactionQueue::PendingQueueLength() const { 230 size_t length = 0; 231 for (const auto& data : mPendingTransactionTable.Values()) { 232 length += data->Length(); 233 } 234 235 return length; 236 } 237 238 size_t PendingTransactionQueue::PendingQueueLengthForWindow( 239 uint64_t windowId) const { 240 auto* pendingQ = mPendingTransactionTable.Get(windowId); 241 return (pendingQ) ? pendingQ->Length() : 0; 242 } 243 244 size_t PendingTransactionQueue::UrgentStartQueueLength() { 245 return mUrgentStartQ.Length(); 246 } 247 248 void PendingTransactionQueue::PrintPendingQ() { 249 LOG(("urgent queue [")); 250 for (const auto& info : mUrgentStartQ) { 251 LOG((" %p", info->Transaction())); 252 } 253 for (const auto& entry : mPendingTransactionTable) { 254 LOG(("] window id = %" PRIx64 " queue [", entry.GetKey())); 255 for (const auto& info : *entry.GetWeak()) { 256 LOG((" %p", info->Transaction())); 257 } 258 } 259 LOG(("]")); 260 } 261 262 void PendingTransactionQueue::Compact() { 263 mUrgentStartQ.Compact(); 264 for (const auto& data : mPendingTransactionTable.Values()) { 265 data->Compact(); 266 } 267 } 268 269 void PendingTransactionQueue::CancelAllTransactions(nsresult reason) { 270 AutoTArray<RefPtr<nsHttpTransaction>, 64> toClose; 271 for (const auto& info : mUrgentStartQ) { 272 toClose.AppendElement(info->Transaction()); 273 } 274 mUrgentStartQ.Clear(); 275 276 // Drain all table entries into toClose, then clear them. 277 for (const auto& data : mPendingTransactionTable.Values()) { 278 for (const auto& info : *data) { 279 toClose.AppendElement(info->Transaction()); 280 } 281 data->Clear(); 282 } 283 mPendingTransactionTable.Clear(); 284 285 for (auto trans : toClose) { 286 LOG(("PendingTransactionQueue::CancelAllTransactions %p\n", trans.get())); 287 trans->Close(reason); 288 } 289 } 290 291 } // namespace net 292 } // namespace mozilla