Bug 467562. Fix race between the timeout on our DNS thread waits and signals to those threads that could cause DNS requests to go AWOL every so often. r+sr=bzbarsky

This commit is contained in:
Patrick McManus 2009-01-14 12:18:27 -05:00
Родитель 0a6f663f0b
Коммит e02c016fc7
2 изменённых файлов: 47 добавлений и 57 удалений

Просмотреть файл

@ -205,6 +205,7 @@ nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
rec->expiration = NowInMinutes();
rec->resolving = PR_FALSE;
rec->onQueue = PR_FALSE;
rec->usingAnyThread = PR_FALSE;
PR_INIT_CLIST(rec);
PR_INIT_CLIST(&rec->callbacks);
rec->negative = PR_FALSE;
@ -331,7 +332,7 @@ nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
, mIdleThreadCV(nsnull)
, mNumIdleThreads(0)
, mThreadCount(0)
, mAnyPriorityThreadCount(0)
, mActiveAnyThreadCount(0)
, mEvictionQSize(0)
, mPendingCount(0)
, mShutdown(PR_TRUE)
@ -342,11 +343,6 @@ nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
PR_INIT_CLIST(&mLowQ);
PR_INIT_CLIST(&mEvictionQ);
mHighPriorityInfo.self = this;
mHighPriorityInfo.onlyHighPriority = PR_TRUE;
mAnyPriorityInfo.self = this;
mAnyPriorityInfo.onlyHighPriority = PR_FALSE;
mLongIdleTimeout = PR_SecondsToInterval(LongIdleTimeoutSeconds);
mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds);
}
@ -602,6 +598,7 @@ nsHostResolver::ResolveHost(const char *host,
// Move from low to med.
MoveQueue(he->rec, mMediumQ);
he->rec->flags = flags;
PR_NotifyCondVar(mIdleThreadCV);
}
}
}
@ -659,27 +656,16 @@ nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
// dispatch new worker thread
NS_ADDREF_THIS(); // owning reference passed to thread
struct nsHostResolverThreadInfo *info;
if (mAnyPriorityThreadCount < HighThreadThreshold) {
info = &mAnyPriorityInfo;
mAnyPriorityThreadCount++;
}
else
info = &mHighPriorityInfo;
mThreadCount++;
PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD,
ThreadFunc,
info,
this,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_UNJOINABLE_THREAD,
0);
if (!thr) {
mThreadCount--;
if (info == &mAnyPriorityInfo)
mAnyPriorityThreadCount--;
NS_RELEASE_THIS();
return NS_ERROR_OUT_OF_MEMORY;
}
@ -720,10 +706,9 @@ nsHostResolver::IssueLookup(nsHostRecord *rec)
rv = ConditionallyCreateThread(rec);
LOG (("DNS Thread Counters: total=%d any=%d high=%d idle=%d pending=%d\n",
LOG (("DNS Thread Counters: total=%d any-live=%d idle=%d pending=%d\n",
mThreadCount,
mAnyPriorityThreadCount,
mThreadCount - mAnyPriorityThreadCount,
mActiveAnyThreadCount,
mNumIdleThreads,
mPendingCount));
@ -740,12 +725,16 @@ nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult)
}
PRBool
nsHostResolver::GetHostToLookup(nsHostRecord **result, struct nsHostResolverThreadInfo *aID)
nsHostResolver::GetHostToLookup(nsHostRecord **result)
{
PRBool timedOut = PR_FALSE;
PRIntervalTime epoch, now, timeout;
nsAutoLock lock(mLock);
PRIntervalTime start = PR_IntervalNow(), timeout;
timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
epoch = PR_IntervalNow();
while (!mShutdown) {
// remove next record from Q; hand over owning reference. Check high, then med, then low
@ -754,42 +743,51 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result, struct nsHostResolverThre
return PR_TRUE;
}
if (! aID->onlyHighPriority) {
if (mActiveAnyThreadCount < HighThreadThreshold) {
if (!PR_CLIST_IS_EMPTY(&mMediumQ)) {
DeQueue (mMediumQ, result);
mActiveAnyThreadCount++;
(*result)->usingAnyThread = PR_TRUE;
return PR_TRUE;
}
if (!PR_CLIST_IS_EMPTY(&mLowQ)) {
DeQueue (mLowQ, result);
mActiveAnyThreadCount++;
(*result)->usingAnyThread = PR_TRUE;
return PR_TRUE;
}
}
timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
// Determining timeout is racy, so allow one cycle through checking the queues
// before exiting.
if (timedOut)
break;
// wait for one or more of the following to occur:
// (1) the pending queue has a host record to process
// (2) the shutdown flag has been set
// (3) the thread has been idle for too long
//
// PR_WaitCondVar will return when any of these conditions is true.
// become the idle thread and wait for a lookup
mNumIdleThreads++;
PR_WaitCondVar(mIdleThreadCV, timeout);
mNumIdleThreads--;
PRIntervalTime delta = PR_IntervalNow() - start;
if (delta >= timeout)
break;
timeout -= delta;
start += delta;
now = PR_IntervalNow();
if ((PRIntervalTime)(now - epoch) >= timeout)
timedOut = PR_TRUE;
else {
// It is possible that PR_WaitCondVar() was interrupted and returned early,
// in which case we will loop back and re-enter it. In that case we want to
// do so with the new timeout reduced to reflect time already spent waiting.
timeout -= (PRIntervalTime)(now - epoch);
epoch = now;
}
}
// tell thread to exit...
mThreadCount--;
if (!aID->onlyHighPriority)
mAnyPriorityThreadCount--;
return PR_FALSE;
}
@ -827,6 +825,11 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo
}
rec->resolving = PR_FALSE;
if (rec->usingAnyThread) {
mActiveAnyThreadCount--;
rec->usingAnyThread = PR_FALSE;
}
if (rec->addr_info && !mShutdown) {
// add to mEvictionQ
PR_APPEND_LINK(rec, &mEvictionQ);
@ -868,11 +871,10 @@ nsHostResolver::ThreadFunc(void *arg)
#if defined(RES_RETRY_ON_FAILURE)
nsResState rs;
#endif
struct nsHostResolverThreadInfo *info = (struct nsHostResolverThreadInfo *) arg;
nsHostResolver *resolver = info->self;
nsHostResolver *resolver = (nsHostResolver *)arg;
nsHostRecord *rec;
PRAddrInfo *ai;
while (resolver->GetHostToLookup(&rec, info)) {
while (resolver->GetHostToLookup(&rec)) {
LOG(("resolving %s ...\n", rec->host));
PRIntn flags = PR_AI_ADDRCONFIG;

Просмотреть файл

@ -131,7 +131,8 @@ private:
* one of the worker threads. */
PRBool onQueue; /* true if pending and on the queue (not yet given to getaddrinfo())*/
PRBool usingAnyThread; /* true if off queue and contributing to mActiveAnyThreadCount */
~nsHostRecord();
};
@ -165,16 +166,6 @@ public:
nsresult status) = 0;
};
/**
* nsHostResolverThreadInfo structures are passed to the resolver
* thread.
*/
struct nsHostResolverThreadInfo
{
nsHostResolver *self;
PRBool onlyHighPriority;
};
/**
* nsHostResolver - an asynchronous host name resolver.
*/
@ -241,12 +232,9 @@ private:
nsHostResolver(PRUint32 maxCacheEntries=50, PRUint32 maxCacheLifetime=1);
~nsHostResolver();
// nsHostResolverThreadInfo * is passed to the ThreadFunc
struct nsHostResolverThreadInfo mHighPriorityInfo, mAnyPriorityInfo;
nsresult Init();
nsresult IssueLookup(nsHostRecord *);
PRBool GetHostToLookup(nsHostRecord **m, struct nsHostResolverThreadInfo *aID);
PRBool GetHostToLookup(nsHostRecord **m);
void OnLookupComplete(nsHostRecord *, nsresult, PRAddrInfo *);
void DeQueue(PRCList &aQ, nsHostRecord **aResult);
void ClearPendingQueue(PRCList *aPendingQueue);
@ -262,7 +250,7 @@ private:
PRCondVar *mIdleThreadCV; // non-null if idle thread
PRUint32 mNumIdleThreads;
PRUint32 mThreadCount;
PRUint32 mAnyPriorityThreadCount;
PRUint32 mActiveAnyThreadCount;
PLDHashTable mDB;
PRCList mHighQ;
PRCList mMediumQ;