Source code

Revision control

Copy as Markdown

Other Tools

/* 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"
namespace mozilla {
namespace net {
static uint64_t TabIdForQueuing(nsAHttpTransaction* transaction) {
return gHttpHandler->ActiveTabPriority() ? 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