Backed out 2 changesets (bug 1424834) for leaks at netwerk/dns/nsHostResolver.cpp:352 r=backout a=backout
authorCosmin Sabou <csabou@mozilla.com>
Mon, 18 Dec 2017 11:52:06 +0200
changeset 396680 5572465c08a9
parent 396679 3a842d55ff1f
child 396681 be9d30ca06ec
child 396690 2fcff634ec0e
child 396709 59a2346b07f7
push id33107
push usercsabou@mozilla.com
push dateMon, 18 Dec 2017 09:52:35 +0000
treeherdermozilla-central@5572465c08a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout, backout
bugs1424834
milestone59.0a1
backs out2503df83bbd9
49cc08386f17
first release with
nightly linux32
5572465c08a9 / 59.0a1 / 20171218100313 / files
nightly linux64
5572465c08a9 / 59.0a1 / 20171218100313 / files
nightly mac
5572465c08a9 / 59.0a1 / 20171218100313 / files
nightly win32
5572465c08a9 / 59.0a1 / 20171218100313 / files
nightly win64
5572465c08a9 / 59.0a1 / 20171218100313 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 2 changesets (bug 1424834) for leaks at netwerk/dns/nsHostResolver.cpp:352 r=backout a=backout Backed out changeset 2503df83bbd9 (bug 1424834) Backed out changeset 49cc08386f17 (bug 1424834)
mfbt/LinkedList.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -546,17 +546,17 @@ public:
    * Measures the memory consumption of the list excluding |this|.  Note that
    * it only measures the list elements themselves.  If the list elements
    * contain pointers to other memory blocks, those blocks must be measured
    * separately during a subsequent iteration over the list.
    */
   size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     size_t n = 0;
-    for (ConstRawType t = getFirst(); t; t = t->getNext()) {
+    for (const T* t = getFirst(); t; t = t->getNext()) {
       n += aMallocSizeOf(t);
     }
     return n;
   }
 
   /*
    * Like sizeOfExcludingThis(), but measures |this| as well.
    */
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -290,16 +290,18 @@ nsDNSRecord::ReportUnusable(uint16_t aPo
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 class nsDNSAsyncRequest final : public nsResolveHostCallback
                               , public nsICancelable
 {
+    ~nsDNSAsyncRequest() = default;
+
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSICANCELABLE
 
     nsDNSAsyncRequest(nsHostResolver   *res,
                       const nsACString &host,
                       const OriginAttributes &attrs,
                       nsIDNSListener   *listener,
@@ -324,38 +326,38 @@ public:
 
     RefPtr<nsHostResolver> mResolver;
     nsCString                mHost; // hostname we're resolving
     const OriginAttributes   mOriginAttributes; // The originAttributes for this resolving
     nsCOMPtr<nsIDNSListener> mListener;
     uint16_t                 mFlags;
     uint16_t                 mAF;
     nsCString                mNetworkInterface;
-private:
-    virtual ~nsDNSAsyncRequest() = default;
 };
 
-NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
-
 void
 nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver *resolver,
                                          nsHostRecord   *hostRecord,
                                          nsresult        status)
 {
     // need to have an owning ref when we issue the callback to enable
     // the caller to be able to addref/release multiple times without
     // destroying the record prematurely.
     nsCOMPtr<nsIDNSRecord> rec;
     if (NS_SUCCEEDED(status)) {
         NS_ASSERTION(hostRecord, "no host record");
         rec = new nsDNSRecord(hostRecord);
     }
 
     mListener->OnLookupComplete(this, rec, status);
     mListener = nullptr;
+
+    // release the reference to ourselves that was added before we were
+    // handed off to the host resolver.
+    NS_RELEASE_THIS();
 }
 
 bool
 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
 {
     nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
     if (wrapper) {
         nsCOMPtr<nsIDNSListener> originalListener;
@@ -373,53 +375,50 @@ nsDNSAsyncRequest::SizeOfIncludingThis(M
     // The following fields aren't measured.
     // - mHost, because it's a non-owning pointer
     // - mResolver, because it's a non-owning pointer
     // - mListener, because it's a non-owning pointer
 
     return n;
 }
 
+NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
+
 NS_IMETHODIMP
 nsDNSAsyncRequest::Cancel(nsresult reason)
 {
     NS_ENSURE_ARG(NS_FAILED(reason));
     mResolver->DetachCallback(mHost.get(), mOriginAttributes, mFlags, mAF,
                               mNetworkInterface.get(), this, reason);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
-class nsDNSSyncRequest
-    : public nsResolveHostCallback
+class nsDNSSyncRequest : public nsResolveHostCallback
 {
-    NS_DECL_THREADSAFE_ISUPPORTS
 public:
     explicit nsDNSSyncRequest(PRMonitor *mon)
         : mDone(false)
         , mStatus(NS_OK)
         , mMonitor(mon) {}
+    virtual ~nsDNSSyncRequest() = default;
 
     void OnResolveHostComplete(nsHostResolver *, nsHostRecord *, nsresult) override;
     bool EqualsAsyncListener(nsIDNSListener *aListener) override;
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
 
     bool                   mDone;
     nsresult               mStatus;
     RefPtr<nsHostRecord> mHostRecord;
 
 private:
-    virtual ~nsDNSSyncRequest() = default;
-
     PRMonitor             *mMonitor;
 };
 
-NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)
-
 void
 nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver *resolver,
                                         nsHostRecord   *hostRecord,
                                         nsresult        status)
 {
     // store results, and wake up nsDNSService::Resolve to process results.
     PR_EnterMonitor(mMonitor);
     mDone = true;
@@ -794,27 +793,26 @@ nsDNSService::AsyncResolveExtended(const
                                       listener, target_, attrs,
                                       result);
 }
 
 NS_IMETHODIMP
 nsDNSService::AsyncResolveExtendedNative(const nsACString        &aHostname,
                                          uint32_t                 flags,
                                          const nsACString        &aNetworkInterface,
-                                         nsIDNSListener          *aListener,
+                                         nsIDNSListener          *listener,
                                          nsIEventTarget          *target_,
                                          const OriginAttributes  &aOriginAttributes,
                                          nsICancelable          **result)
 {
     // grab reference to global host resolver and IDN service.  beware
     // simultaneous shutdown!!
     RefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     nsCOMPtr<nsIEventTarget> target = target_;
-    nsCOMPtr<nsIDNSListener> listener = aListener;
     bool localDomain = false;
     {
         MutexAutoLock lock(mLock);
 
         if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
             return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
 
         res = mResolver;
@@ -847,26 +845,31 @@ nsDNSService::AsyncResolveExtendedNative
     }
 
     if (target) {
         listener = new DNSListenerProxy(listener, target);
     }
 
     uint16_t af = GetAFForLookup(hostname, flags);
 
-    MOZ_ASSERT(listener);
-    RefPtr<nsDNSAsyncRequest> req =
+    auto *req =
         new nsDNSAsyncRequest(res, hostname, aOriginAttributes, listener, flags, af,
                               aNetworkInterface);
     if (!req)
         return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(*result = req);
 
+    // addref for resolver; will be released when OnResolveHostComplete is called.
+    NS_ADDREF(req);
     rv = res->ResolveHost(req->mHost.get(), req->mOriginAttributes, flags, af,
                           req->mNetworkInterface.get(), req);
-    req.forget(result);
+    if (NS_FAILED(rv)) {
+        NS_RELEASE(req);
+        NS_RELEASE(*result);
+    }
     return rv;
 }
 
 NS_IMETHODIMP
 nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
                                  uint32_t          aFlags,
                                  nsIDNSListener   *aListener,
                                  nsresult          aReason,
@@ -1047,33 +1050,35 @@ nsDNSService::ResolveInternal(const nsAC
     // we need to use a monitor! ;-)
     //
 
     PRMonitor *mon = PR_NewMonitor();
     if (!mon)
         return NS_ERROR_OUT_OF_MEMORY;
 
     PR_EnterMonitor(mon);
-    RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon);
+    nsDNSSyncRequest syncReq(mon);
 
     uint16_t af = GetAFForLookup(hostname, flags);
 
-    rv = res->ResolveHost(hostname.get(), aOriginAttributes, flags, af, "", syncReq);
+    rv = res->ResolveHost(hostname.get(), aOriginAttributes, flags, af, "", &syncReq);
     if (NS_SUCCEEDED(rv)) {
         // wait for result
-        while (!syncReq->mDone) {
+        while (!syncReq.mDone)
             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
-        }
 
-        if (NS_FAILED(syncReq->mStatus)) {
-            rv = syncReq->mStatus;
-        } else {
-            NS_ASSERTION(syncReq->mHostRecord, "no host record");
-            RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord);
-            rec.forget(result);
+        if (NS_FAILED(syncReq.mStatus))
+            rv = syncReq.mStatus;
+        else {
+            NS_ASSERTION(syncReq.mHostRecord, "no host record");
+            auto *rec = new nsDNSRecord(syncReq.mHostRecord);
+            if (!rec)
+                rv = NS_ERROR_OUT_OF_MEMORY;
+            else
+                NS_ADDREF(*result = rec);
         }
     }
 
     PR_ExitMonitor(mon);
     PR_DestroyMonitor(mon);
     return rv;
 }
 
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -184,16 +184,17 @@ nsHostRecord::nsHostRecord(const nsHostK
     af = key->af;
     netInterface = host + strlen(key->host) + 1;
     memcpy((char *) netInterface, key->netInterface,
            strlen(key->netInterface) + 1);
     originSuffix = netInterface + strlen(key->netInterface) + 1;
     memcpy((char *) originSuffix, key->originSuffix,
            strlen(key->originSuffix) + 1);
     PR_INIT_CLIST(this);
+    PR_INIT_CLIST(&callbacks);
 }
 
 nsresult
 nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
 {
     size_t hostLen = strlen(key->host) + 1;
     size_t netInterfaceLen = strlen(key->netInterface) + 1;
     size_t originSuffixLen = strlen(key->originSuffix) + 1;
@@ -224,18 +225,16 @@ nsHostRecord::CopyExpirationTimesAndFlag
     mValidStart = aFromHostRecord->mValidStart;
     mValidEnd = aFromHostRecord->mValidEnd;
     mGraceStart = aFromHostRecord->mGraceStart;
     mDoomed = aFromHostRecord->mDoomed;
 }
 
 nsHostRecord::~nsHostRecord()
 {
-    mCallbacks.clear();
-
     Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
     delete addr_info;
 }
 
 bool
 nsHostRecord::Blacklisted(NetAddr *aQuery)
 {
     // must call locked
@@ -322,39 +321,41 @@ nsHostRecord::HasUsableResult(const mozi
     if (CheckExpiration(now) == EXP_EXPIRED) {
         return false;
     }
 
     return addr_info || addr || negative;
 }
 
 static size_t
-SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
+SizeOfResolveHostCallbackListExcludingHead(const PRCList *head,
                                            MallocSizeOf mallocSizeOf)
 {
-    size_t n = aCallbacks.sizeOfIncludingThis(mallocSizeOf);
-
-    for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
-      n += t->SizeOfIncludingThis(mallocSizeOf);
+    size_t n = 0;
+    PRCList *curr = head->next;
+    while (curr != head) {
+        nsResolveHostCallback *callback =
+            static_cast<nsResolveHostCallback*>(curr);
+        n += callback->SizeOfIncludingThis(mallocSizeOf);
+        curr = curr->next;
     }
-
     return n;
 }
 
 size_t
 nsHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
 {
     size_t n = mallocSizeOf(this);
 
     // The |host| field (inherited from nsHostKey) actually points to extra
     // memory that is allocated beyond the end of the nsHostRecord (see
     // nsHostRecord::Create()).  So it will be included in the
     // |mallocSizeOf(this)| call above.
 
-    n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, mallocSizeOf);
+    n += SizeOfResolveHostCallbackListExcludingHead(&callbacks, mallocSizeOf);
     n += addr_info ? addr_info->SizeOfIncludingThis(mallocSizeOf) : 0;
     n += mallocSizeOf(addr.get());
 
     n += mBlacklistedItems.ShallowSizeOfExcludingThis(mallocSizeOf);
     for (size_t i = 0; i < mBlacklistedItems.Length(); i++) {
         n += mBlacklistedItems[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
     }
     return n;
@@ -727,30 +728,29 @@ nsHostResolver::MoveQueue(nsHostRecord *
 }
 
 nsresult
 nsHostResolver::ResolveHost(const char             *host,
                             const OriginAttributes &aOriginAttributes,
                             uint16_t                flags,
                             uint16_t                af,
                             const char             *netInterface,
-                            nsResolveHostCallback  *aCallback)
+                            nsResolveHostCallback  *callback)
 {
     NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
     NS_ENSURE_TRUE(netInterface, NS_ERROR_UNEXPECTED);
 
     LOG(("Resolving host [%s%s%s]%s.\n", LOG_HOST(host, netInterface),
          flags & RES_BYPASS_CACHE ? " - bypassing cache" : ""));
 
     // ensure that we are working with a valid hostname before proceeding.  see
     // bug 304904 for details.
     if (!net_IsValidHostName(nsDependentCString(host)))
         return NS_ERROR_UNKNOWN_HOST;
 
-    RefPtr<nsResolveHostCallback> callback(aCallback);
     // if result is set inside the lock, then we need to issue the
     // callback before returning.
     RefPtr<nsHostRecord> result;
     nsresult status = NS_OK, rv = NS_OK;
     {
         MutexAutoLock lock(mLock);
 
         if (mShutdown)
@@ -926,36 +926,36 @@ nsHostResolver::ResolveHost(const char  
                 }
                 // If no valid address was found in the cache or this is an
                 // AF_UNSPEC request, then start a new lookup.
                 if (!result) {
                     LOG(("  No usable address in cache for host [%s%s%s].",
                          LOG_HOST(host, netInterface)));
 
                     // Add callback to the list of pending callbacks.
-                    he->rec->mCallbacks.insertBack(callback);
+                    PR_APPEND_LINK(callback, &he->rec->callbacks);
                     he->rec->flags = flags;
                     rv = IssueLookup(he->rec);
                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                           METHOD_NETWORK_FIRST);
-                    if (NS_FAILED(rv) && callback->isInList()) {
-                        callback->remove();
+                    if (NS_FAILED(rv)) {
+                        PR_REMOVE_AND_INIT_LINK(callback);
                     }
                     else {
                         LOG(("  DNS lookup for host [%s%s%s] blocking "
                              "pending 'getaddrinfo' query: callback [%p]",
-                             LOG_HOST(host, netInterface), callback.get()));
+                             LOG_HOST(host, netInterface), callback));
                     }
                 }
             }
             else {
                 LOG(("  Host [%s%s%s] is being resolved. Appending callback "
-                     "[%p].", LOG_HOST(host, netInterface), callback.get()));
+                     "[%p].", LOG_HOST(host, netInterface), callback));
 
