tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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