From 4ca8ffb4a0eaacdfdc62483af77147a8a1469887 Mon Sep 17 00:00:00 2001 From: "gordon%netscape.com" Date: Mon, 27 Mar 2000 22:19:18 +0000 Subject: [PATCH] Landing DNS_CANCEL_BRANCH. Enable canceling of dns lookup requests, and underlying structure for possible DNS cache. Original changes by warren, gordon. r = gagan. git-svn-id: svn://10.0.0.236/trunk@64254 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/netwerk/dns/public/nsIDNSService.idl | 6 +- mozilla/netwerk/dns/src/nsDnsService.cpp | 1134 +++++++++++------- mozilla/netwerk/dns/src/nsDnsService.h | 27 +- 3 files changed, 749 insertions(+), 418 deletions(-) diff --git a/mozilla/netwerk/dns/public/nsIDNSService.idl b/mozilla/netwerk/dns/public/nsIDNSService.idl index 253ee2a08fc..e68500899ab 100644 --- a/mozilla/netwerk/dns/public/nsIDNSService.idl +++ b/mozilla/netwerk/dns/public/nsIDNSService.idl @@ -40,9 +40,11 @@ interface nsIDNSListener; [scriptable, uuid(598f2f80-206f-11d3-9348-00104ba0fd40)] interface nsIDNSService : nsISupports { - nsIRequest lookup(in nsISupports ctxt, in string hostname, - in nsIDNSListener listener); + nsIRequest lookup(in string hostname, + in nsIDNSListener listener, + in nsISupports ctxt); + void init(); void shutdown(); }; diff --git a/mozilla/netwerk/dns/src/nsDnsService.cpp b/mozilla/netwerk/dns/src/nsDnsService.cpp index b1381245101..ebfd2e624d0 100644 --- a/mozilla/netwerk/dns/src/nsDnsService.cpp +++ b/mozilla/netwerk/dns/src/nsDnsService.cpp @@ -30,6 +30,9 @@ #include "nsIIOService.h" #include "nsIServiceManager.h" #include "netCore.h" +#include "nsAutoLock.h" +#include "nsIStreamObserver.h" +#include "nsTime.h" #ifdef DNS_TIMING #include "prinrval.h" #include "prtime.h" @@ -40,6 +43,7 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); //////////////////////////////////////////////////////////////////////////////// // Platform specific defines and includes //////////////////////////////////////////////////////////////////////////////// + // PC #if defined(XP_PC) && !defined(XP_OS2) #define WM_DNS_SHUTDOWN (WM_USER + 200) @@ -66,12 +70,12 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); typedef struct nsInetHostInfo { InetHostInfo hostInfo; - nsDNSLookup * lookup; + nsDNSLookup * lookup; // weak reference } nsInetHostInfo; typedef struct nsLookupElement { QElem qElem; - nsDNSLookup * lookup; + nsDNSLookup * lookup; // weak reference } nsLookupElement; #endif /* XP_MAC */ @@ -82,30 +86,101 @@ typedef struct nsLookupElement { nsDNSService * nsDNSService::gService = nsnull; PRBool nsDNSService::gNeedLateInitialization = PR_FALSE; -class nsDNSLookup +//////////////////////////////////////////////////////////////////////////////// + +class nsDNSLookup; + +class nsDNSRequest : public nsIRequest { public: - nsDNSLookup(nsISupports * clientContext, const char * hostName, nsIDNSListener* listener); - virtual ~nsDNSLookup(void); + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUEST - nsresult FinishHostEntry(void); - nsresult CallOnFound(void); - const char * HostName(void) { return mHostName; } - nsresult InitiateDNSLookup(void); + nsDNSRequest() + : mLookup(nsnull), + mSuspendCount(0) +#ifdef DNS_TIMING + ,mStartTime(PR_IntervalNow()) +#endif + { NS_INIT_REFCNT(); } + virtual ~nsDNSRequest() {} + + nsresult Init(nsDNSLookup* lookup, + nsIDNSListener* userListener, + nsISupports* userContext); + nsresult FireStart(); + nsresult FireStop(nsresult status); protected: - // Input when creating a nsDNSLookup - nsCOMPtr mListener; - nsCOMPtr mContext; + nsCOMPtr mUserListener; + nsCOMPtr mUserContext; + nsDNSLookup* mLookup; // weak ref + PRUint32 mSuspendCount; +#ifdef DNS_TIMING + PRIntervalTime mStartTime; +#endif +}; + +//////////////////////////////////////////////////////////////////////////////// + +class nsDNSLookup : public nsISupports +{ +public: + NS_DECL_ISUPPORTS + + nsDNSLookup(); + virtual ~nsDNSLookup(void); + + + nsresult Init(const char * hostName); + void Reset(void); + const char * HostName() { return mHostName; } + nsHostEnt* HostEntry() { return &mHostEntry; } + PRBool IsComplete() { return mComplete; } + nsresult HandleRequest(nsDNSRequest* req); + nsresult InitiateLookup(void); + nsresult CompletedLookup(nsresult status); + nsresult Suspend(nsDNSRequest* req); + nsresult Resume(nsDNSRequest* req); + static PRBool FindCompleted(nsHashKey *aKey, void *aData, void* closure); + static PRBool CompletedEntry(nsHashKey *aKey, void *aData, void* closure); + static PRBool DeleteEntry(nsHashKey *aKey, void *aData, void* closure); + PRBool IsExpired() { +#ifdef xDEBUG + char buf[256]; + PRExplodedTime et; + + PR_ExplodeTime(mExpires, PR_LocalTimeParameters, &et); + PR_FormatTimeUSEnglish(buf, sizeof(buf), "%c", &et); + fprintf(stderr, "\nDNS %s expires %s\n", mHostName, buf); + + PR_ExplodeTime(nsTime(), PR_LocalTimeParameters, &et); + PR_FormatTimeUSEnglish(buf, sizeof(buf), "%c", &et); + fprintf(stderr, "now %s ==> %s\n", buf, + mExpires < nsTime() + ? "expired" : "valid"); + fflush(stderr); +#endif + return mExpires < nsTime(); + } + + friend class nsDNSService; + friend class nsDNSCache; + +protected: + nsCOMPtr mRequests; char * mHostName; // Result of the DNS Lookup nsHostEnt mHostEntry; - nsresult mResult; + nsresult mStatus; PRBool mComplete; + nsTime mExpires; // Platform specific portions #if defined(XP_MAC) friend pascal void nsDnsServiceNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie); + nsresult FinishHostEntry(void); + nsLookupElement mLookupElement; nsInetHostInfo mInetHostInfo; #endif @@ -115,13 +190,10 @@ protected: HANDLE mLookupHandle; PRUint32 mMsgID; #endif - -#ifdef DNS_TIMING - PRIntervalTime mStartTime; -#endif }; + //////////////////////////////////////////////////////////////////////////////// // utility routines: //////////////////////////////////////////////////////////////////////////////// @@ -129,40 +201,175 @@ protected: // Allocate space from the buffer, aligning it to "align" before doing // the allocation. "align" must be a power of 2. // NOTE: this code was taken from NSPR. -static char *BufAlloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align) { - char *buf = *bufp; - PRIntn buflen = *buflenp; - if (align && ((long)buf & (align - 1))) { - PRIntn skip = align - ((ptrdiff_t)buf & (align - 1)); - if (buflen < skip) { - return 0; - } - buf += skip; - buflen -= skip; - } - if (buflen < amount) { - return 0; - } - *bufp = buf + amount; - *buflenp = buflen - amount; - return buf; +static char * +BufAlloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align) { + char *buf = *bufp; + PRIntn buflen = *buflenp; + + if (align && ((long)buf & (align - 1))) { + PRIntn skip = align - ((ptrdiff_t)buf & (align - 1)); + if (buflen < skip) { + return 0; + } + buf += skip; + buflen -= skip; + } + if (buflen < amount) { + return 0; + } + *bufp = buf + amount; + *buflenp = buflen - amount; + return buf; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsDNSRequest methods: +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRequest, nsIRequest); + +nsresult +nsDNSRequest::Init(nsDNSLookup* lookup, + nsIDNSListener* userListener, + nsISupports* userContext) +{ + mLookup = lookup; + mUserListener = userListener; + mUserContext = userContext; + return NS_OK; +} + +nsresult +nsDNSRequest::FireStart() +{ + nsresult rv; + NS_ASSERTION(mUserListener, "calling FireStart more than once"); + if (mUserListener == nsnull) + return NS_ERROR_FAILURE; + rv = mUserListener->OnStartLookup(mUserContext, mLookup->HostName()); + return rv; +} + +nsresult +nsDNSRequest::FireStop(nsresult status) +{ + nsresult rv; + NS_ASSERTION(mUserListener, "calling FireStop more than once"); + if (mUserListener == nsnull) + return NS_ERROR_FAILURE; + + if (NS_SUCCEEDED(status)) { + rv = mUserListener->OnFound(mUserContext, + mLookup->HostName(), + mLookup->HostEntry()); + NS_ASSERTION(NS_SUCCEEDED(rv), "OnFound failed"); + } + rv = mUserListener->OnStopLookup(mUserContext, + mLookup->HostName(), + status); + NS_ASSERTION(NS_SUCCEEDED(rv), "OnStopLookup failed"); + + mUserListener = null_nsCOMPtr(); + mUserContext = null_nsCOMPtr(); +#ifdef DNS_TIMING + if (nsDNSService::gService->mOut) { + PRIntervalTime stopTime = PR_IntervalNow(); + double duration = PR_IntervalToMicroseconds(stopTime - mStartTime); + nsDNSService::gService->mCount++; + nsDNSService::gService->mTimes += duration; + nsDNSService::gService->mSquaredTimes += duration * duration; + fprintf(nsDNSService::gService->mOut, "DNS time #%d: %u us for %s\n", + (PRInt32)nsDNSService::gService->mCount, + (PRInt32)duration, mLookup->HostName()); + } +#endif + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRequest::IsPending(PRBool *result) +{ + *result = !mLookup->IsComplete(); + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRequest::Cancel(void) +{ + if (mUserListener) { + // Hold onto a reference to ourself because if we decide to remove + // this request from the mRequests list, it could be the last + // reference, causing ourself to be deleted. We need to live until + // this method completes: + nsCOMPtr req = this; + + (void)mLookup->Suspend(this); + return FireStop(NS_BINDING_ABORTED); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRequest::Suspend(void) +{ + if (mSuspendCount++ == 0) { + // Hold onto a reference to ourself because if we decide to remove + // this request from the mRequests list, it could be the last + // reference, causing ourself to be deleted. We need to live until + // this method completes: + nsCOMPtr req = this; + + return mLookup->Suspend(this); + } + return NS_OK; +} + +NS_IMETHODIMP +nsDNSRequest::Resume(void) +{ + if (--mSuspendCount == 0) { + // Hold onto a reference to ourself because if we decide to remove + // this request from the mRequests list, it could be the last + // reference, causing ourself to be deleted. We need to live until + // this method completes: + nsCOMPtr req = this; + + return mLookup->Resume(this); + } + return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsDNSLookup methods: //////////////////////////////////////////////////////////////////////////////// -nsDNSLookup::nsDNSLookup(nsISupports * clientContext, const char * hostName, nsIDNSListener* listener) - : mStartTime(PR_IntervalNow()) +NS_IMPL_THREADSAFE_ISUPPORTS0(nsDNSLookup); + +nsDNSLookup::nsDNSLookup() + : mHostName(nsnull), + mStatus(NS_OK), + mComplete(PR_FALSE), + mExpires(0) { - MOZ_COUNT_CTOR(nsDNSLookup); + NS_INIT_REFCNT(); + MOZ_COUNT_CTOR(nsDNSLookup); +} + + +nsresult +nsDNSLookup::Init(const char * hostName) +{ + nsresult rv; // Store input into member variables - mHostName = new char [PL_strlen(hostName) + 1]; - PL_strcpy(mHostName, hostName); - mContext = clientContext; - mListener = listener; + mHostName = nsCRT::strdup(hostName); + if (mHostName == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + + rv = NS_NewISupportsArray(getter_AddRefs(mRequests)); + if (NS_FAILED(rv)) return rv; + #if defined(XP_PC) && !defined(XP_OS2) mMsgID = 0; #endif @@ -170,29 +377,38 @@ nsDNSLookup::nsDNSLookup(nsISupports * clientContext, const char * hostName, nsI // Initialize result holders mHostEntry.bufLen = PR_NETDB_BUF_SIZE; mHostEntry.bufPtr = mHostEntry.buffer; - mComplete = PR_FALSE; - mResult = NS_OK; // Platform specific initializations #if defined(XP_MAC) mInetHostInfo.lookup = this; mLookupElement.lookup = this; #endif + + Reset(); + return NS_OK; } +void +nsDNSLookup::Reset(void) +{ + mComplete = PR_FALSE; + mStatus = NS_OK; + mExpires = LL_ZERO; +// fprintf(stderr, "DNS reset for %s\n", mHostName); +} nsDNSLookup::~nsDNSLookup(void) { MOZ_COUNT_DTOR(nsDNSLookup); + if (mHostName) - delete [] mHostName; + nsCRT::free(mHostName); } - +#if defined(XP_MAC) nsresult nsDNSLookup::FinishHostEntry(void) { -#if defined(XP_MAC) PRIntn len, count, i; // convert InetHostInfo to PRHostEnt @@ -235,53 +451,82 @@ nsDNSLookup::FinishHostEntry(void) return NS_ERROR_OUT_OF_MEMORY; *(InetHost *)mHostEntry.hostEnt.h_addr_list[i] = mInetHostInfo.hostInfo.addrs[i]; } -#endif - - mComplete = PR_TRUE; return NS_OK; } - +#endif nsresult -nsDNSLookup::CallOnFound(void) +nsDNSLookup::HandleRequest(nsDNSRequest* req) { - nsresult result; - // iterate through request queue calling listeners - - // but for now just do this - if (NS_SUCCEEDED(mResult)) - { - result = mListener->OnFound(mContext, mHostName, &mHostEntry); - } - result = mListener->OnStopLookup(mContext, mHostName, mResult); - - mListener = 0; - mContext = 0; - -#ifdef DNS_TIMING - if (nsDNSService::gService->mOut) { - PRIntervalTime stopTime = PR_IntervalNow(); - double duration = PR_IntervalToMicroseconds(stopTime - mStartTime); - nsDNSService::gService->mCount++; - nsDNSService::gService->mTimes += duration; - nsDNSService::gService->mSquaredTimes += duration * duration; - fprintf(nsDNSService::gService->mOut, "DNS time #%d: %u us for %s\n", - (PRInt32)nsDNSService::gService->mCount, - (PRInt32)duration, HostName()); - } -#endif - return result; + nsresult rv; + + rv = req->FireStart(); + if (NS_FAILED(rv)) return rv; + return Resume(req); } - nsresult -nsDNSLookup::InitiateDNSLookup(void) +nsDNSLookup::InitiateLookup(void) { - nsresult rv = NS_OK; + nsresult rv = NS_OK; +// nsAutoCMonitor mon(this); // XXX don't think we need this + PRStatus status = PR_SUCCESS; + + PRBool numeric = PR_TRUE; + for (const char *hostCheck = mHostName; *hostCheck; hostCheck++) { + if (!nsString2::IsDigit(*hostCheck) && (*hostCheck != '.') ) { + numeric = PR_FALSE; + break; + } + } + + if (numeric) { + // If it is numeric then try to convert it into an IP-Address + PRNetAddr *netAddr = (PRNetAddr*)nsAllocator::Alloc(sizeof(PRNetAddr)); + if (!netAddr) return NS_ERROR_OUT_OF_MEMORY; + + status = PR_StringToNetAddr(mHostName, netAddr); + if (PR_SUCCESS == status) { + // slam the IP in and move on. + mHostEntry.bufLen = PR_NETDB_BUF_SIZE; + + PRUint32 hostNameLen = nsCRT::strlen(mHostName); + mHostEntry.hostEnt.h_name = (char*)BufAlloc(hostNameLen + 1, + (char**)&mHostEntry.buffer, + &mHostEntry.bufLen, + 0); + memcpy(mHostEntry.hostEnt.h_name, mHostName, hostNameLen + 1); + + mHostEntry.hostEnt.h_aliases = (char**)BufAlloc(1 * sizeof(char*), + (char**)&mHostEntry.buffer, + &mHostEntry.bufLen, + sizeof(char **)); + mHostEntry.hostEnt.h_aliases[0] = '\0'; + + mHostEntry.hostEnt.h_addrtype = 2; + mHostEntry.hostEnt.h_length = 4; + mHostEntry.hostEnt.h_addr_list = (char**)BufAlloc(2 * sizeof(char*), + (char**)&mHostEntry.buffer, + &mHostEntry.bufLen, + sizeof(char **)); + mHostEntry.hostEnt.h_addr_list[0] = (char*)BufAlloc(mHostEntry.hostEnt.h_length, + (char**)&mHostEntry.buffer, + &mHostEntry.bufLen, + 0); + memcpy(mHostEntry.hostEnt.h_addr_list[0], &netAddr->inet.ip, mHostEntry.hostEnt.h_length); + mHostEntry.hostEnt.h_addr_list[1] = '\0'; + + return CompletedLookup(NS_OK); + } + } + + // Incomming hostname is not a numeric ip address. Need to do the actual + // dns lookup. + #if defined(XP_MAC) - OSErr err; + OSErr err; err = OTInetStringToAddress(nsDNSService::gService->mServiceRef, (char *)mHostName, (InetHostInfo *)&mInetHostInfo); if (err != noErr) @@ -292,70 +537,204 @@ nsDNSLookup::InitiateDNSLookup(void) mMsgID = nsDNSService::gService->AllocMsgID(); if (mMsgID == 0) return NS_ERROR_UNEXPECTED; - PR_Lock(nsDNSService::gService->mThreadLock); // protect against lookup completing before WSAAsyncGetHostByName returns, better to refcount lookup - mLookupHandle = WSAAsyncGetHostByName(nsDNSService::gService->mDNSWindow, mMsgID, + { + nsAutoMonitor mon(nsDNSService::gService->mMonitor); // protect against lookup completing before WSAAsyncGetHostByName returns, better to refcount lookup + + mLookupHandle = WSAAsyncGetHostByName(nsDNSService::gService->mDNSWindow, mMsgID, mHostName, (char *)&mHostEntry.hostEnt, PR_NETDB_BUF_SIZE); - // check for error conditions - if (mLookupHandle == nsnull) { - rv = NS_ERROR_UNEXPECTED; // or call WSAGetLastError() for details; - // While we would like to use mLookupHandle to allow for canceling the request in - // the future, there is a bug with winsock2 on win95 that causes WSAAsyncGetHostByName - // to always return the same value. We have worked around this problem by using the - // msgID to identify which lookup has completed, rather than the handle, however, we - // still need to identify when this is a problem (by comparing the return values of - // the first two calls made to WSAAsyncGetHostByName), to avoid using the handle on - // those systems. For more info, see bug 23709. + // check for error conditions + if (mLookupHandle == nsnull) { + rv = NS_ERROR_UNEXPECTED; // or call WSAGetLastError() for details; + // While we would like to use mLookupHandle to allow for canceling the request in + // the future, there is a bug with winsock2 on win95 that causes WSAAsyncGetHostByName + // to always return the same value. We have worked around this problem by using the + // msgID to identify which lookup has completed, rather than the handle, however, we + // still need to identify when this is a problem (by comparing the return values of + // the first two calls made to WSAAsyncGetHostByName), to avoid using the handle on + // those systems. For more info, see bug 23709. + } } - PR_Unlock(nsDNSService::gService->mThreadLock); #endif /* XP_PC */ #ifdef XP_UNIX // temporary SYNC version - PRStatus status = PR_GetHostByName(mHostName, mHostEntry.buffer, - PR_NETDB_BUF_SIZE, - &(mHostEntry.hostEnt)); + status = PR_GetHostByName(mHostName, mHostEntry.buffer, PR_NETDB_BUF_SIZE, &(mHostEntry.hostEnt)); if (PR_SUCCESS != status) rv = NS_ERROR_UNKNOWN_HOST; - mResult = rv; - CallOnFound(); - if (PR_SUCCESS == status) - delete this; // nsDNSLookup deleted by nsDNSService::Lookup() in failure case + return CompletedLookup(rv); #endif /* XP_UNIX */ return rv; } +#define EXPIRATION_INTERVAL (15*60*1000000) // 15 min worth of microseconds +//#define EXPIRATION_INTERVAL (30*1000000) // 30 sec worth of microseconds +nsresult +nsDNSLookup::CompletedLookup(nsresult status) +{ + nsresult rv; + nsAutoCMonitor mon(this); // protect mRequests + + mStatus = status; + mExpires = nsTime() + nsInt64(EXPIRATION_INTERVAL); // now + 15 minutes + mComplete = PR_TRUE; + + while (PR_TRUE) { + nsDNSRequest* req = (nsDNSRequest*)mRequests->ElementAt(0); + if (req == nsnull) break; + + rv = mRequests->RemoveElementAt(0) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool + if (NS_FAILED(rv)) { + NS_RELEASE(req); + return rv; + } + + // We can't be holding the lock around the OnFound/OnStopLookup + // callbacks: + mon.Exit(); + rv = req->FireStop(mStatus); + mon.Enter(); + NS_RELEASE(req); + NS_ASSERTION(NS_SUCCEEDED(rv), "req->FireStop() failed."); + // continue notifying requests, even if one fails + } + + // XXX remove from hashtable and release - for now + nsStringKey key(mHostName); + (void) nsDNSService::gService->mLookups.Remove(&key); + + return NS_OK; +} + +nsresult +nsDNSLookup::Suspend(nsDNSRequest* req) +{ + nsresult rv; + nsAutoCMonitor mon(this); + + if (mComplete) + return NS_ERROR_FAILURE; + + rv = mRequests->RemoveElement(req) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool + if (NS_FAILED(rv)) return rv; + + PRUint32 cnt; + rv = mRequests->Count(&cnt); + if (NS_FAILED(rv)) return rv; + if (cnt == 0) { + // XXX need to do the platform-specific cancelation here + } + + return rv; +} + +nsresult +nsDNSLookup::Resume(nsDNSRequest* req) +{ + nsresult rv; + PRUint32 reqCount; + + if (mComplete && !IsExpired()) { +// fprintf(stderr, "\nDNS cache hit for %s\n", mHostName); + rv = req->FireStop(mStatus); + return rv; + } + + { // protect mRequests + nsAutoCMonitor mon(this); + + rv = mRequests->AppendElement(req) ? NS_OK : NS_ERROR_FAILURE; // XXX this method incorrectly returns a bool + if (NS_FAILED(rv)) return rv; + + rv = mRequests->Count(&reqCount); + if (NS_FAILED(rv)) return rv; + } + + if (reqCount == 1) { + // if this was the first request, then we need to kick off + // the lookup +// fprintf(stderr, "\nDNS cache miss for %s\n", mHostName); + rv = InitiateLookup(); + } + else { +// fprintf(stderr, "DNS consolidating lookup for %s\n", mHostName); + } + return rv; +} + + //////////////////////////////////////////////////////////////////////////////// // Platform specific helper routines //////////////////////////////////////////////////////////////////////////////// #if defined(XP_MAC) -pascal void nsDnsServiceNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie); - -pascal void nsDnsServiceNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie) +pascal void +nsDnsServiceNotifierRoutine(void * contextPtr, OTEventCode code, + OTResult result, void * cookie) { - if (code == T_DNRSTRINGTOADDRCOMPLETE) { + if (code == T_DNRSTRINGTOADDRCOMPLETE) { nsDNSService * dnsService = (nsDNSService *)contextPtr; nsDNSLookup * dnsLookup = ((nsInetHostInfo *)cookie)->lookup; PRThread * thread; if (result != kOTNoError) - dnsLookup->mResult = NS_ERROR_UNKNOWN_HOST; + dnsLookup->mStatus = NS_ERROR_UNKNOWN_HOST; // queue result & wake up dns service thread Enqueue((QElem *)&dnsLookup->mLookupElement, &dnsService->mCompletionQueue); dnsService->mThread->GetPRThread(&thread); if (thread) - PR_Mac_PostAsyncNotify(thread); + PR_Mac_PostAsyncNotify(thread); } - // or else we don't handle the event + // or else we don't handle the event } #endif /* XP_MAC */ #if defined(XP_PC) && !defined(XP_OS2) +struct FindCompletedClosure { + UINT mMsg; + nsDNSLookup** mResult; +}; + +PRBool +nsDNSLookup::FindCompleted(nsHashKey *aKey, void *aData, void* closure) +{ + nsDNSLookup* lookup = (nsDNSLookup*)aData; + FindCompletedClosure* c = (FindCompletedClosure*)closure; + if (lookup->mMsgID == c->mMsg) { + *c->mResult = lookup; + return PR_FALSE; // quit looking + } + return PR_TRUE; // keep going +} + +LRESULT +nsDNSService::LookupComplete(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + nsresult rv; + nsDNSLookup* lookup = nsnull; + + { + nsAutoMonitor mon(mMonitor); // protect mLookups + + // which lookup completed? fetch lookup for this (HANDLE)wParam + FindCompletedClosure closure = { (HANDLE)wParam, &lookup }; + mLookups.Enumerate(nsDNSLookup::FindCompleted, &closure); + NS_IF_ADDREF(lookup); + } // exit monitor + + if (lookup) { + int error = WSAGETASYNCERROR(lParam); + rv = lookup->CompletedLookup(error ? NS_ERROR_UNKNOWN_HOST : NS_OK); + NS_IF_RELEASE(lookup); + return NS_SUCCEEDED(rv) ? 0 : -1; + } + return -1; // XXX right result? +} + static LRESULT CALLBACK nsDNSEventProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -363,39 +742,7 @@ nsDNSEventProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) int error = nsnull; if ((uMsg >= WM_USER) && (uMsg < WM_USER+128)) { - // dns lookup complete - get error code - error = WSAGETASYNCERROR(lParam); - - // which lookup completed? fetch lookup for this msgID - PRInt32 index; - nsDNSLookup * lookup = nsnull; - PRBool rv; - - // find matching lookup element - PR_Lock(nsDNSService::gService->mThreadLock); // so we don't collide with thread calling Lookup() - index = nsDNSService::gService->mCompletionQueue.Count(); - while (index) { - lookup = (nsDNSLookup *)nsDNSService::gService->mCompletionQueue.ElementAt(index-1); - if (lookup->mMsgID == uMsg) { - break; - } - index--; - } - - if (lookup && (lookup->mMsgID == uMsg)) { - rv = nsDNSService::gService->mCompletionQueue.RemoveElement(lookup); - NS_ASSERTION(rv == PR_TRUE, "error removing dns lookup element."); - - lookup->mComplete = PR_TRUE; - if (error != 0) - lookup->mResult = NS_ERROR_UNKNOWN_HOST; - - (void)lookup->CallOnFound(); - nsDNSService::gService->FreeMsgID(lookup->mMsgID); - delete lookup; // until we implement the dns cache - } - result = 0; - PR_Unlock(nsDNSService::gService->mThreadLock); + result = nsDNSService::gService->LookupComplete(hWnd, uMsg, wParam, lParam); } else if (uMsg == WM_DNS_SHUTDOWN) { // dispose DNS EventHandler Window @@ -419,9 +766,21 @@ nsDNSEventProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // nsDNSService methods: //////////////////////////////////////////////////////////////////////////////// +PRBool +nsDNSLookup::DeleteEntry(nsHashKey *aKey, void *aData, void* closure) +{ + nsDNSLookup* lookup = (nsDNSLookup*)aData; + delete lookup; + return PR_TRUE; // keep iterating +} + nsDNSService::nsDNSService() - : + : mState(NS_OK), + mMonitor(nsnull), + mLookups(64) +// mLookups(nsnull, nsnull, nsDNSLookup::DeleteEntry, nsnull) #ifdef DNS_TIMING + , mCount(0), mTimes(0), mSquaredTimes(0), @@ -432,7 +791,6 @@ nsDNSService::nsDNSService() NS_ASSERTION(gService==nsnull,"multiple nsDNSServices allocated!"); gService = this; - mThreadLock = nsnull; #if defined(XP_MAC) gNeedLateInitialization = PR_TRUE; @@ -449,6 +807,7 @@ nsDNSService::nsDNSService() #endif /* defined(XP_MAC) */ #if defined(XP_PC) && !defined(XP_OS2) + // initialize bit vector for allocating message IDs. int i; for (i=0; i<4; i++) mMsgIDBitVector[i] = 0; @@ -474,32 +833,28 @@ nsresult nsDNSService::Init() { nsresult rv = NS_OK; -// initialize DNS cache (persistent?) - mThreadLock = PR_NewLock(); - if (!mThreadLock) - return NS_ERROR_OUT_OF_MEMORY; + NS_ASSERTION(mMonitor == nsnull, "nsDNSService not shut down"); + mMonitor = PR_NewMonitor(); + if (mMonitor == nsnull) + return NS_ERROR_OUT_OF_MEMORY; #if defined(XP_PC) // sync with DNS thread to allow it to create the DNS window - PRMonitor * monitor; - PRStatus status; - - monitor = PR_CEnterMonitor(this); + nsAutoMonitor mon(mMonitor); #endif #if defined(XP_MAC) || defined(XP_PC) // create DNS thread + NS_ASSERTION(mThread == nsnull, "nsDNSService not shut down"); rv = NS_NewThread(getter_AddRefs(mThread), this, 0, PR_JOINABLE_THREAD); #endif - #if defined(XP_PC) - status = PR_CWait(this, PR_INTERVAL_NO_TIMEOUT); - status = PR_CExitMonitor(this); + mon.Wait(); #endif - return rv; + return rv; } nsresult @@ -545,9 +900,16 @@ nsDNSService::LateInit() nsDNSService::~nsDNSService() { + nsresult rv = Shutdown(); + NS_ASSERTION(NS_SUCCEEDED(rv), "DNS shutdown failed"); + NS_ASSERTION(mThread == nsnull, "DNS shutdown failed"); + NS_ASSERTION(mLookups.Count() == 0, "didn't clean up lookups"); + + if (mMonitor) + PR_DestroyMonitor(mMonitor); + NS_ASSERTION(gService==this,"multiple nsDNSServices allocated."); gService = nsnull; - Shutdown(); #ifdef DNS_TIMING if (mOut) { @@ -586,6 +948,10 @@ nsDNSService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) return rv; } +//////////////////////////////////////////////////////////////////////////////// +// nsIRunnable implementation... +//////////////////////////////////////////////////////////////////////////////// + nsresult nsDNSService::InitDNSThread(void) { @@ -611,18 +977,221 @@ nsDNSService::InitDNSThread(void) 0, 0, 0, 10, 10, NULL, NULL, NULL, NULL); // sync with Create thread - PRMonitor * monitor; - PRStatus status; - - monitor = PR_CEnterMonitor(this); - mState = NS_OK; - status = PR_CNotify(this); - status = PR_CExitMonitor(this); + nsAutoMonitor mon(mMonitor); + mon.Notify(); #endif /* XP_PC */ return NS_OK; } +NS_IMETHODIMP +nsDNSService::Run(void) +{ + nsresult rv; + + rv = InitDNSThread(); + if (NS_FAILED(rv)) return rv; + +#if defined(XP_PC) && !defined(XP_OS2) + MSG msg; + + while(GetMessage(&msg, mDNSWindow, 0, 0)) { + // no TranslateMessage() because we're not expecting input + DispatchMessage(&msg); + } +#endif /* XP_PC */ + +#if defined(XP_MAC) + OSErr err; + nsLookupElement * lookupElement; + nsDNSLookup * lookup; + + mThreadRunning = PR_TRUE; + + while (mThreadRunning) { + + PR_Mac_WaitForAsyncNotify(PR_INTERVAL_NO_TIMEOUT); + // check queue for completed DNS lookups + while ((lookupElement = (nsLookupElement *)mCompletionQueue.qHead) != nsnull) { + + err = Dequeue((QElemPtr)lookupElement, &mCompletionQueue); + if (err) + continue; // assert + + lookup = lookupElement->lookup; + // convert InetHostInfo to nsHostEnt + rv = lookup->FinishHostEntry(); + if (NS_SUCCEEDED(rv)) { + NS_ADDREF(lookup); + rv = lookup->CompletedLookup(NS_OK); // sets lookup->mComplete = PR_TRUE; + NS_RELEASE(lookup); + NS_ASSERTION(NS_SUCCEEDED(rv), "Completed failed"); + } + } + } +#endif /* XP_MAC */ + + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIDNSService methods: +//////////////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP +nsDNSService::Lookup(const char* hostName, + nsIDNSListener* userListener, + nsISupports* userContext, + nsIRequest* *result) +{ + nsresult rv; + nsDNSRequest* req; + + NS_WITH_SERVICE(nsIIOService, ios, kIOServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + PRBool offline; + rv = ios->GetOffline(&offline); + if (NS_FAILED(rv)) return rv; + if (offline) return NS_ERROR_OFFLINE; + + if (gNeedLateInitialization) { // check flag without monitor for speed + nsAutoMonitor mon(mMonitor); // in case another thread is about to start LateInit() + if (gNeedLateInitialization) { // check flag again to be sure we really need to do initialization + rv = LateInit(); + if (NS_FAILED(rv)) return rv; + } + } + +#if !defined(XP_UNIX) + if (mThread == nsnull) + return NS_ERROR_OFFLINE; +#endif + + nsDNSLookup * lookup; + rv = GetLookupEntry(hostName, &lookup); + if (NS_FAILED(rv)) return rv; + + req = new nsDNSRequest(); + if (req == nsnull) { + return NS_ERROR_OUT_OF_MEMORY; + } + + NS_ADDREF(req); + rv = req->Init(lookup, userListener, userContext); + if (NS_FAILED(rv)) goto done; + + rv = lookup->HandleRequest(req); + + done: + + NS_RELEASE(lookup); + NS_RELEASE(req); + if (NS_SUCCEEDED(rv)) { + *result = req; + } + return rv; +} + +nsresult +nsDNSService::GetLookupEntry(const char* hostName, + nsDNSLookup* *result) +{ + nsresult rv; + void* prev; + + nsAutoMonitor mon(mMonitor); + + nsStringKey key(hostName); + nsDNSLookup * lookup = (nsDNSLookup*)mLookups.Get(&key); + if (lookup) { + nsAutoCMonitor mon(lookup); + + if (lookup->mComplete && lookup->IsExpired()) { + lookup->Reset(); + } + *result = lookup; // already ADD_REF'd by Get(); + return NS_OK; + } + + // no lookup entry exists for this request, either because this + // is the first time, or an old one was cleaned out + + lookup = new nsDNSLookup(); + if (lookup == nsnull) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = lookup->Init(hostName); + if (NS_FAILED(rv)) goto done; + + NS_ADDREF(lookup); + prev = mLookups.Put(&key, lookup); + NS_ASSERTION(prev == nsnull, "already a nsDNSLookup entry"); + + *result = lookup; + done: + if (NS_FAILED(rv)) + delete lookup; + return rv; +} + +PRBool +nsDNSLookup::CompletedEntry(nsHashKey *aKey, void *aData, void* closure) +{ + nsresult rv; + nsDNSLookup* lookup = (nsDNSLookup*)aData; + nsresult* status = (nsresult*)closure; + + rv = lookup->CompletedLookup(*status); + NS_ASSERTION(NS_SUCCEEDED(rv), "lookup Completed failed"); + return PR_TRUE; // keep iterating +} + +NS_IMETHODIMP +nsDNSService::Shutdown() +{ + nsresult rv = NS_OK; + + if (mThread == nsnull) return rv; + + { + nsAutoMonitor mon(mMonitor); // protect mLookups + + nsresult status = NS_BINDING_ABORTED; + mLookups.Enumerate(nsDNSLookup::CompletedEntry, &status); + mLookups.Reset(); + +#if defined(XP_MAC) + + mThreadRunning = PR_FALSE; + + // deallocate Open Transport Service Provider + (void) OTCloseProvider((ProviderRef)mServiceRef); + CloseOpenTransport(); // should be moved to terminate routine + PRThread* dnsServiceThread; + rv = mThread->GetPRThread(&dnsServiceThread); + if (dnsServiceThread) + PR_Mac_PostAsyncNotify(dnsServiceThread); + rv = mThread->Join(); + +#elif defined(XP_PC) && !defined(XP_OS2) + + SendMessage(mDNSWindow, WM_DNS_SHUTDOWN, 0, 0); + rv = mThread->Join(); + +#elif defined(XP_UNIX) + // XXXX - ? +#endif + } + + // Have to break the cycle here, otherwise nsDNSService holds onto the thread + // and the thread holds onto the nsDNSService via its mRunnable + mThread = nsnull; + + PR_DestroyMonitor(mMonitor); // XXX also done in destructor + mMonitor = nsnull; + return rv; +} #if defined(XP_PC) && !defined(XP_OS2) @@ -666,243 +1235,4 @@ nsDNSService::FreeMsgID(PRUint32 msgID) #endif -// -// -------------------------------------------------------------------------- -// nsIRunnable implementation... -// -------------------------------------------------------------------------- -// -NS_IMETHODIMP -nsDNSService::Run(void) -{ - nsresult rv = NS_OK; - -#if defined(XP_PC) && !defined(XP_OS2) - MSG msg; - - InitDNSThread(); - - while(GetMessage(&msg, mDNSWindow, 0, 0)) { - // no TranslateMessage() because we're not expecting input - DispatchMessage(&msg); - } -#endif /* XP_PC */ - -#if defined(XP_MAC) - OSErr err; - nsLookupElement * lookupElement; - nsDNSLookup * lookup; - - mThreadRunning = PR_TRUE; - - while (mThreadRunning) { - - PR_Mac_WaitForAsyncNotify(PR_INTERVAL_NO_TIMEOUT); - // check queue for completed DNS lookups - while ((lookupElement = (nsLookupElement *)mCompletionQueue.qHead) != nsnull) { - - err = Dequeue((QElemPtr)lookupElement, &mCompletionQueue); - if (err) - continue; // assert - - lookup = lookupElement->lookup; - // convert InetHostInfo to nsHostEnt - rv = lookup->FinishHostEntry(); - if (NS_SUCCEEDED(rv)) { - // put lookup in cache - } - - // issue callbacks - rv = lookup->CallOnFound(); - - delete lookup; // until we start caching them - - } - } -#endif /* XP_MAC */ - - return rv; -} - - //////////////////////////////////////////////////////////////////////////////// -// nsIDNSService methods: -//////////////////////////////////////////////////////////////////////////////// - - -NS_IMETHODIMP -nsDNSService::Lookup(nsISupports* clientContext, - const char* hostName, - nsIDNSListener* listener, - nsIRequest* *DNSRequest) -{ - nsresult rv = NS_OK; - - NS_WITH_SERVICE(nsIIOService, ios, kIOServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - PRBool offline; - rv = ios->GetOffline(&offline); - if (NS_FAILED(rv)) return rv; - if (offline) return NS_ERROR_OFFLINE; - - if (gNeedLateInitialization) { - rv = LateInit(); - if (NS_FAILED(rv)) return rv; - } - - - PRStatus status = PR_SUCCESS; - - PRBool numeric = PR_TRUE; - for (const char *hostCheck = hostName; *hostCheck; hostCheck++) { - if (!nsCRT::IsAsciiDigit(*hostCheck) && (*hostCheck != '.') ) { - numeric = PR_FALSE; - break; - } - } - - if (numeric) { - PRIntervalTime startTime = PR_IntervalNow(); - nsHostEnt* hostentry = new nsHostEnt; - if (!hostentry) return NS_ERROR_OUT_OF_MEMORY; - - (void)listener->OnStartLookup(clientContext, hostName); - - // If it is numeric then try to convert it into an IP-Address - PRNetAddr *netAddr = (PRNetAddr*)nsAllocator::Alloc(sizeof(PRNetAddr)); - if (!netAddr) { - delete hostentry; - return NS_ERROR_OUT_OF_MEMORY; - } - status = PR_StringToNetAddr(hostName, netAddr); - if (PR_SUCCESS == status) { - // slam the IP in and move on. - PRHostEnt *ent = &(hostentry->hostEnt); - PRIntn bufLen = hostentry->bufLen = PR_NETDB_BUF_SIZE; - char *buffer = hostentry->buffer; - ent->h_name = (char*)BufAlloc(PL_strlen(hostName) + 1, - &buffer, - &bufLen, - 0); - memcpy(ent->h_name, hostName, PL_strlen(hostName) + 1); - - ent->h_aliases = (char**)BufAlloc(1 * sizeof(char*), - &buffer, - &bufLen, - sizeof(char **)); - ent->h_aliases[0] = '\0'; - - ent->h_addrtype = 2; - ent->h_length = 4; - ent->h_addr_list = (char**)BufAlloc(2 * sizeof(char*), - &buffer, - &bufLen, - sizeof(char **)); - ent->h_addr_list[0] = (char*)BufAlloc(ent->h_length, - &buffer, - &bufLen, - 0); - memcpy(ent->h_addr_list[0], &netAddr->inet.ip, ent->h_length); - ent->h_addr_list[1] = '\0'; - - (void)listener->OnFound(clientContext, hostName, hostentry); - - delete hostentry; - // XXX: The hostentry should really be reference counted so the - // listener does not need to copy it... - - rv = listener->OnStopLookup(clientContext, hostName, NS_OK); -#ifdef DNS_TIMING - if (gService->mOut) { - PRIntervalTime stopTime = PR_IntervalNow(); - double duration = PR_IntervalToMicroseconds(stopTime - startTime); - gService->mCount++; - gService->mTimes += duration; - gService->mSquaredTimes += duration * duration; - fprintf(gService->mOut, "DNS time #%d: %u us for %s\n", - (PRInt32)gService->mCount, - (PRInt32)duration, hostName); - } -#endif - return rv; - } - } - - // Incomming hostname is not a numeric ip address. Need to do the actual - // dns lookup. - - // create nsDNSLookup - nsDNSLookup * lookup = new nsDNSLookup(clientContext, hostName, listener); - if (!lookup) { - return NS_ERROR_OUT_OF_MEMORY; - } - -#if defined(XP_PC) && !defined(XP_OS2) - // save on outstanding lookup queue - PR_Lock(mThreadLock); - mCompletionQueue.AppendElement(lookup); - PR_Unlock(mThreadLock); -#endif /* XP_PC */ - - (void)listener->OnStartLookup(clientContext, hostName); - - // initiate async lookup - rv = lookup->InitiateDNSLookup(); - if (rv != NS_OK) { -#if defined(XP_PC) && !defined(XP_OS2) - PR_Lock(mThreadLock); - mCompletionQueue.RemoveElement(lookup); - PR_Unlock(mThreadLock); -#endif - delete lookup; - } - return rv; -} - -NS_IMETHODIMP -nsDNSService::Shutdown() -{ - nsresult rv = NS_OK; - - // XXX clean up outstanding requests - // XXX deallocate cache - -#if defined(XP_MAC) - mThreadRunning = PR_FALSE; - - // deallocate Open Transport Service Provider - if (mServiceRef) { - OSStatus status = OTCloseProvider((ProviderRef)mServiceRef); - } - CloseOpenTransport(); // should be moved to terminate routine - - PRThread* dnsServiceThread; - if (mThread) { - rv = mThread->GetPRThread(&dnsServiceThread); - if (dnsServiceThread) - PR_Mac_PostAsyncNotify(dnsServiceThread); - rv = mThread->Join(); - // Have to break the cycle here, otherwise nsDNSService holds onto the thread - // and the thread holds onto the nsDNSService via its mRunnable -// mThread = nsnull; - } - -#elif defined(XP_PC) && !defined(XP_OS2) - SendMessage(mDNSWindow, WM_DNS_SHUTDOWN, 0, 0); - if (mThread) { - rv = mThread->Join(); - // Have to break the cycle here, otherwise nsDNSService holds onto the thread - // and the thread holds onto the nsDNSService via its mRunnable - mThread = nsnull; - } - -#elif defined(XP_UNIX) - // XXXX - ? -#endif - - if (mThreadLock) { - PR_DestroyLock(mThreadLock); - mThreadLock = nsnull; - } - - return rv; -} diff --git a/mozilla/netwerk/dns/src/nsDnsService.h b/mozilla/netwerk/dns/src/nsDnsService.h index e0e7841220c..2be8cdbd25a 100644 --- a/mozilla/netwerk/dns/src/nsDnsService.h +++ b/mozilla/netwerk/dns/src/nsDnsService.h @@ -26,7 +26,7 @@ #include "nsIDNSService.h" #include "nsIRunnable.h" #include "nsIThread.h" -#include "nsVoidArray.h" +#include "nsISupportsArray.h" #if defined(XP_MAC) #include #include @@ -36,6 +36,8 @@ #include #endif #include "nsCOMPtr.h" +#include "nsHashtable.h" +#include "prmon.h" //#ifdef DEBUG #define DNS_TIMING 1 // XXX remove later @@ -50,39 +52,36 @@ class nsDNSService : public nsIDNSService, public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE + NS_DECL_NSIDNSSERVICE // nsDNSService methods: nsDNSService(); virtual ~nsDNSService(); - nsresult Init(); - nsresult LateInit(); - nsresult InitDNSThread(); // Define a Create method to be used with a factory: static NS_METHOD Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult); - // nsIDNSService methods: - NS_DECL_NSIDNSSERVICE - -protected: friend class nsDNSLookup; +protected: + nsresult LateInit(); + nsresult InitDNSThread(); + nsresult GetLookupEntry(const char* hostName, nsDNSLookup* *result); + static nsDNSService * gService; static PRBool gNeedLateInitialization; nsCOMPtr mThread; - PRLock * mThreadLock; nsresult mState; - - // nsDNSLookup cache? - list of nsDNSLookups, hash table (nsHashTable, nsStringKey) - // list of nsDNSLookups in order of expiration (PRCList?) + PRMonitor* mMonitor; + nsSupportsHashtable mLookups; // of nsDNSLookups #if defined(XP_MAC) friend pascal void nsDnsServiceNotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie); PRBool mThreadRunning; InetSvcRef mServiceRef; - QHdr mCompletionQueue; + QHdr mCompletionQueue; #if TARGET_CARBON OTClientContextPtr mClientContext; OTNotifyUPP nsDnsServiceNotifierRoutineUPP; @@ -94,8 +93,8 @@ protected: void FreeMsgID(PRUint32 msgID); friend static LRESULT CALLBACK nsDNSEventProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT LookupComplete(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); HWND mDNSWindow; - nsVoidArray mCompletionQueue; PRUint32 mMsgIDBitVector[4]; #endif /* XP_PC */