--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -82,30 +82,16 @@ LazyLogModule gHostResolverLog("nsHostRe
}
#define LOG_HOST(host, interface) host, \
(interface && interface[0] != '\0') ? " on interface " : "", \
(interface && interface[0] != '\0') ? interface : ""
//----------------------------------------------------------------------------
-static inline void
-MoveCList(PRCList &from, PRCList &to)
-{
- if (!PR_CLIST_IS_EMPTY(&from)) {
- to.next = from.next;
- to.prev = from.prev;
- to.next->prev = &to;
- to.prev->next = &to;
- PR_INIT_CLIST(&from);
- }
-}
-
-//----------------------------------------------------------------------------
-
#if defined(RES_RETRY_ON_FAILURE)
// this class represents the resolver state for a given thread. if we
// encounter a lookup failure, then we can invoke the Reset method on an
// instance of this class to reset the resolver (in case /etc/resolv.conf
// for example changed). this is mainly an issue on GNU systems since glibc
// only reads in /etc/resolv.conf once per thread. it may be an issue on
// other systems as well.
@@ -215,17 +201,16 @@ nsHostRecord::nsHostRecord(const nsHostK
, mDoomed(false)
, mDidCallbacks(false)
, mGetTtl(false)
, mTrrAUsed(INIT)
, mTrrAAAAUsed(INIT)
, mBlacklistedCount(0)
, mResolveAgain(false)
{
- PR_INIT_CLIST(this);
}
void
nsHostRecord::Cancel()
{
if (mTrrA) {
mTrrA->Cancel();
}
@@ -530,20 +515,16 @@ nsHostResolver::nsHostResolver(uint32_t
, mEvictionQSize(0)
, mShutdown(true)
, mNumIdleThreads(0)
, mThreadCount(0)
, mActiveAnyThreadCount(0)
, mPendingCount(0)
{
mCreationTime = PR_Now();
- PR_INIT_CLIST(&mHighQ);
- PR_INIT_CLIST(&mMediumQ);
- PR_INIT_CLIST(&mLowQ);
- PR_INIT_CLIST(&mEvictionQ);
mLongIdleTimeout = PR_SecondsToInterval(LongIdleTimeoutSeconds);
mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
}
nsHostResolver::~nsHostResolver() = default;
nsresult
@@ -585,25 +566,22 @@ nsHostResolver::Init()
res_ninit(&_res);
}
#endif
return NS_OK;
}
void
-nsHostResolver::ClearPendingQueue(PRCList *aPendingQ)
+nsHostResolver::ClearPendingQueue(LinkedList<RefPtr<nsHostRecord>>& aPendingQ)
{
// loop through pending queue, erroring out pending lookups.
- if (!PR_CLIST_IS_EMPTY(aPendingQ)) {
- PRCList *node = aPendingQ->next;
- while (node != aPendingQ) {
- RefPtr<nsHostRecord> rec = dont_AddRef(static_cast<nsHostRecord *>(node));
+ if (!aPendingQ.isEmpty()) {
+ for (RefPtr<nsHostRecord> rec : aPendingQ) {
rec->Cancel();
- node = node->next;
CompleteLookup(rec, NS_ERROR_ABORT, nullptr, rec->pb);
}
}
}
//
// FlushCache() is what we call when the network has changed. We must not
// trust names that were resolved before this change. They may resolve
@@ -617,34 +595,32 @@ nsHostResolver::ClearPendingQueue(PRCLis
void
nsHostResolver::FlushCache()
{
MutexAutoLock lock(mLock);
mEvictionQSize = 0;
// Clear the evictionQ and remove all its corresponding entries from
// the cache first
- if (!PR_CLIST_IS_EMPTY(&mEvictionQ)) {
- PRCList *node = mEvictionQ.next;
- while (node != &mEvictionQ) {
- nsHostRecord *rec = static_cast<nsHostRecord *>(node);
+ if (!mEvictionQ.isEmpty()) {
+ for (RefPtr<nsHostRecord> rec : mEvictionQ) {
rec->Cancel();
- node = node->next;
- PR_REMOVE_AND_INIT_LINK(rec);
mRecordDB.Remove(*static_cast<nsHostKey *>(rec));
- NS_RELEASE(rec);
}
+ mEvictionQ.clear();
}
// Refresh the cache entries that are resolving RIGHT now, remove the rest.
for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
nsHostRecord* record = iter.UserData();
// Try to remove the record, or mark it for refresh.
if (record->RemoveOrRefresh()) {
- PR_REMOVE_LINK(record);
+ if (record->isInList()) {
+ record->remove();
+ }
iter.Remove();
}
}
}
void
nsHostResolver::Shutdown()
{
@@ -652,55 +628,54 @@ nsHostResolver::Shutdown()
{
DebugOnly<nsresult> rv = Preferences::UnregisterCallback(
&DnsPrefChanged, kPrefGetTtl, this);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Could not unregister DNS TTL pref callback.");
}
- PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
- PR_INIT_CLIST(&pendingQHigh);
- PR_INIT_CLIST(&pendingQMed);
- PR_INIT_CLIST(&pendingQLow);
- PR_INIT_CLIST(&evictionQ);
+ LinkedList<RefPtr<nsHostRecord>> pendingQHigh, pendingQMed, pendingQLow, evictionQ;
{
MutexAutoLock lock(mLock);
mShutdown = true;
- MoveCList(mHighQ, pendingQHigh);
- MoveCList(mMediumQ, pendingQMed);
- MoveCList(mLowQ, pendingQLow);
- MoveCList(mEvictionQ, evictionQ);
+ // Move queues to temporary lists.
+ pendingQHigh = mozilla::Move(mHighQ);
+ pendingQMed = mozilla::Move(mMediumQ);
+ pendingQLow = mozilla::Move(mLowQ);
+ evictionQ = mozilla::Move(mEvictionQ);
+
mEvictionQSize = 0;
mPendingCount = 0;
if (mNumIdleThreads)
mIdleThreadCV.NotifyAll();
// empty host database
mRecordDB.Clear();
}
- ClearPendingQueue(&pendingQHigh);
- ClearPendingQueue(&pendingQMed);
- ClearPendingQueue(&pendingQLow);
+ ClearPendingQueue(pendingQHigh);
+ ClearPendingQueue(pendingQMed);
+ ClearPendingQueue(pendingQLow);
- if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
- PRCList *node = evictionQ.next;
- while (node != &evictionQ) {
- nsHostRecord *rec = static_cast<nsHostRecord *>(node);
+ if (!evictionQ.isEmpty()) {
+ for (RefPtr<nsHostRecord> rec : evictionQ) {
rec->Cancel();
- node = node->next;
- NS_RELEASE(rec);
}
}
+ pendingQHigh.clear();
+ pendingQMed.clear();
+ pendingQLow.clear();
+ evictionQ.clear();
+
for (auto iter = mRecordDB.Iter(); !iter.Done(); iter.Next()) {
iter.UserData()->Cancel();
}
#ifdef NS_BUILD_REFCNT_LOGGING
// Logically join the outstanding worker threads with a timeout.
// Use this approach instead of PR_JoinThread() because that does
// not allow a timeout which may be necessary for a semi-responsive
@@ -716,25 +691,16 @@ nsHostResolver::Shutdown()
{
mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to shutdown GetAddrInfo");
}
}
-void
-nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ)
-{
- NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued");
-
- PR_REMOVE_LINK(aRec);
- PR_APPEND_LINK(aRec, &aDestQ);
-}
-
nsresult
nsHostResolver::GetHostRecord(const char *host,
uint16_t flags, uint16_t af, bool pb,
const nsCString &netInterface,
const nsCString &originSuffix,
nsHostRecord **result)
{
MutexAutoLock lock(mLock);
@@ -978,23 +944,27 @@ nsHostResolver::ResolveHost(const char
// Consider the case where we are on a pending queue of
// lower priority than the request is being made at.
// In that case we should upgrade to the higher queue.
if (IsHighPriority(flags) &&
!IsHighPriority(rec->flags)) {
// Move from (low|med) to high.
- MoveQueue(rec, mHighQ);
+ NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
+ rec->remove();
+ mHighQ.insertBack(rec);
rec->flags = flags;
ConditionallyCreateThread(rec);
} else if (IsMediumPriority(flags) &&
IsLowPriority(rec->flags)) {
// Move from low to med.
- MoveQueue(rec, mMediumQ);
+ NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
+ rec->remove();
+ mMediumQ.insertBack(rec);
rec->flags = flags;
mIdleThreadCV.Notify();
}
}
}
}
}
@@ -1092,34 +1062,35 @@ nsHostResolver::TrrLookup_unlocked(nsHos
{
MutexAutoLock lock(mLock);
return TrrLookup(rec, pushedTRR);
}
// returns error if no TRR resolve is issued
// it is impt this is not called while a native lookup is going on
nsresult
-nsHostResolver::TrrLookup(nsHostRecord *rec, TRR *pushedTRR)
+nsHostResolver::TrrLookup(nsHostRecord *aRec, TRR *pushedTRR)
{
+ RefPtr<nsHostRecord> rec(aRec);
mLock.AssertCurrentThreadOwns();
MOZ_ASSERT(!TRROutstanding());
MOZ_ASSERT(!rec->mResolving);
if (!gTRRService || !gTRRService->Enabled()) {
LOG(("TrrLookup:: %s service not enabled\n", rec->host.get()));
return NS_ERROR_UNKNOWN_HOST;
}
- if (rec->next != rec) {
+ if (rec->isInList()) {
// we're already on the eviction queue. This is a renewal
MOZ_ASSERT(mEvictionQSize);
- AssertOnQ(rec, &mEvictionQ);
- PR_REMOVE_AND_INIT_LINK(rec);
+ AssertOnQ(rec, mEvictionQ);
+
+ rec->remove();
mEvictionQSize--;
- rec->Release();
}
rec->mTRRSuccess = 0; // bump for each successful TRR response
rec->mTrrAUsed = nsHostRecord::INIT;
rec->mTrrAAAAUsed = nsHostRecord::INIT;
if (gTRRService && gTRRService->IsTRRBlacklisted(rec->host, rec->pb, true)) {
Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED, true);
@@ -1167,58 +1138,57 @@ nsHostResolver::TrrLookup(nsHostRecord *
}
}
} while (sendAgain);
return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
}
void
-nsHostResolver::AssertOnQ(nsHostRecord *rec, PRCList *q)
+nsHostResolver::AssertOnQ(nsHostRecord *rec, LinkedList<RefPtr<nsHostRecord>>& q)
{
#ifdef DEBUG
- MOZ_ASSERT(!PR_CLIST_IS_EMPTY(q));
- nsHostRecord *i = static_cast<nsHostRecord *>(PR_LIST_HEAD(q));
- while (i != rec) {
- MOZ_ASSERT(i->next != q);
- i = static_cast<nsHostRecord *>(i->next);
+ MOZ_ASSERT(!q.isEmpty());
+ MOZ_ASSERT(rec->isInList());
+ for (RefPtr<nsHostRecord> r: q) {
+ if (rec == r) {
+ return;
+ }
}
+ MOZ_ASSERT(false, "Did not find element");
#endif
}
nsresult
-nsHostResolver::NativeLookup(nsHostRecord *rec)
+nsHostResolver::NativeLookup(nsHostRecord *aRec)
{
mLock.AssertCurrentThreadOwns();
+ RefPtr<nsHostRecord> rec(aRec);
rec->mNativeStart = TimeStamp::Now();
// Add rec to one of the pending queues, possibly removing it from mEvictionQ.
- // If rec is on mEvictionQ, then we can just move the owning
- // reference over to the new active queue.
- if (rec->next == rec) { // not on a pending queue
- NS_ADDREF(rec);
- } else {
+ if (rec->isInList()) {
MOZ_ASSERT(mEvictionQSize);
- AssertOnQ(rec, &mEvictionQ);
- PR_REMOVE_AND_INIT_LINK(rec); // was on the eviction queue
+ AssertOnQ(rec, mEvictionQ);
+ rec->remove(); // was on the eviction queue
mEvictionQSize--;
}
switch (nsHostRecord::GetPriority(rec->flags)) {
case nsHostRecord::DNS_PRIORITY_HIGH:
- PR_APPEND_LINK(rec, &mHighQ);
+ mHighQ.insertBack(rec);
break;
case nsHostRecord::DNS_PRIORITY_MEDIUM:
- PR_APPEND_LINK(rec, &mMediumQ);
+ mMediumQ.insertBack(rec);
break;
case nsHostRecord::DNS_PRIORITY_LOW:
- PR_APPEND_LINK(rec, &mLowQ);
+ mLowQ.insertBack(rec);
break;
}
mPendingCount++;
rec->mNative = true;
rec->mNativeUsed = true;
rec->onQueue = true;
rec->mResolving++;
@@ -1300,21 +1270,21 @@ nsHostResolver::ConditionallyRefreshReco
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_RENEWAL);
}
}
return NS_OK;
}
void
-nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
+nsHostResolver::DeQueue(LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult)
{
- *aResult = static_cast<nsHostRecord *>(aQ.next);
- PR_REMOVE_AND_INIT_LINK(*aResult);
+ RefPtr<nsHostRecord> rec = aQ.popFirst();
mPendingCount--;
+ rec.forget(aResult);
(*aResult)->onQueue = false;
}
bool
nsHostResolver::GetHostToLookup(nsHostRecord **result)
{
bool timedOut = false;
PRIntervalTime epoch, now, timeout;
@@ -1324,32 +1294,32 @@ nsHostResolver::GetHostToLookup(nsHostRe
timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
epoch = PR_IntervalNow();
while (!mShutdown) {
// remove next record from Q; hand over owning reference. Check high, then med, then low
#define SET_GET_TTL(var, val) (var)->mGetTtl = sGetTtlEnabled && (val)
- if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
+ if (!mHighQ.isEmpty()) {
DeQueue (mHighQ, result);
SET_GET_TTL(*result, false);
return true;
}
if (mActiveAnyThreadCount < HighThreadThreshold) {
- if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
+ if (!mMediumQ.isEmpty()) {
DeQueue (mMediumQ, result);
mActiveAnyThreadCount++;
(*result)->usingAnyThread = true;
SET_GET_TTL(*result, true);
return true;
}
- if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
+ if (!mLowQ.isEmpty()) {
DeQueue (mLowQ, result);
mActiveAnyThreadCount++;
(*result)->usingAnyThread = true;
SET_GET_TTL(*result, true);
return true;
}
}
@@ -1655,35 +1625,31 @@ nsHostResolver::CompleteLookup(nsHostRec
}
rec->mDidCallbacks = true;
}
if (!rec->mResolving && !mShutdown) {
rec->ResolveComplete();
// add to mEvictionQ
- MOZ_ASSERT(rec->next == rec && rec->prev == rec); // not on a queue
- PR_APPEND_LINK(rec, &mEvictionQ);
- rec->AddRef();
+ MOZ_ASSERT(!rec->isInList());
+ mEvictionQ.insertBack(rec);
if (mEvictionQSize < mMaxCacheEntries) {
mEvictionQSize++;
} else {
// remove first element on mEvictionQ
- nsHostRecord *head =
- static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
- PR_REMOVE_AND_INIT_LINK(head);
- mRecordDB.Remove(*static_cast<nsHostKey *>(head));
+ RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
+ mRecordDB.Remove(*static_cast<nsHostKey *>(head.get()));
if (!head->negative) {
// record the age of the entry upon eviction.
TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
static_cast<uint32_t>(age.ToSeconds() / 60));
}
- head->Release(); // release reference owned by mEvictionQ
}
}
#ifdef DNSQUERY_AVAILABLE
// Unless the result is from TRR, resolve again to get TTL
bool fromTRR = false;
{
MutexAutoLock lock(rec->addr_info_lock);
@@ -1740,19 +1706,18 @@ nsHostResolver::CancelAsyncRequest(const
break;
}
}
// If there are no more callbacks, remove the hash table entry
if (recPtr && recPtr->mCallbacks.isEmpty()) {
mRecordDB.Remove(*static_cast<nsHostKey *>(recPtr));
// If record is on a Queue, remove it and then deref it
- if (recPtr->next != recPtr) {
- PR_REMOVE_AND_INIT_LINK(recPtr);
- NS_RELEASE(recPtr);
+ if (recPtr->isInList()) {
+ recPtr->remove();
}
}
}
}
size_t
nsHostResolver::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
{
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -2,17 +2,16 @@
/* 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/. */
#ifndef nsHostResolver_h__
#define nsHostResolver_h__
#include "nscore.h"
-#include "prclist.h"
#include "prnetdb.h"
#include "PLDHashTable.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "nsISupportsImpl.h"
#include "nsIDNSListener.h"
#include "nsIDNSService.h"
#include "nsTArray.h"
@@ -70,17 +69,19 @@ struct nsHostKey
bool operator==(const nsHostKey& other) const;
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
PLDHashNumber Hash() const;
};
/**
* nsHostRecord - ref counted object type stored in host resolver cache.
*/
-class nsHostRecord : public PRCList, public nsHostKey
+class nsHostRecord :
+ public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
+ public nsHostKey
{
typedef mozilla::Mutex Mutex;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostRecord)
/* a fully resolved host record has either a non-null |addr_info| or |addr|
* field. if |addr_info| is null, it implies that the |host| is an IP
@@ -389,37 +390,40 @@ public:
private:
explicit nsHostResolver(uint32_t maxCacheEntries,
uint32_t defaultCacheEntryLifetime,
uint32_t defaultGracePeriod);
virtual ~nsHostResolver();
nsresult Init();
- void AssertOnQ(nsHostRecord *, PRCList *);
+ // In debug builds it asserts that the element is in the list.
+ void AssertOnQ(nsHostRecord *, mozilla::LinkedList<RefPtr<nsHostRecord>>&);
mozilla::net::ResolverMode Mode();
nsresult NativeLookup(nsHostRecord *);
nsresult TrrLookup(nsHostRecord *, mozilla::net::TRR *pushedTRR = nullptr);
// Kick-off a name resolve operation, using native resolver and/or TRR
nsresult NameLookup(nsHostRecord *);
bool GetHostToLookup(nsHostRecord **m);
- void DeQueue(PRCList &aQ, nsHostRecord **aResult);
- void ClearPendingQueue(PRCList *aPendingQueue);
+ // Removes the first element from the list and returns it AddRef-ed in aResult
+ // Should not be called for an empty linked list.
+ void DeQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aQ, nsHostRecord **aResult);
+ // Cancels host records in the pending queue and also
+ // calls CompleteLookup with the NS_ERROR_ABORT result code.
+ void ClearPendingQueue(mozilla::LinkedList<RefPtr<nsHostRecord>>& aPendingQ);
nsresult ConditionallyCreateThread(nsHostRecord *rec);
/**
* Starts a new lookup in the background for entries that are in the grace
* period with a failed connect or all cached entries are negative.
*/
nsresult ConditionallyRefreshRecord(nsHostRecord *rec, const char *host);
- static void MoveQueue(nsHostRecord *aRec, PRCList &aDestQ);
-
static void ThreadFunc(void *);
enum {
METHOD_HIT = 1,
METHOD_RENEWAL = 2,
METHOD_NEGATIVE_HIT = 3,
METHOD_LITERAL = 4,
METHOD_OVERFLOW = 5,
@@ -428,20 +432,20 @@ private:
};
uint32_t mMaxCacheEntries;
uint32_t mDefaultCacheLifetime; // granularity seconds
uint32_t mDefaultGracePeriod; // granularity seconds
mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const
CondVar mIdleThreadCV;
nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB;
- PRCList mHighQ;
- PRCList mMediumQ;
- PRCList mLowQ;
- PRCList mEvictionQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ;
+ mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
uint32_t mEvictionQSize;
PRTime mCreationTime;
PRIntervalTime mLongIdleTimeout;
PRIntervalTime mShortIdleTimeout;
mozilla::Atomic<bool> mShutdown;
mozilla::Atomic<uint32_t> mNumIdleThreads;
mozilla::Atomic<uint32_t> mThreadCount;