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/. */
6 // HttpLog.h should generally be included first
9 // Log on level :5, instead of default :4.
11 #define LOG(args) LOG5(args)
13 #define LOG_ENABLED() LOG5_ENABLED()
15 #include "PendingTransactionQueue.h"
16 #include "nsHttpHandler.h"
17 #include "mozilla/ChaosMode.h"
22 static uint64_t TabIdForQueuing(nsAHttpTransaction
* transaction
) {
23 return gHttpHandler
->ActiveTabPriority() ? transaction
->BrowserId() : 0;
26 // This function decides the transaction's order in the pending queue.
27 // Given two transactions t1 and t2, returning true means that t2 is
28 // more important than t1 and thus should be dispatched first.
29 static bool TransactionComparator(nsHttpTransaction
* t1
,
30 nsHttpTransaction
* t2
) {
32 t1
->Caps() & (NS_HTTP_LOAD_AS_BLOCKING
| NS_HTTP_LOAD_UNBLOCKED
);
34 t2
->Caps() & (NS_HTTP_LOAD_AS_BLOCKING
| NS_HTTP_LOAD_UNBLOCKED
);
36 if (t1Blocking
> t2Blocking
) {
40 if (t2Blocking
> t1Blocking
) {
44 return t1
->Priority() >= t2
->Priority();
47 void PendingTransactionQueue::InsertTransactionNormal(
48 PendingTransactionInfo
* info
,
49 bool aInsertAsFirstForTheSamePriority
/*= false*/) {
51 ("PendingTransactionQueue::InsertTransactionNormal"
52 " trans=%p, bid=%" PRIu64
"\n",
53 info
->Transaction(), info
->Transaction()->BrowserId()));
55 uint64_t windowId
= TabIdForQueuing(info
->Transaction());
56 nsTArray
<RefPtr
<PendingTransactionInfo
>>* const infoArray
=
57 mPendingTransactionTable
.GetOrInsertNew(windowId
);
59 // XXX At least if a new array was empty before, this isn't efficient, as it
60 // does an insert-sort. It would be better to just append all elements and
62 InsertTransactionSorted(*infoArray
, info
, aInsertAsFirstForTheSamePriority
);
65 void PendingTransactionQueue::InsertTransactionSorted(
66 nsTArray
<RefPtr
<PendingTransactionInfo
>>& pendingQ
,
67 PendingTransactionInfo
* pendingTransInfo
,
68 bool aInsertAsFirstForTheSamePriority
/*= false*/) {
69 // insert the transaction into the front of the queue based on following
71 // 1. The transaction has NS_HTTP_LOAD_AS_BLOCKING or NS_HTTP_LOAD_UNBLOCKED.
72 // 2. The transaction's priority is higher.
74 // search in reverse order under the assumption that many of the
75 // existing transactions will have the same priority (usually 0).
77 nsHttpTransaction
* trans
= pendingTransInfo
->Transaction();
79 for (int32_t i
= pendingQ
.Length() - 1; i
>= 0; --i
) {
80 nsHttpTransaction
* t
= pendingQ
[i
]->Transaction();
81 if (TransactionComparator(trans
, t
)) {
82 if (ChaosMode::isActive(ChaosFeature::NetworkScheduling
) ||
83 aInsertAsFirstForTheSamePriority
) {
84 int32_t samePriorityCount
;
85 for (samePriorityCount
= 0; i
- samePriorityCount
>= 0;
86 ++samePriorityCount
) {
87 if (pendingQ
[i
- samePriorityCount
]->Transaction()->Priority() !=
92 if (aInsertAsFirstForTheSamePriority
) {
93 i
-= samePriorityCount
;
95 // skip over 0...all of the elements with the same priority.
96 i
-= ChaosMode::randomUint32LessThan(samePriorityCount
+ 1);
99 pendingQ
.InsertElementAt(i
+ 1, pendingTransInfo
);
103 pendingQ
.InsertElementAt(0, pendingTransInfo
);
106 void PendingTransactionQueue::InsertTransaction(
107 PendingTransactionInfo
* pendingTransInfo
,
108 bool aInsertAsFirstForTheSamePriority
/* = false */) {
109 if (pendingTransInfo
->Transaction()->Caps() & NS_HTTP_URGENT_START
) {
111 (" adding transaction to pending queue "
112 "[trans=%p urgent-start-count=%zu]\n",
113 pendingTransInfo
->Transaction(), mUrgentStartQ
.Length() + 1));
114 // put this transaction on the urgent-start queue...
115 InsertTransactionSorted(mUrgentStartQ
, pendingTransInfo
);
118 (" adding transaction to pending queue "
119 "[trans=%p pending-count=%zu]\n",
120 pendingTransInfo
->Transaction(), PendingQueueLength() + 1));
121 // put this transaction on the pending queue...
122 InsertTransactionNormal(pendingTransInfo
);
126 nsTArray
<RefPtr
<PendingTransactionInfo
>>*
127 PendingTransactionQueue::GetTransactionPendingQHelper(
128 nsAHttpTransaction
* trans
) {
129 nsTArray
<RefPtr
<PendingTransactionInfo
>>* pendingQ
= nullptr;
130 int32_t caps
= trans
->Caps();
131 if (caps
& NS_HTTP_URGENT_START
) {
132 pendingQ
= &(mUrgentStartQ
);
134 pendingQ
= mPendingTransactionTable
.Get(TabIdForQueuing(trans
));
139 void PendingTransactionQueue::AppendPendingUrgentStartQ(
140 nsTArray
<RefPtr
<PendingTransactionInfo
>>& result
) {
141 result
.InsertElementsAt(0, mUrgentStartQ
.Elements(), mUrgentStartQ
.Length());
142 mUrgentStartQ
.Clear();
145 void PendingTransactionQueue::AppendPendingQForFocusedWindow(
146 uint64_t windowId
, nsTArray
<RefPtr
<PendingTransactionInfo
>>& result
,
148 nsTArray
<RefPtr
<PendingTransactionInfo
>>* infoArray
= nullptr;
149 if (!mPendingTransactionTable
.Get(windowId
, &infoArray
)) {
154 uint32_t countToAppend
= maxCount
;
155 countToAppend
= countToAppend
> infoArray
->Length() || countToAppend
== 0
156 ? infoArray
->Length()
159 result
.InsertElementsAt(result
.Length(), infoArray
->Elements(),
161 infoArray
->RemoveElementsAt(0, countToAppend
);
164 ("PendingTransactionQueue::AppendPendingQForFocusedWindow, "
165 "pendingQ count=%zu window.count=%zu for focused window (id=%" PRIu64
167 result
.Length(), infoArray
->Length(), windowId
));
170 void PendingTransactionQueue::AppendPendingQForNonFocusedWindows(
171 uint64_t windowId
, nsTArray
<RefPtr
<PendingTransactionInfo
>>& result
,
173 // XXX Adjust the order of transactions in a smarter manner.
174 uint32_t totalCount
= 0;
175 for (const auto& entry
: mPendingTransactionTable
) {
176 if (windowId
&& entry
.GetKey() == windowId
) {
181 for (; count
< entry
.GetWeak()->Length(); ++count
) {
182 if (maxCount
&& totalCount
== maxCount
) {
186 // Because elements in |result| could come from multiple penndingQ,
187 // call |InsertTransactionSorted| to make sure the order is correct.
188 InsertTransactionSorted(result
, entry
.GetWeak()->ElementAt(count
));
191 entry
.GetWeak()->RemoveElementsAt(0, count
);
193 if (maxCount
&& totalCount
== maxCount
) {
194 if (entry
.GetWeak()->Length()) {
195 // There are still some pending transactions for background
196 // tabs but we limit their dispatch. This is considered as
197 // an active tab optimization.
198 nsHttp::NotifyActiveTabLoadOptimization();
205 void PendingTransactionQueue::ReschedTransaction(nsHttpTransaction
* aTrans
) {
206 nsTArray
<RefPtr
<PendingTransactionInfo
>>* pendingQ
=
207 GetTransactionPendingQHelper(aTrans
);
210 pendingQ
? pendingQ
->IndexOf(aTrans
, 0, PendingComparator()) : -1;
212 RefPtr
<PendingTransactionInfo
> pendingTransInfo
= (*pendingQ
)[index
];
213 pendingQ
->RemoveElementAt(index
);
214 InsertTransactionSorted(*pendingQ
, pendingTransInfo
);
218 void PendingTransactionQueue::RemoveEmptyPendingQ() {
219 for (auto it
= mPendingTransactionTable
.Iter(); !it
.Done(); it
.Next()) {
220 if (it
.UserData()->IsEmpty()) {
226 size_t PendingTransactionQueue::PendingQueueLength() const {
228 for (const auto& data
: mPendingTransactionTable
.Values()) {
229 length
+= data
->Length();
235 size_t PendingTransactionQueue::PendingQueueLengthForWindow(
236 uint64_t windowId
) const {
237 auto* pendingQ
= mPendingTransactionTable
.Get(windowId
);
238 return (pendingQ
) ? pendingQ
->Length() : 0;
241 size_t PendingTransactionQueue::UrgentStartQueueLength() {
242 return mUrgentStartQ
.Length();
245 void PendingTransactionQueue::PrintPendingQ() {
246 LOG(("urgent queue ["));
247 for (const auto& info
: mUrgentStartQ
) {
248 LOG((" %p", info
->Transaction()));
250 for (const auto& entry
: mPendingTransactionTable
) {
251 LOG(("] window id = %" PRIx64
" queue [", entry
.GetKey()));
252 for (const auto& info
: *entry
.GetWeak()) {
253 LOG((" %p", info
->Transaction()));
259 void PendingTransactionQueue::Compact() {
260 mUrgentStartQ
.Compact();
261 for (const auto& data
: mPendingTransactionTable
.Values()) {
266 void PendingTransactionQueue::CancelAllTransactions(nsresult reason
) {
267 for (const auto& pendingTransInfo
: mUrgentStartQ
) {
268 LOG(("PendingTransactionQueue::CancelAllTransactions %p\n",
269 pendingTransInfo
->Transaction()));
270 pendingTransInfo
->Transaction()->Close(reason
);
272 mUrgentStartQ
.Clear();
274 for (const auto& data
: mPendingTransactionTable
.Values()) {
275 for (const auto& pendingTransInfo
: *data
) {
276 LOG(("PendingTransactionQueue::CancelAllTransactions %p\n",
277 pendingTransInfo
->Transaction()));
278 pendingTransInfo
->Transaction()->Close(reason
);
283 mPendingTransactionTable
.Clear();
287 } // namespace mozilla