tor-browser

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

HostRecordQueue.cpp (7364B)


      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 #include "HostRecordQueue.h"
      7 #include "mozilla/glean/NetwerkDnsMetrics.h"
      8 #include "nsQueryObject.h"
      9 
     10 namespace mozilla {
     11 namespace net {
     12 
     13 void HostRecordQueue::InsertRecord(nsHostRecord* aRec,
     14                                   nsIDNSService::DNSFlags aFlags,
     15                                   const MutexAutoLock& aProofOfLock) {
     16  if (aRec->isInList()) {
     17    MOZ_DIAGNOSTIC_ASSERT(!mEvictionQ.contains(aRec),
     18                          "Already in eviction queue");
     19    MOZ_DIAGNOSTIC_ASSERT(!mHighQ.contains(aRec), "Already in high queue");
     20    MOZ_DIAGNOSTIC_ASSERT(!mMediumQ.contains(aRec), "Already in med queue");
     21    MOZ_DIAGNOSTIC_ASSERT(!mLowQ.contains(aRec), "Already in low queue");
     22    MOZ_DIAGNOSTIC_CRASH("Already on some other queue?");
     23  }
     24 
     25  switch (AddrHostRecord::GetPriority(aFlags)) {
     26    case AddrHostRecord::DNS_PRIORITY_HIGH:
     27      mHighQ.insertBack(aRec);
     28      break;
     29 
     30    case AddrHostRecord::DNS_PRIORITY_MEDIUM:
     31      mMediumQ.insertBack(aRec);
     32      break;
     33 
     34    case AddrHostRecord::DNS_PRIORITY_LOW:
     35      mLowQ.insertBack(aRec);
     36      break;
     37  }
     38  mPendingCount++;
     39 }
     40 
     41 void HostRecordQueue::AddToEvictionQ(
     42    nsHostRecord* aRec, uint32_t aMaxCacheEntries,
     43    nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
     44    const MutexAutoLock& aProofOfLock) {
     45  if (aRec->isInList()) {
     46    bool inEvictionQ = mEvictionQ.contains(aRec);
     47    MOZ_DIAGNOSTIC_ASSERT(!inEvictionQ, "Already in eviction queue");
     48    bool inHighQ = mHighQ.contains(aRec);
     49    MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue");
     50    bool inMediumQ = mMediumQ.contains(aRec);
     51    MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue");
     52    bool inLowQ = mLowQ.contains(aRec);
     53    MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue");
     54    MOZ_DIAGNOSTIC_CRASH("Already on some other queue?");
     55 
     56    // Bug 1678117 - it's not clear why this can happen, but let's fix it
     57    // for release users.
     58    aRec->remove();
     59    if (inEvictionQ) {
     60      MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0);
     61      mEvictionQSize--;
     62    } else if (inHighQ || inMediumQ || inLowQ) {
     63      MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
     64      mPendingCount--;
     65    }
     66  }
     67  mEvictionQ.insertBack(aRec);
     68  if (mEvictionQSize < aMaxCacheEntries) {
     69    mEvictionQSize++;
     70  } else {
     71    // remove first element on mEvictionQ
     72    RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
     73    aDB.Remove(*static_cast<nsHostKey*>(head.get()));
     74 
     75    if (!head->negative) {
     76      // record the age of the entry upon eviction.
     77      TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
     78      if (aRec->IsAddrRecord()) {
     79        glean::dns::cleanup_age.AccumulateRawDuration(age);
     80      } else {
     81        glean::dns::by_type_cleanup_age.AccumulateRawDuration(age);
     82      }
     83      if (head->CheckExpiration(TimeStamp::Now()) !=
     84          nsHostRecord::EXP_EXPIRED) {
     85        if (aRec->IsAddrRecord()) {
     86          glean::dns::premature_eviction.AccumulateRawDuration(age);
     87        } else {
     88          glean::dns::by_type_premature_eviction.AccumulateRawDuration(age);
     89        }
     90      }
     91    }
     92  }
     93 }
     94 
     95 void HostRecordQueue::MoveToEvictionQueueTail(
     96    nsHostRecord* aRec, const MutexAutoLock& aProofOfLock) {
     97  bool inEvictionQ = mEvictionQ.contains(aRec);
     98  if (!inEvictionQ) {
     99    // Note: this function can be called when the record isn’t in the
    100    // mEvictionQ. For example, if we immediately start a TTL lookup (see
    101    // nsHostResolver::CompleteLookupLocked), the record may not be in
    102    // mEvictionQ.
    103    return;
    104  }
    105 
    106  aRec->remove();
    107  mEvictionQ.insertBack(aRec);
    108 }
    109 
    110 void HostRecordQueue::MaybeRenewHostRecord(nsHostRecord* aRec,
    111                                           const MutexAutoLock& aProofOfLock) {
    112  if (!aRec->isInList()) {
    113    return;
    114  }
    115 
    116  bool inEvictionQ = mEvictionQ.contains(aRec);
    117  MOZ_DIAGNOSTIC_ASSERT(inEvictionQ, "Should be in eviction queue");
    118  bool inHighQ = mHighQ.contains(aRec);
    119  MOZ_DIAGNOSTIC_ASSERT(!inHighQ, "Already in high queue");
    120  bool inMediumQ = mMediumQ.contains(aRec);
    121  MOZ_DIAGNOSTIC_ASSERT(!inMediumQ, "Already in med queue");
    122  bool inLowQ = mLowQ.contains(aRec);
    123  MOZ_DIAGNOSTIC_ASSERT(!inLowQ, "Already in low queue");
    124 
    125  // we're already on the eviction queue. This is a renewal
    126  aRec->remove();
    127  if (inEvictionQ) {
    128    MOZ_DIAGNOSTIC_ASSERT(mEvictionQSize > 0);
    129    mEvictionQSize--;
    130  } else if (inHighQ || inMediumQ || inLowQ) {
    131    MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
    132    mPendingCount--;
    133  }
    134 }
    135 
    136 void HostRecordQueue::FlushEvictionQ(
    137    nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord>& aDB,
    138    const MutexAutoLock& aProofOfLock) {
    139  mEvictionQSize = 0;
    140 
    141  // Clear the evictionQ and remove all its corresponding entries from
    142  // the cache first
    143  if (!mEvictionQ.isEmpty()) {
    144    for (const RefPtr<nsHostRecord>& rec : mEvictionQ) {
    145      rec->Cancel();
    146      aDB.Remove(*static_cast<nsHostKey*>(rec));
    147    }
    148    mEvictionQ.clear();
    149  }
    150 }
    151 
    152 void HostRecordQueue::MaybeRemoveFromQ(nsHostRecord* aRec,
    153                                       const MutexAutoLock& aProofOfLock) {
    154  if (!aRec->isInList()) {
    155    return;
    156  }
    157 
    158  if (mHighQ.contains(aRec) || mMediumQ.contains(aRec) ||
    159      mLowQ.contains(aRec)) {
    160    mPendingCount--;
    161  } else if (mEvictionQ.contains(aRec)) {
    162    mEvictionQSize--;
    163  } else {
    164    MOZ_ASSERT(false, "record is in other queue");
    165  }
    166 
    167  aRec->remove();
    168 }
    169 
    170 void HostRecordQueue::MoveToAnotherPendingQ(nsHostRecord* aRec,
    171                                            nsIDNSService::DNSFlags aFlags,
    172                                            const MutexAutoLock& aProofOfLock) {
    173  if (!(mHighQ.contains(aRec) || mMediumQ.contains(aRec) ||
    174        mLowQ.contains(aRec))) {
    175    MOZ_ASSERT(false, "record is not in the pending queue");
    176    return;
    177  }
    178 
    179  aRec->remove();
    180  // We just removed from pending queue. Insert record will
    181  // increment this value again.
    182  mPendingCount--;
    183 
    184  InsertRecord(aRec, aFlags, aProofOfLock);
    185 }
    186 
    187 already_AddRefed<nsHostRecord> HostRecordQueue::Dequeue(
    188    bool aHighQOnly, const MutexAutoLock& aProofOfLock) {
    189  RefPtr<nsHostRecord> rec;
    190  if (!mHighQ.isEmpty()) {
    191    rec = mHighQ.popFirst();
    192  } else if (!mMediumQ.isEmpty() && !aHighQOnly) {
    193    rec = mMediumQ.popFirst();
    194  } else if (!mLowQ.isEmpty() && !aHighQOnly) {
    195    rec = mLowQ.popFirst();
    196  }
    197 
    198  if (rec) {
    199    mPendingCount--;
    200  }
    201 
    202  return rec.forget();
    203 }
    204 
    205 void HostRecordQueue::ClearAll(
    206    const std::function<void(nsHostRecord*)>& aCallback,
    207    const MutexAutoLock& aProofOfLock) {
    208  mPendingCount = 0;
    209 
    210  auto clearPendingQ = [&](LinkedList<RefPtr<nsHostRecord>>& aPendingQ) {
    211    if (aPendingQ.isEmpty()) {
    212      return;
    213    }
    214 
    215    // loop through pending queue, erroring out pending lookups.
    216    for (const RefPtr<nsHostRecord>& rec : aPendingQ) {
    217      rec->Cancel();
    218      aCallback(rec);
    219    }
    220    aPendingQ.clear();
    221  };
    222 
    223  clearPendingQ(mHighQ);
    224  clearPendingQ(mMediumQ);
    225  clearPendingQ(mLowQ);
    226 
    227  mEvictionQSize = 0;
    228  if (!mEvictionQ.isEmpty()) {
    229    for (const RefPtr<nsHostRecord>& rec : mEvictionQ) {
    230      rec->Cancel();
    231    }
    232  }
    233 
    234  mEvictionQ.clear();
    235 }
    236 
    237 }  // namespace net
    238 }  // namespace mozilla