290 lines
9.5 KiB
C++
290 lines
9.5 KiB
C++
/* vim:set ts=4 sw=2 sts=2 et cin: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// HttpLog.h should generally be included first
|
|
#include "HttpLog.h"
|
|
|
|
// Log on level :5, instead of default :4.
|
|
#undef LOG
|
|
#define LOG(args) LOG5(args)
|
|
#undef LOG_ENABLED
|
|
#define LOG_ENABLED() LOG5_ENABLED()
|
|
|
|
#include "PendingTransactionQueue.h"
|
|
#include "nsHttpHandler.h"
|
|
#include "mozilla/ChaosMode.h"
|
|
#include "mozilla/StaticPrefs_network.h"
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
static uint64_t TabIdForQueuing(nsAHttpTransaction* transaction) {
|
|
return StaticPrefs::network_http_active_tab_priority()
|
|
? transaction->BrowserId()
|
|
: 0;
|
|
}
|
|
|
|
// This function decides the transaction's order in the pending queue.
|
|
// Given two transactions t1 and t2, returning true means that t2 is
|
|
// more important than t1 and thus should be dispatched first.
|
|
static bool TransactionComparator(nsHttpTransaction* t1,
|
|
nsHttpTransaction* t2) {
|
|
bool t1Blocking =
|
|
t1->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED);
|
|
bool t2Blocking =
|
|
t2->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED);
|
|
|
|
if (t1Blocking > t2Blocking) {
|
|
return false;
|
|
}
|
|
|
|
if (t2Blocking > t1Blocking) {
|
|
return true;
|
|
}
|
|
|
|
return t1->Priority() >= t2->Priority();
|
|
}
|
|
|
|
void PendingTransactionQueue::InsertTransactionNormal(
|
|
PendingTransactionInfo* info,
|
|
bool aInsertAsFirstForTheSamePriority /*= false*/) {
|
|
LOG(
|
|
("PendingTransactionQueue::InsertTransactionNormal"
|
|
" trans=%p, bid=%" PRIu64 "\n",
|
|
info->Transaction(), info->Transaction()->BrowserId()));
|
|
|
|
uint64_t windowId = TabIdForQueuing(info->Transaction());
|
|
nsTArray<RefPtr<PendingTransactionInfo>>* const infoArray =
|
|
mPendingTransactionTable.GetOrInsertNew(windowId);
|
|
|
|
// XXX At least if a new array was empty before, this isn't efficient, as it
|
|
// does an insert-sort. It would be better to just append all elements and
|
|
// then sort.
|
|
InsertTransactionSorted(*infoArray, info, aInsertAsFirstForTheSamePriority);
|
|
}
|
|
|
|
void PendingTransactionQueue::InsertTransactionSorted(
|
|
nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ,
|
|
PendingTransactionInfo* pendingTransInfo,
|
|
bool aInsertAsFirstForTheSamePriority /*= false*/) {
|
|
// insert the transaction into the front of the queue based on following
|
|
// rules:
|
|
// 1. The transaction has NS_HTTP_LOAD_AS_BLOCKING or NS_HTTP_LOAD_UNBLOCKED.
|
|
// 2. The transaction's priority is higher.
|
|
//
|
|
// search in reverse order under the assumption that many of the
|
|
// existing transactions will have the same priority (usually 0).
|
|
|
|
nsHttpTransaction* trans = pendingTransInfo->Transaction();
|
|
|
|
for (int32_t i = pendingQ.Length() - 1; i >= 0; --i) {
|
|
nsHttpTransaction* t = pendingQ[i]->Transaction();
|
|
if (TransactionComparator(trans, t)) {
|
|
if (ChaosMode::isActive(ChaosFeature::NetworkScheduling) ||
|
|
aInsertAsFirstForTheSamePriority) {
|
|
int32_t samePriorityCount;
|
|
for (samePriorityCount = 0; i - samePriorityCount >= 0;
|
|
++samePriorityCount) {
|
|
if (pendingQ[i - samePriorityCount]->Transaction()->Priority() !=
|
|
trans->Priority()) {
|
|
break;
|
|
}
|
|
}
|
|
if (aInsertAsFirstForTheSamePriority) {
|
|
i -= samePriorityCount;
|
|
} else {
|
|
// skip over 0...all of the elements with the same priority.
|
|
i -= ChaosMode::randomUint32LessThan(samePriorityCount + 1);
|
|
}
|
|
}
|
|
pendingQ.InsertElementAt(i + 1, pendingTransInfo);
|
|
return;
|
|
}
|
|
}
|
|
pendingQ.InsertElementAt(0, pendingTransInfo);
|
|
}
|
|
|
|
void PendingTransactionQueue::InsertTransaction(
|
|
PendingTransactionInfo* pendingTransInfo,
|
|
bool aInsertAsFirstForTheSamePriority /* = false */) {
|
|
if (pendingTransInfo->Transaction()->Caps() & NS_HTTP_URGENT_START) {
|
|
LOG(
|
|
(" adding transaction to pending queue "
|
|
"[trans=%p urgent-start-count=%zu]\n",
|
|
pendingTransInfo->Transaction(), mUrgentStartQ.Length() + 1));
|
|
// put this transaction on the urgent-start queue...
|
|
InsertTransactionSorted(mUrgentStartQ, pendingTransInfo);
|
|
} else {
|
|
LOG(
|
|
(" adding transaction to pending queue "
|
|
"[trans=%p pending-count=%zu]\n",
|
|
pendingTransInfo->Transaction(), PendingQueueLength() + 1));
|
|
// put this transaction on the pending queue...
|
|
InsertTransactionNormal(pendingTransInfo);
|
|
}
|
|
}
|
|
|
|
nsTArray<RefPtr<PendingTransactionInfo>>*
|
|
PendingTransactionQueue::GetTransactionPendingQHelper(
|
|
nsAHttpTransaction* trans) {
|
|
nsTArray<RefPtr<PendingTransactionInfo>>* pendingQ = nullptr;
|
|
int32_t caps = trans->Caps();
|
|
if (caps & NS_HTTP_URGENT_START) {
|
|
pendingQ = &(mUrgentStartQ);
|
|
} else {
|
|
pendingQ = mPendingTransactionTable.Get(TabIdForQueuing(trans));
|
|
}
|
|
return pendingQ;
|
|
}
|
|
|
|
void PendingTransactionQueue::AppendPendingUrgentStartQ(
|
|
nsTArray<RefPtr<PendingTransactionInfo>>& result) {
|
|
result.InsertElementsAt(0, mUrgentStartQ.Elements(), mUrgentStartQ.Length());
|
|
mUrgentStartQ.Clear();
|
|
}
|
|
|
|
void PendingTransactionQueue::AppendPendingQForFocusedWindow(
|
|
uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result,
|
|
uint32_t maxCount) {
|
|
nsTArray<RefPtr<PendingTransactionInfo>>* infoArray = nullptr;
|
|
if (!mPendingTransactionTable.Get(windowId, &infoArray)) {
|
|
result.Clear();
|
|
return;
|
|
}
|
|
|
|
uint32_t countToAppend = maxCount;
|
|
countToAppend = countToAppend > infoArray->Length() || countToAppend == 0
|
|
? infoArray->Length()
|
|
: countToAppend;
|
|
|
|
result.InsertElementsAt(result.Length(), infoArray->Elements(),
|
|
countToAppend);
|
|
infoArray->RemoveElementsAt(0, countToAppend);
|
|
|
|
LOG(
|
|
("PendingTransactionQueue::AppendPendingQForFocusedWindow, "
|
|
"pendingQ count=%zu window.count=%zu for focused window (id=%" PRIu64
|
|
")\n",
|
|
result.Length(), infoArray->Length(), windowId));
|
|
}
|
|
|
|
void PendingTransactionQueue::AppendPendingQForNonFocusedWindows(
|
|
uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>>& result,
|
|
uint32_t maxCount) {
|
|
// XXX Adjust the order of transactions in a smarter manner.
|
|
uint32_t totalCount = 0;
|
|
for (const auto& entry : mPendingTransactionTable) {
|
|
if (windowId && entry.GetKey() == windowId) {
|
|
continue;
|
|
}
|
|
|
|
uint32_t count = 0;
|
|
for (; count < entry.GetWeak()->Length(); ++count) {
|
|
if (maxCount && totalCount == maxCount) {
|
|
break;
|
|
}
|
|
|
|
// Because elements in |result| could come from multiple penndingQ,
|
|
// call |InsertTransactionSorted| to make sure the order is correct.
|
|
InsertTransactionSorted(result, entry.GetWeak()->ElementAt(count));
|
|
++totalCount;
|
|
}
|
|
entry.GetWeak()->RemoveElementsAt(0, count);
|
|
|
|
if (maxCount && totalCount == maxCount) {
|
|
if (entry.GetWeak()->Length()) {
|
|
// There are still some pending transactions for background
|
|
// tabs but we limit their dispatch. This is considered as
|
|
// an active tab optimization.
|
|
nsHttp::NotifyActiveTabLoadOptimization();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PendingTransactionQueue::ReschedTransaction(nsHttpTransaction* aTrans) {
|
|
nsTArray<RefPtr<PendingTransactionInfo>>* pendingQ =
|
|
GetTransactionPendingQHelper(aTrans);
|
|
|
|
int32_t index =
|
|
pendingQ ? pendingQ->IndexOf(aTrans, 0, PendingComparator()) : -1;
|
|
if (index >= 0) {
|
|
RefPtr<PendingTransactionInfo> pendingTransInfo = (*pendingQ)[index];
|
|
pendingQ->RemoveElementAt(index);
|
|
InsertTransactionSorted(*pendingQ, pendingTransInfo);
|
|
}
|
|
}
|
|
|
|
void PendingTransactionQueue::RemoveEmptyPendingQ() {
|
|
for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
|
|
if (it.UserData()->IsEmpty()) {
|
|
it.Remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t PendingTransactionQueue::PendingQueueLength() const {
|
|
size_t length = 0;
|
|
for (const auto& data : mPendingTransactionTable.Values()) {
|
|
length += data->Length();
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
size_t PendingTransactionQueue::PendingQueueLengthForWindow(
|
|
uint64_t windowId) const {
|
|
auto* pendingQ = mPendingTransactionTable.Get(windowId);
|
|
return (pendingQ) ? pendingQ->Length() : 0;
|
|
}
|
|
|
|
size_t PendingTransactionQueue::UrgentStartQueueLength() {
|
|
return mUrgentStartQ.Length();
|
|
}
|
|
|
|
void PendingTransactionQueue::PrintPendingQ() {
|
|
LOG(("urgent queue ["));
|
|
for (const auto& info : mUrgentStartQ) {
|
|
LOG((" %p", info->Transaction()));
|
|
}
|
|
for (const auto& entry : mPendingTransactionTable) {
|
|
LOG(("] window id = %" PRIx64 " queue [", entry.GetKey()));
|
|
for (const auto& info : *entry.GetWeak()) {
|
|
LOG((" %p", info->Transaction()));
|
|
}
|
|
}
|
|
LOG(("]"));
|
|
}
|
|
|
|
void PendingTransactionQueue::Compact() {
|
|
mUrgentStartQ.Compact();
|
|
for (const auto& data : mPendingTransactionTable.Values()) {
|
|
data->Compact();
|
|
}
|
|
}
|
|
|
|
void PendingTransactionQueue::CancelAllTransactions(nsresult reason) {
|
|
for (const auto& pendingTransInfo : mUrgentStartQ) {
|
|
LOG(("PendingTransactionQueue::CancelAllTransactions %p\n",
|
|
pendingTransInfo->Transaction()));
|
|
pendingTransInfo->Transaction()->Close(reason);
|
|
}
|
|
mUrgentStartQ.Clear();
|
|
|
|
for (const auto& data : mPendingTransactionTable.Values()) {
|
|
for (const auto& pendingTransInfo : *data) {
|
|
LOG(("PendingTransactionQueue::CancelAllTransactions %p\n",
|
|
pendingTransInfo->Transaction()));
|
|
pendingTransInfo->Transaction()->Close(reason);
|
|
}
|
|
data->Clear();
|
|
}
|
|
|
|
mPendingTransactionTable.Clear();
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|