diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/protocol/http/PendingTransactionQueue.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/netwerk/protocol/http/PendingTransactionQueue.cpp b/netwerk/protocol/http/PendingTransactionQueue.cpp new file mode 100644 index 0000000000..6fea59be03 --- /dev/null +++ b/netwerk/protocol/http/PendingTransactionQueue.cpp @@ -0,0 +1,290 @@ +/* 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->TopLevelOuterContentWindowId() + : 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, windowId=%" PRIu64 "\n", + info->Transaction(), + info->Transaction()->TopLevelOuterContentWindowId())); + + uint64_t windowId = TabIdForQueuing(info->Transaction()); + nsTArray<RefPtr<PendingTransactionInfo>>* infoArray; + if (!mPendingTransactionTable.Get(windowId, &infoArray)) { + infoArray = new nsTArray<RefPtr<PendingTransactionInfo>>(); + mPendingTransactionTable.Put(windowId, infoArray); + } + + 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 (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) { + if (windowId && it.Key() == windowId) { + continue; + } + + uint32_t count = 0; + for (; count < it.UserData()->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, it.UserData()->ElementAt(count)); + ++totalCount; + } + it.UserData()->RemoveElementsAt(0, count); + + if (maxCount && totalCount == maxCount) { + if (it.UserData()->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 (auto it = mPendingTransactionTable.ConstIter(); !it.Done(); it.Next()) { + length += it.UserData()->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 (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) { + LOG(("] window id = %" PRIx64 " queue [", it.Key())); + for (const auto& info : *it.UserData()) { + LOG((" %p", info->Transaction())); + } + } + LOG(("]")); +} + +void PendingTransactionQueue::Compact() { + mUrgentStartQ.Compact(); + for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) { + it.UserData()->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 (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) { + for (const auto& pendingTransInfo : *it.UserData()) { + LOG(("PendingTransactionQueue::CancelAllTransactions %p\n", + pendingTransInfo->Transaction())); + pendingTransInfo->Transaction()->Close(reason); + } + it.UserData()->Clear(); + } + + mPendingTransactionTable.Clear(); +} + +} // namespace net +} // namespace mozilla |