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