-                he->rec->mCallbacks.insertBack(callback);
+                PR_APPEND_LINK(callback, &he->rec->callbacks);
                 if (he->rec->onQueue) {
                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                           METHOD_NETWORK_SHARED);
 
                     // 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.
 
@@ -971,66 +971,60 @@ nsHostResolver::ResolveHost(const char  
                         MoveQueue(he->rec, mMediumQ);
                         he->rec->flags = flags;
                         mIdleThreadCV.Notify();
                     }
                 }
             }
         }
     }
-
     if (result) {
-        if (callback->isInList()) {
-            callback->remove();
-        }
         callback->OnResolveHostComplete(this, result, status);
     }
 
     return rv;
 }
 
 void
 nsHostResolver::DetachCallback(const char             *host,
                                const OriginAttributes &aOriginAttributes,
                                uint16_t                flags,
                                uint16_t                af,
                                const char             *netInterface,
-                               nsResolveHostCallback  *aCallback,
+                               nsResolveHostCallback  *callback,
                                nsresult                status)
 {
     RefPtr<nsHostRecord> rec;
-    RefPtr<nsResolveHostCallback> callback(aCallback);
-
     {
         MutexAutoLock lock(mLock);
 
         nsAutoCString originSuffix;
         aOriginAttributes.CreateSuffix(originSuffix);
 
         nsHostKey key = { host, flags, af, netInterface, originSuffix.get() };
         auto he = static_cast<nsHostDBEnt*>(mDB.Search(&key));
         if (he) {
             // walk list looking for |callback|... we cannot assume
             // that it will be there!
-
-            for (nsResolveHostCallback* c: he->rec->mCallbacks) {
-                if (c == callback) {
+            PRCList *node = he->rec->callbacks.next;
+            while (node != &he->rec->callbacks) {
+                if (static_cast<nsResolveHostCallback *>(node) == callback) {
+                    PR_REMOVE_LINK(callback);
                     rec = he->rec;
-                    c->remove();
                     break;
                 }
+                node = node->next;
             }
         }
     }
 
     // complete callback with the given status code; this would only be done if
     // the record was in the process of being resolved.
-    if (rec) {
+    if (rec)
         callback->OnResolveHostComplete(this, rec, status);
-    }
 }
 
 nsresult
 nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
 {
     if (mNumIdleThreads) {
         // wake up idle thread to process this lookup
         mIdleThreadCV.Notify();
@@ -1294,30 +1288,30 @@ different_rrset(AddrInfo *rrset1, AddrIn
 // CompleteLookup() checks if the resolving should be redone and if so it
 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
 // takes ownership of AddrInfo parameter
 nsHostResolver::LookupStatus
 nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* newRRSet)
 {
     // get the list of pending callbacks for this lookup, and notify
     // them that the lookup is complete.
-    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs;
-
+    PRCList cbs;
+    PR_INIT_CLIST(&cbs);
     {
         MutexAutoLock lock(mLock);
 
         if (rec->mResolveAgain && (status != NS_ERROR_ABORT)) {
             LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
             rec->mResolveAgain = false;
             delete newRRSet;
             return LOOKUP_RESOLVEAGAIN;
         }
 
         // grab list of callbacks to notify
-        cbs = mozilla::Move(rec->mCallbacks);
+        MoveCList(rec->callbacks, cbs);
 
         // update record fields.  We might have a rec->addr_info already if a
         // previous lookup result expired and we're reresolving it..
         AddrInfo  *old_addr_info;
         {
             MutexAutoLock lock(rec->addr_info_lock);
             if (different_rrset(rec->addr_info, newRRSet)) {
                 LOG(("nsHostResolver record %p new gencnt\n", rec));
@@ -1375,18 +1369,25 @@ nsHostResolver::CompleteLookup(nsHostRec
                 NS_WARNING_ASSERTION(
                     NS_SUCCEEDED(rv),
                     "Could not issue second async lookup for TTL.");
             }
 #endif
         }
     }
 
-    for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
-        c->OnResolveHostComplete(this, rec, status);
+    if (!PR_CLIST_IS_EMPTY(&cbs)) {
+        PRCList *node = cbs.next;
+        while (node != &cbs) {
+            nsResolveHostCallback *callback =
+                    static_cast<nsResolveHostCallback *>(node);
+            node = node->next;
+            callback->OnResolveHostComplete(this, rec, status);
+            // NOTE: callback must not be dereferenced after this point!!
+        }
     }
 
     NS_RELEASE(rec);
 
     return LOOKUP_OK;
 }
 
 void
@@ -1404,28 +1405,34 @@ nsHostResolver::CancelAsyncRequest(const
     nsAutoCString originSuffix;
     aOriginAttributes.CreateSuffix(originSuffix);
 
     // Lookup the host record associated with host, flags & address family
     nsHostKey key = { host, flags, af, netInterface, originSuffix.get() };
     auto he = static_cast<nsHostDBEnt*>(mDB.Search(&key));
     if (he) {
         nsHostRecord* recPtr = nullptr;
-
-        for (RefPtr<nsResolveHostCallback> c : he->rec->mCallbacks) {
-            if (c->EqualsAsyncListener(aListener)) {
-                c->remove();
+        PRCList *node = he->rec->callbacks.next;
+        // Remove the first nsDNSAsyncRequest callback which matches the
+        // supplied listener object
+        while (node != &he->rec->callbacks) {
+            nsResolveHostCallback *callback
+                = static_cast<nsResolveHostCallback *>(node);
+            if (callback && (callback->EqualsAsyncListener(aListener))) {
+                // Remove from the list of callbacks
+                PR_REMOVE_LINK(callback);
                 recPtr = he->rec;
-                c->OnResolveHostComplete(this, recPtr, status);
+                callback->OnResolveHostComplete(this, recPtr, status);
                 break;
             }
+            node = node->next;
         }
 
         // If there are no more callbacks, remove the hash table entry
-        if (recPtr && recPtr->mCallbacks.isEmpty()) {
+        if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
             mDB.Remove((nsHostKey *)recPtr);
             // If record is on a Queue, remove it and then deref it
             if (recPtr->next != recPtr) {
                 PR_REMOVE_LINK(recPtr);
                 NS_RELEASE(recPtr);
             }
         }
     }
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -15,17 +15,16 @@
 #include "nsISupportsImpl.h"
 #include "nsIDNSListener.h"
 #include "nsIDNSService.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "GetAddrInfo.h"
 #include "mozilla/net/DNS.h"
 #include "mozilla/net/DashboardTypes.h"
-#include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 
 class nsHostResolver;
 class nsHostRecord;
 class nsResolveHostCallback;
 
 #define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY  3
@@ -126,17 +125,18 @@ public:
     static DnsPriority GetPriority(uint16_t aFlags);
 
     bool RemoveOrRefresh(); // Mark records currently being resolved as needed
                             // to resolve again.
 
 private:
     friend class nsHostResolver;
 
-    mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
+
+    PRCList callbacks; /* list of callbacks */
 
     bool    resolving; /* true if this record is being resolved, which means
                         * that it is either on the pending queue or owned by
                         * one of the worker threads. */
 
     bool    onQueue;  /* true if pending and on the queue (not yet given to getaddrinfo())*/
     bool    usingAnyThread; /* true if off queue and contributing to mActiveAnyThreadCount */
     bool    mDoomed; /* explicitly expired */
@@ -158,23 +158,20 @@ private:
     // of gencnt.
     nsTArray<nsCString> mBlacklistedItems;
 
     explicit nsHostRecord(const nsHostKey *key);           /* use Create() instead */
    ~nsHostRecord();
 };
 
 /**
- * This class is used to notify listeners when a ResolveHost operation is
- * complete. Classes that derive it must implement threadsafe nsISupports
- * to be able to use RefPtr with this class.
+ * ResolveHost callback object.  It's PRCList members are used by
+ * the nsHostResolver and should not be used by anything else.
  */
-class nsResolveHostCallback
-    : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>
-    , public nsISupports
+class NS_NO_VTABLE nsResolveHostCallback : public PRCList
 {
 public:
     /**
      * OnResolveHostComplete
      *
      * this function is called to complete a host lookup initiated by
      * nsHostResolver::ResolveHost.  it may be invoked recursively from
      * ResolveHost or on an unspecified background thread.
@@ -203,18 +200,16 @@ public:
      * the original listener is known.
      *
      * @param aListener
      *        nsIDNSListener object associated with the original request
      */
     virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0;
 
     virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0;
-protected:
-    virtual ~nsResolveHostCallback() = default;
 };
 
 /**
  * nsHostResolver - an asynchronous host name resolver.
  */
 class nsHostResolver
 {
     typedef mozilla::CondVar CondVar;