Bug 1424834 - Replace nsHostRecord.callbacks with LinkedList<RefPtr<nsResolveHostCallback>> r=mayhemer
* nsResolveHostCallback extends nsISupports (for addref-ing and because nsDNSAsyncRequest implements nsICancelable)
* nsResolveHostCallback extends LinkedListElement<RefPtr<nsResolveHostCallback>> so the list can properly manage references
* nsDNSAsyncRequest and nsDNSSyncRequest properly implement nsISupports and use RefPtr to manage lifetimes
MozReview-Commit-ID: 5NvBcWZzfyN
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -290,18 +290,16 @@ 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,
@@ -326,38 +324,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;
@@ -375,50 +373,53 @@ 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;
@@ -793,26 +794,27 @@ nsDNSService::AsyncResolveExtended(const
listener, target_, attrs,
result);
}
NS_IMETHODIMP
nsDNSService::AsyncResolveExtendedNative(const nsACString &aHostname,
uint32_t flags,
const nsACString &aNetworkInterface,
- nsIDNSListener *listener,
+ nsIDNSListener *aListener,
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;
@@ -845,31 +847,26 @@ nsDNSService::AsyncResolveExtendedNative
}
if (target) {
listener = new DNSListenerProxy(listener, target);
}
uint16_t af = GetAFForLookup(hostname, flags);
- auto *req =
+ MOZ_ASSERT(listener);
+ RefPtr<nsDNSAsyncRequest> 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);
- if (NS_FAILED(rv)) {
- NS_RELEASE(req);
- NS_RELEASE(*result);
- }
+ req.forget(result);
return rv;
}
NS_IMETHODIMP
nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
uint32_t aFlags,
nsIDNSListener *aListener,
nsresult aReason,
@@ -1050,35 +1047,33 @@ 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);
- nsDNSSyncRequest syncReq(mon);
+ RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(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");
- auto *rec = new nsDNSRecord(syncReq.mHostRecord);
- if (!rec)
- rv = NS_ERROR_OUT_OF_MEMORY;
- else
- NS_ADDREF(*result = rec);
+ 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);
}
}
PR_ExitMonitor(mon);
PR_DestroyMonitor(mon);
return rv;
}
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -184,17 +184,16 @@ 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;
@@ -225,16 +224,18 @@ 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
@@ -321,41 +322,39 @@ nsHostRecord::HasUsableResult(const mozi
if (CheckExpiration(now) == EXP_EXPIRED) {
return false;
}
return addr_info || addr || negative;
}
static size_t
-SizeOfResolveHostCallbackListExcludingHead(const PRCList *head,
+SizeOfResolveHostCallbackListExcludingHead(const mozilla::LinkedList<RefPtr<nsResolveHostCallback>>& aCallbacks,
MallocSizeOf 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;
+ size_t n = 0; // TODO: should be aCallbacks.sizeOfIncludingThis(mallocSizeOf);
+
+ for (const nsResolveHostCallback* t = aCallbacks.getFirst(); t; t = t->getNext()) {
+ n += t->SizeOfIncludingThis(mallocSizeOf);
}
+
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(&callbacks, mallocSizeOf);
+ n += SizeOfResolveHostCallbackListExcludingHead(mCallbacks, 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;
@@ -728,29 +727,30 @@ nsHostResolver::MoveQueue(nsHostRecord *
}
nsresult
nsHostResolver::ResolveHost(const char *host,
const OriginAttributes &aOriginAttributes,
uint16_t flags,
uint16_t af,
const char *netInterface,
- nsResolveHostCallback *callback)
+ nsResolveHostCallback *aCallback)
{
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.
- PR_APPEND_LINK(callback, &he->rec->callbacks);
+ he->rec->mCallbacks.insertBack(callback);
he->rec->flags = flags;
rv = IssueLookup(he->rec);
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NETWORK_FIRST);
- if (NS_FAILED(rv)) {
- PR_REMOVE_AND_INIT_LINK(callback);
+ if (NS_FAILED(rv) && callback->isInList()) {
+ callback->remove();
}
else {
LOG((" DNS lookup for host [%s%s%s] blocking "
"pending 'getaddrinfo' query: callback [%p]",
- LOG_HOST(host, netInterface), callback));
+ LOG_HOST(host, netInterface), callback.get()));
}
}
}
else {
LOG((" Host [%s%s%s] is being resolved. Appending callback "
- "[%p].", LOG_HOST(host, netInterface), callback));
+ "[%p].", LOG_HOST(host, netInterface), callback.get()));
- PR_APPEND_LINK(callback, &he->rec->callbacks);
+ he->rec->mCallbacks.insertBack(callback);
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,60 +971,66 @@ 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 *callback,
+ nsResolveHostCallback *aCallback,
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!
- PRCList *node = he->rec->callbacks.next;
- while (node != &he->rec->callbacks) {
- if (static_cast<nsResolveHostCallback *>(node) == callback) {
- PR_REMOVE_LINK(callback);
+
+ for (nsResolveHostCallback* c: he->rec->mCallbacks) {
+ if (c == 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();
@@ -1288,30 +1294,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.
- PRCList cbs;
- PR_INIT_CLIST(&cbs);
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> 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
- MoveCList(rec->callbacks, cbs);
+ cbs = mozilla::Move(rec->mCallbacks);
// 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));
@@ -1369,25 +1375,18 @@ nsHostResolver::CompleteLookup(nsHostRec
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"Could not issue second async lookup for TTL.");
}
#endif
}
}
- 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!!
- }
+ for (nsResolveHostCallback* c = cbs.getFirst(); c; c = c->removeAndGetNext()) {
+ c->OnResolveHostComplete(this, rec, status);
}
NS_RELEASE(rec);
return LOOKUP_OK;
}
void
@@ -1405,34 +1404,28 @@ 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;
- 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);
+
+ for (RefPtr<nsResolveHostCallback> c : he->rec->mCallbacks) {
+ if (c->EqualsAsyncListener(aListener)) {
+ c->remove();
recPtr = he->rec;
- callback->OnResolveHostComplete(this, recPtr, status);
+ c->OnResolveHostComplete(this, recPtr, status);
break;
}
- node = node->next;
}
// If there are no more callbacks, remove the hash table entry
- if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) {
+ if (recPtr && recPtr->mCallbacks.isEmpty()) {
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,16 +15,17 @@
#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
@@ -125,18 +126,17 @@ public:
static DnsPriority GetPriority(uint16_t aFlags);
bool RemoveOrRefresh(); // Mark records currently being resolved as needed
// to resolve again.
private:
friend class nsHostResolver;
-
- PRCList callbacks; /* list of callbacks */
+ mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
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,20 +158,23 @@ private:
// of gencnt.
nsTArray<nsCString> mBlacklistedItems;
explicit nsHostRecord(const nsHostKey *key); /* use Create() instead */
~nsHostRecord();
};
/**
- * ResolveHost callback object. It's PRCList members are used by
- * the nsHostResolver and should not be used by anything else.
+ * 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.
*/
-class NS_NO_VTABLE nsResolveHostCallback : public PRCList
+class nsResolveHostCallback
+ : public mozilla::LinkedListElement<RefPtr<nsResolveHostCallback>>
+ , public nsISupports
{
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.
@@ -200,16 +203,18 @@ 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;