зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0a6f663f0b
Коммит
e02c016fc7
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче