1 /* vim:set ts=4 sw=4 sts=4 et cin: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla.
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by IBM Corporation are Copyright (C) 2003
19 * IBM Corporation. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #if defined(MOZ_LOGGING)
42 #if defined(HAVE_RES_NINIT)
43 #include <sys/types.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <arpa/nameser.h>
48 #define RES_RETRY_ON_FAILURE
52 #include "nsHostResolver.h"
53 #include "nsNetError.h"
54 #include "nsISupportsBase.h"
55 #include "nsISupportsUtils.h"
56 #include "nsAutoLock.h"
57 #include "nsAutoPtr.h"
67 #include "nsURLHelper.h"
69 //----------------------------------------------------------------------------
71 // Use a persistent thread pool in order to avoid spinning up new threads all the time.
72 // In particular, thread creation results in a res_init() call from libc which is
75 // The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests
76 // go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS
77 // currently in the pool a new thread is created for high priority requests. If
78 // the new request is at a lower priority a new thread will only be created if
79 // there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be
80 // created or an idle thread located for the request it is queued.
82 // When the pool is greater than HighThreadThreshold in size a thread will be destroyed after
83 // ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a
86 #define MAX_NON_PRIORITY_REQUESTS 150
88 #define HighThreadThreshold MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY
89 #define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold
90 #define ShortIdleTimeoutSeconds 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS
92 PR_STATIC_ASSERT (HighThreadThreshold
<= MAX_RESOLVER_THREADS
);
94 //----------------------------------------------------------------------------
96 #if defined(PR_LOGGING)
97 static PRLogModuleInfo
*gHostResolverLog
= nsnull
;
98 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
103 //----------------------------------------------------------------------------
106 MoveCList(PRCList
&from
, PRCList
&to
)
108 if (!PR_CLIST_IS_EMPTY(&from
)) {
113 PR_INIT_CLIST(&from
);
120 PRTime now
= PR_Now(), minutes
, factor
;
121 LL_I2L(factor
, 60 * PR_USEC_PER_SEC
);
122 LL_DIV(minutes
, now
, factor
);
124 LL_L2UI(result
, minutes
);
128 //----------------------------------------------------------------------------
130 #if defined(RES_RETRY_ON_FAILURE)
132 // this class represents the resolver state for a given thread. if we
133 // encounter a lookup failure, then we can invoke the Reset method on an
134 // instance of this class to reset the resolver (in case /etc/resolv.conf
135 // for example changed). this is mainly an issue on GNU systems since glibc
136 // only reads in /etc/resolv.conf once per thread. it may be an issue on
137 // other systems as well.
143 // initialize mLastReset to the time when this object
144 // is created. this means that a reset will not occur
145 // if a thread is too young. the alternative would be
146 // to initialize this to the beginning of time, so that
147 // the first failure would cause a reset, but since the
148 // thread would have just started up, it likely would
149 // already have current /etc/resolv.conf info.
150 : mLastReset(PR_IntervalNow())
156 // reset no more than once per second
157 if (PR_IntervalToSeconds(PR_IntervalNow() - mLastReset
) < 1)
160 LOG(("calling res_ninit\n"));
162 mLastReset
= PR_IntervalNow();
163 return (res_ninit(&_res
) == 0);
167 PRIntervalTime mLastReset
;
170 #endif // RES_RETRY_ON_FAILURE
172 //----------------------------------------------------------------------------
174 // this macro filters out any flags that are not used when constructing the
175 // host key. the significant flags are those that would affect the resulting
176 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
177 #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
180 nsHostRecord::Create(const nsHostKey
*key
, nsHostRecord
**result
)
182 PRLock
*lock
= PR_NewLock();
184 return NS_ERROR_OUT_OF_MEMORY
;
186 size_t hostLen
= strlen(key
->host
) + 1;
187 size_t size
= hostLen
+ sizeof(nsHostRecord
);
189 nsHostRecord
*rec
= (nsHostRecord
*) ::operator new(size
);
191 PR_DestroyLock(lock
);
192 return NS_ERROR_OUT_OF_MEMORY
;
195 rec
->host
= ((char *) rec
) + sizeof(nsHostRecord
);
196 rec
->flags
= key
->flags
;
199 rec
->_refc
= 1; // addref
200 NS_LOG_ADDREF(rec
, 1, "nsHostRecord", sizeof(nsHostRecord
));
201 rec
->addr_info_lock
= lock
;
202 rec
->addr_info
= nsnull
;
203 rec
->addr_info_gencnt
= 0;
205 rec
->expiration
= NowInMinutes();
206 rec
->resolving
= PR_FALSE
;
207 rec
->onQueue
= PR_FALSE
;
209 PR_INIT_CLIST(&rec
->callbacks
);
210 rec
->negative
= PR_FALSE
;
211 memcpy((char *) rec
->host
, key
->host
, hostLen
);
217 nsHostRecord::~nsHostRecord()
220 PR_DestroyLock(addr_info_lock
);
222 PR_FreeAddrInfo(addr_info
);
227 //----------------------------------------------------------------------------
229 struct nsHostDBEnt
: PLDHashEntryHdr
235 HostDB_HashKey(PLDHashTable
*table
, const void *key
)
237 const nsHostKey
*hk
= static_cast<const nsHostKey
*>(key
);
238 return PL_DHashStringKey(table
, hk
->host
) ^ RES_KEY_FLAGS(hk
->flags
) ^ hk
->af
;
242 HostDB_MatchEntry(PLDHashTable
*table
,
243 const PLDHashEntryHdr
*entry
,
246 const nsHostDBEnt
*he
= static_cast<const nsHostDBEnt
*>(entry
);
247 const nsHostKey
*hk
= static_cast<const nsHostKey
*>(key
);
249 return !strcmp(he
->rec
->host
, hk
->host
) &&
250 RES_KEY_FLAGS (he
->rec
->flags
) == RES_KEY_FLAGS(hk
->flags
) &&
251 he
->rec
->af
== hk
->af
;
255 HostDB_MoveEntry(PLDHashTable
*table
,
256 const PLDHashEntryHdr
*from
,
259 static_cast<nsHostDBEnt
*>(to
)->rec
=
260 static_cast<const nsHostDBEnt
*>(from
)->rec
;
264 HostDB_ClearEntry(PLDHashTable
*table
,
265 PLDHashEntryHdr
*entry
)
267 LOG(("evicting record\n"));
268 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>(entry
);
269 #if defined(DEBUG) && defined(PR_LOGGING)
270 if (!he
->rec
->addr_info
)
271 LOG(("%s: => no addr_info\n", he
->rec
->host
));
273 PRInt32 now
= (PRInt32
) NowInMinutes();
274 PRInt32 diff
= (PRInt32
) he
->rec
->expiration
- now
;
275 LOG(("%s: exp=%d => %s\n",
277 PR_GetCanonNameFromAddrInfo(he
->rec
->addr_info
)));
282 iter
= PR_EnumerateAddrInfo(iter
, he
->rec
->addr_info
, 0, &addr
);
285 PR_NetAddrToString(&addr
, buf
, sizeof(buf
));
294 HostDB_InitEntry(PLDHashTable
*table
,
295 PLDHashEntryHdr
*entry
,
298 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>(entry
);
299 nsHostRecord::Create(static_cast<const nsHostKey
*>(key
), &he
->rec
);
303 static PLDHashTableOps gHostDB_ops
=
311 PL_DHashFinalizeStub
,
315 static PLDHashOperator
316 HostDB_RemoveEntry(PLDHashTable
*table
,
317 PLDHashEntryHdr
*hdr
,
321 return PL_DHASH_REMOVE
;
324 //----------------------------------------------------------------------------
326 nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries
,
327 PRUint32 maxCacheLifetime
)
328 : mMaxCacheEntries(maxCacheEntries
)
329 , mMaxCacheLifetime(maxCacheLifetime
)
331 , mIdleThreadCV(nsnull
)
334 , mAnyPriorityThreadCount(0)
339 mCreationTime
= PR_Now();
340 PR_INIT_CLIST(&mHighQ
);
341 PR_INIT_CLIST(&mMediumQ
);
342 PR_INIT_CLIST(&mLowQ
);
343 PR_INIT_CLIST(&mEvictionQ
);
345 mHighPriorityInfo
.self
= this;
346 mHighPriorityInfo
.onlyHighPriority
= PR_TRUE
;
347 mAnyPriorityInfo
.self
= this;
348 mAnyPriorityInfo
.onlyHighPriority
= PR_FALSE
;
350 mLongIdleTimeout
= PR_SecondsToInterval(LongIdleTimeoutSeconds
);
351 mShortIdleTimeout
= PR_SecondsToInterval(ShortIdleTimeoutSeconds
);
354 nsHostResolver::~nsHostResolver()
357 PR_DestroyCondVar(mIdleThreadCV
);
360 PR_DestroyLock(mLock
);
362 PL_DHashTableFinish(&mDB
);
366 nsHostResolver::Init()
368 mLock
= PR_NewLock();
370 return NS_ERROR_OUT_OF_MEMORY
;
372 mIdleThreadCV
= PR_NewCondVar(mLock
);
374 return NS_ERROR_OUT_OF_MEMORY
;
376 PL_DHashTableInit(&mDB
, &gHostDB_ops
, nsnull
, sizeof(nsHostDBEnt
), 0);
378 mShutdown
= PR_FALSE
;
380 #if defined(HAVE_RES_NINIT)
381 // We want to make sure the system is using the correct resolver settings,
382 // so we force it to reload those settings whenever we startup a subsequent
383 // nsHostResolver instance. We assume that there is no reason to do this
384 // for the first nsHostResolver instance since that is usually created
385 // during application startup.
386 static int initCount
= 0;
387 if (initCount
++ > 0) {
388 LOG(("calling res_ninit\n"));
396 nsHostResolver::ClearPendingQueue(PRCList
*aPendingQ
)
398 // loop through pending queue, erroring out pending lookups.
399 if (!PR_CLIST_IS_EMPTY(aPendingQ
)) {
400 PRCList
*node
= aPendingQ
->next
;
401 while (node
!= aPendingQ
) {
402 nsHostRecord
*rec
= static_cast<nsHostRecord
*>(node
);
404 OnLookupComplete(rec
, NS_ERROR_ABORT
, nsnull
);
410 nsHostResolver::Shutdown()
412 LOG(("nsHostResolver::Shutdown\n"));
414 PRCList pendingQHigh
, pendingQMed
, pendingQLow
, evictionQ
;
415 PR_INIT_CLIST(&pendingQHigh
);
416 PR_INIT_CLIST(&pendingQMed
);
417 PR_INIT_CLIST(&pendingQLow
);
418 PR_INIT_CLIST(&evictionQ
);
421 nsAutoLock
lock(mLock
);
425 MoveCList(mHighQ
, pendingQHigh
);
426 MoveCList(mMediumQ
, pendingQMed
);
427 MoveCList(mLowQ
, pendingQLow
);
428 MoveCList(mEvictionQ
, evictionQ
);
433 PR_NotifyAllCondVar(mIdleThreadCV
);
435 // empty host database
436 PL_DHashTableEnumerate(&mDB
, HostDB_RemoveEntry
, nsnull
);
439 ClearPendingQueue(&pendingQHigh
);
440 ClearPendingQueue(&pendingQMed
);
441 ClearPendingQueue(&pendingQLow
);
443 if (!PR_CLIST_IS_EMPTY(&evictionQ
)) {
444 PRCList
*node
= evictionQ
.next
;
445 while (node
!= &evictionQ
) {
446 nsHostRecord
*rec
= static_cast<nsHostRecord
*>(node
);
452 #ifdef NS_BUILD_REFCNT_LOGGING
454 // Logically join the outstanding worker threads with a timeout.
455 // Use this approach instead of PR_JoinThread() because that does
456 // not allow a timeout which may be necessary for a semi-responsive
457 // shutdown if the thread is blocked on a very slow DNS resolution.
458 // mThreadCount is read outside of mLock, but the worst case
459 // scenario for that race is one extra 25ms sleep.
461 PRIntervalTime delay
= PR_MillisecondsToInterval(25);
462 PRIntervalTime stopTime
= PR_IntervalNow() + PR_SecondsToInterval(20);
463 while (mThreadCount
&& PR_IntervalNow() < stopTime
)
469 IsHighPriority(PRUint16 flags
)
471 return !(flags
& (nsHostResolver::RES_PRIORITY_LOW
| nsHostResolver::RES_PRIORITY_MEDIUM
));
475 IsMediumPriority(PRUint16 flags
)
477 return flags
& nsHostResolver::RES_PRIORITY_MEDIUM
;
481 IsLowPriority(PRUint16 flags
)
483 return flags
& nsHostResolver::RES_PRIORITY_LOW
;
487 nsHostResolver::MoveQueue(nsHostRecord
*aRec
, PRCList
&aDestQ
)
489 NS_ASSERTION(aRec
->onQueue
, "Moving Host Record Not Currently Queued");
491 PR_REMOVE_LINK(aRec
);
492 PR_APPEND_LINK(aRec
, &aDestQ
);
496 nsHostResolver::ResolveHost(const char *host
,
499 nsResolveHostCallback
*callback
)
501 NS_ENSURE_TRUE(host
&& *host
, NS_ERROR_UNEXPECTED
);
503 LOG(("nsHostResolver::ResolveHost [host=%s]\n", host
));
505 // ensure that we are working with a valid hostname before proceeding. see
506 // bug 304904 for details.
507 if (!net_IsValidHostName(nsDependentCString(host
)))
508 return NS_ERROR_UNKNOWN_HOST
;
510 // if result is set inside the lock, then we need to issue the
511 // callback before returning.
512 nsRefPtr
<nsHostRecord
> result
;
513 nsresult status
= NS_OK
, rv
= NS_OK
;
515 nsAutoLock
lock(mLock
);
518 rv
= NS_ERROR_NOT_INITIALIZED
;
522 // unfortunately, PR_StringToNetAddr does not properly initialize
523 // the output buffer in the case of IPv6 input. see bug 223145.
524 memset(&tempAddr
, 0, sizeof(PRNetAddr
));
526 // check to see if there is already an entry for this |host|
527 // in the hash table. if so, then check to see if we can't
528 // just reuse the lookup result. otherwise, if there are
529 // any pending callbacks, then add to pending callbacks queue,
530 // and return. otherwise, add ourselves as first pending
531 // callback, and proceed to do the lookup.
533 nsHostKey key
= { host
, flags
, af
};
534 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>
535 (PL_DHashTableOperate(&mDB
, &key
, PL_DHASH_ADD
));
537 // if the record is null, then HostDB_InitEntry failed.
539 rv
= NS_ERROR_OUT_OF_MEMORY
;
540 // do we have a cached result that we can reuse?
541 else if (!(flags
& RES_BYPASS_CACHE
) &&
542 he
->rec
->HasResult() &&
543 NowInMinutes() <= he
->rec
->expiration
) {
544 LOG(("using cached record\n"));
545 // put reference to host record on stack...
547 if (he
->rec
->negative
) {
548 status
= NS_ERROR_UNKNOWN_HOST
;
549 if (!he
->rec
->resolving
)
550 // return the cached failure to the caller, but try and refresh
551 // the record in the background
552 IssueLookup(he
->rec
);
555 // if the host name is an IP address literal and has been parsed,
556 // go ahead and use it.
557 else if (he
->rec
->addr
) {
560 // try parsing the host name as an IP address literal to short
561 // circuit full host resolution. (this is necessary on some
562 // platforms like Win9x. see bug 219376 for more details.)
563 else if (PR_StringToNetAddr(host
, &tempAddr
) == PR_SUCCESS
) {
564 // ok, just copy the result into the host record, and be done
566 he
->rec
->addr
= (PRNetAddr
*) malloc(sizeof(PRNetAddr
));
568 status
= NS_ERROR_OUT_OF_MEMORY
;
570 memcpy(he
->rec
->addr
, &tempAddr
, sizeof(PRNetAddr
));
571 // put reference to host record on stack...
574 else if (mPendingCount
>= MAX_NON_PRIORITY_REQUESTS
&&
575 !IsHighPriority(flags
) &&
576 !he
->rec
->resolving
) {
577 // This is a lower priority request and we are swamped, so refuse it.
578 rv
= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
;
580 // otherwise, hit the resolver...
582 // Add callback to the list of pending callbacks.
583 PR_APPEND_LINK(callback
, &he
->rec
->callbacks
);
585 if (!he
->rec
->resolving
) {
586 he
->rec
->flags
= flags
;
587 rv
= IssueLookup(he
->rec
);
589 PR_REMOVE_AND_INIT_LINK(callback
);
591 else if (he
->rec
->onQueue
) {
592 // Consider the case where we are on a pending queue of
593 // lower priority than the request is being made at.
594 // In that case we should upgrade to the higher queue.
596 if (IsHighPriority(flags
) && !IsHighPriority(he
->rec
->flags
)) {
597 // Move from (low|med) to high.
598 MoveQueue(he
->rec
, mHighQ
);
599 he
->rec
->flags
= flags
;
600 ConditionallyCreateThread(he
->rec
);
601 } else if (IsMediumPriority(flags
) && IsLowPriority(he
->rec
->flags
)) {
602 // Move from low to med.
603 MoveQueue(he
->rec
, mMediumQ
);
604 he
->rec
->flags
= flags
;
611 callback
->OnLookupComplete(this, result
, status
);
616 nsHostResolver::DetachCallback(const char *host
,
619 nsResolveHostCallback
*callback
,
622 nsRefPtr
<nsHostRecord
> rec
;
624 nsAutoLock
lock(mLock
);
626 nsHostKey key
= { host
, flags
, af
};
627 nsHostDBEnt
*he
= static_cast<nsHostDBEnt
*>
628 (PL_DHashTableOperate(&mDB
, &key
, PL_DHASH_LOOKUP
));
630 // walk list looking for |callback|... we cannot assume
631 // that it will be there!
632 PRCList
*node
= he
->rec
->callbacks
.next
;
633 while (node
!= &he
->rec
->callbacks
) {
634 if (static_cast<nsResolveHostCallback
*>(node
) == callback
) {
635 PR_REMOVE_LINK(callback
);
644 // complete callback with the given status code; this would only be done if
645 // the record was in the process of being resolved.
647 callback
->OnLookupComplete(this, rec
, status
);
651 nsHostResolver::ConditionallyCreateThread(nsHostRecord
*rec
)
653 if (mNumIdleThreads
) {
654 // wake up idle thread to process this lookup
655 PR_NotifyCondVar(mIdleThreadCV
);
657 else if ((mThreadCount
< HighThreadThreshold
) ||
658 (IsHighPriority(rec
->flags
) && mThreadCount
< MAX_RESOLVER_THREADS
)) {
659 // dispatch new worker thread
660 NS_ADDREF_THIS(); // owning reference passed to thread
662 struct nsHostResolverThreadInfo
*info
;
664 if (mAnyPriorityThreadCount
< HighThreadThreshold
) {
665 info
= &mAnyPriorityInfo
;
666 mAnyPriorityThreadCount
++;
669 info
= &mHighPriorityInfo
;
672 PRThread
*thr
= PR_CreateThread(PR_SYSTEM_THREAD
,
677 PR_UNJOINABLE_THREAD
,
681 if (info
== &mAnyPriorityInfo
)
682 mAnyPriorityThreadCount
--;
684 return NS_ERROR_OUT_OF_MEMORY
;
687 #if defined(PR_LOGGING)
689 LOG(("lookup waiting for thread - %s ...\n", rec
->host
));
695 nsHostResolver::IssueLookup(nsHostRecord
*rec
)
698 NS_ASSERTION(!rec
->resolving
, "record is already being resolved");
700 // Add rec to one of the pending queues, possibly removing it from mEvictionQ.
701 // If rec is on mEvictionQ, then we can just move the owning
702 // reference over to the new active queue.
703 if (rec
->next
== rec
)
710 if (IsHighPriority(rec
->flags
))
711 PR_APPEND_LINK(rec
, &mHighQ
);
712 else if (IsMediumPriority(rec
->flags
))
713 PR_APPEND_LINK(rec
, &mMediumQ
);
715 PR_APPEND_LINK(rec
, &mLowQ
);
718 rec
->resolving
= PR_TRUE
;
719 rec
->onQueue
= PR_TRUE
;
721 rv
= ConditionallyCreateThread(rec
);
723 LOG (("DNS Thread Counters: total=%d any=%d high=%d idle=%d pending=%d\n",
725 mAnyPriorityThreadCount
,
726 mThreadCount
- mAnyPriorityThreadCount
,
734 nsHostResolver::DeQueue(PRCList
&aQ
, nsHostRecord
**aResult
)
736 *aResult
= static_cast<nsHostRecord
*>(aQ
.next
);
737 PR_REMOVE_AND_INIT_LINK(*aResult
);
739 (*aResult
)->onQueue
= PR_FALSE
;
743 nsHostResolver::GetHostToLookup(nsHostRecord
**result
, struct nsHostResolverThreadInfo
*aID
)
745 nsAutoLock
lock(mLock
);
747 PRIntervalTime start
= PR_IntervalNow(), timeout
;
750 // remove next record from Q; hand over owning reference. Check high, then med, then low
752 if (!PR_CLIST_IS_EMPTY(&mHighQ
)) {
753 DeQueue (mHighQ
, result
);
757 if (! aID
->onlyHighPriority
) {
758 if (!PR_CLIST_IS_EMPTY(&mMediumQ
)) {
759 DeQueue (mMediumQ
, result
);
763 if (!PR_CLIST_IS_EMPTY(&mLowQ
)) {
764 DeQueue (mLowQ
, result
);
769 timeout
= (mNumIdleThreads
>= HighThreadThreshold
) ? mShortIdleTimeout
: mLongIdleTimeout
;
770 // wait for one or more of the following to occur:
771 // (1) the pending queue has a host record to process
772 // (2) the shutdown flag has been set
773 // (3) the thread has been idle for too long
775 // PR_WaitCondVar will return when any of these conditions is true.
776 // become the idle thread and wait for a lookup
779 PR_WaitCondVar(mIdleThreadCV
, timeout
);
782 PRIntervalTime delta
= PR_IntervalNow() - start
;
783 if (delta
>= timeout
)
789 // tell thread to exit...
791 if (!aID
->onlyHighPriority
)
792 mAnyPriorityThreadCount
--;
797 nsHostResolver::OnLookupComplete(nsHostRecord
*rec
, nsresult status
, PRAddrInfo
*result
)
799 // get the list of pending callbacks for this lookup, and notify
800 // them that the lookup is complete.
804 nsAutoLock
lock(mLock
);
806 // grab list of callbacks to notify
807 MoveCList(rec
->callbacks
, cbs
);
809 // update record fields. We might have a rec->addr_info already if a
810 // previous lookup result expired and we're reresolving it..
811 PRAddrInfo
*old_addr_info
;
812 PR_Lock(rec
->addr_info_lock
);
813 old_addr_info
= rec
->addr_info
;
814 rec
->addr_info
= result
;
815 rec
->addr_info_gencnt
++;
816 PR_Unlock(rec
->addr_info_lock
);
818 PR_FreeAddrInfo(old_addr_info
);
819 rec
->expiration
= NowInMinutes();
821 rec
->expiration
+= mMaxCacheLifetime
;
822 rec
->negative
= PR_FALSE
;
825 rec
->expiration
+= 1; /* one minute for negative cache */
826 rec
->negative
= PR_TRUE
;
828 rec
->resolving
= PR_FALSE
;
830 if (rec
->addr_info
&& !mShutdown
) {
832 PR_APPEND_LINK(rec
, &mEvictionQ
);
834 if (mEvictionQSize
< mMaxCacheEntries
)
837 // remove first element on mEvictionQ
839 static_cast<nsHostRecord
*>(PR_LIST_HEAD(&mEvictionQ
));
840 PR_REMOVE_AND_INIT_LINK(head
);
841 PL_DHashTableOperate(&mDB
, (nsHostKey
*) head
, PL_DHASH_REMOVE
);
842 // release reference to rec owned by mEvictionQ
848 if (!PR_CLIST_IS_EMPTY(&cbs
)) {
849 PRCList
*node
= cbs
.next
;
850 while (node
!= &cbs
) {
851 nsResolveHostCallback
*callback
=
852 static_cast<nsResolveHostCallback
*>(node
);
854 callback
->OnLookupComplete(this, rec
, status
);
855 // NOTE: callback must not be dereferenced after this point!!
862 //----------------------------------------------------------------------------
865 nsHostResolver::ThreadFunc(void *arg
)
867 LOG(("nsHostResolver::ThreadFunc entering\n"));
868 #if defined(RES_RETRY_ON_FAILURE)
871 struct nsHostResolverThreadInfo
*info
= (struct nsHostResolverThreadInfo
*) arg
;
872 nsHostResolver
*resolver
= info
->self
;
875 while (resolver
->GetHostToLookup(&rec
, info
)) {
876 LOG(("resolving %s ...\n", rec
->host
));
878 PRIntn flags
= PR_AI_ADDRCONFIG
;
879 if (!(rec
->flags
& RES_CANON_NAME
))
880 flags
|= PR_AI_NOCANONNAME
;
882 ai
= PR_GetAddrInfoByName(rec
->host
, rec
->af
, flags
);
883 #if defined(RES_RETRY_ON_FAILURE)
884 if (!ai
&& rs
.Reset())
885 ai
= PR_GetAddrInfoByName(rec
->host
, rec
->af
, flags
);
888 // convert error code to nsresult.
889 nsresult status
= ai
? NS_OK
: NS_ERROR_UNKNOWN_HOST
;
890 resolver
->OnLookupComplete(rec
, status
, ai
);
891 LOG(("lookup complete for %s ...\n", rec
->host
));
893 NS_RELEASE(resolver
);
894 LOG(("nsHostResolver::ThreadFunc exiting\n"));
897 //----------------------------------------------------------------------------
900 nsHostResolver::Create(PRUint32 maxCacheEntries
,
901 PRUint32 maxCacheLifetime
,
902 nsHostResolver
**result
)
904 #if defined(PR_LOGGING)
905 if (!gHostResolverLog
)
906 gHostResolverLog
= PR_NewLogModule("nsHostResolver");
909 nsHostResolver
*res
= new nsHostResolver(maxCacheEntries
,
912 return NS_ERROR_OUT_OF_MEMORY
;
915 nsresult rv
= res
->Init();