Bug 1471280 - Use nsThreadPool for DNS resolver threads draft
authorValentin Gosu <valentin.gosu@gmail.com>
Wed, 04 Jul 2018 20:36:58 +0200
changeset 814224 58e8c141cf41e2a7746612ae999c69af549bdeab
parent 814220 1102f5fd4b584ab0a1f11c215cf1f3d678a41732
child 814225 f5e9c8844137b18c8a764c449eb01c8f3732b067
push id115137
push uservalentin.gosu@gmail.com
push dateWed, 04 Jul 2018 19:44:12 +0000
bugs1471280
milestone63.0a1
Bug 1471280 - Use nsThreadPool for DNS resolver threads Instead of creating and deleteing each thread, we use a nsThreadPool with a max of 8 resolver threads. Whereas before each thread would run ThreadFunc exactly once then shut down, the threads may now remain active for a while. During this time we may post another task(runnable) to the thread. MozReview-Commit-ID: FiE370ic1ah
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -15,17 +15,19 @@
 #include <stdlib.h>
 #include <ctime>
 #include "nsHostResolver.h"
 #include "nsError.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsUtils.h"
 #include "nsIThreadManager.h"
 #include "nsAutoPtr.h"
+#include "nsComponentManagerUtils.h"
 #include "nsPrintfCString.h"
+#include "nsXPCOMCIDInternal.h"
 #include "prthread.h"
 #include "prerror.h"
 #include "prtime.h"
 #include "mozilla/Logging.h"
 #include "PLDHashTable.h"
 #include "plstr.h"
 #include "nsURLHelper.h"
 #include "nsThreadUtils.h"
@@ -521,21 +523,21 @@ NS_IMPL_ISUPPORTS0(nsHostResolver)
 
 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
                                uint32_t defaultCacheEntryLifetime,
                                uint32_t defaultGracePeriod)
     : mMaxCacheEntries(maxCacheEntries)
     , mDefaultCacheLifetime(defaultCacheEntryLifetime)
     , mDefaultGracePeriod(defaultGracePeriod)
     , mLock("nsHostResolver.mLock")
-    , mIdleThreadCV(mLock, "nsHostResolver.mIdleThreadCV")
+    , mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV")
     , mEvictionQSize(0)
     , mShutdown(true)
-    , mNumIdleThreads(0)
-    , mThreadCount(0)
+    , mNumIdleTasks(0)
+    , mActiveTaskCount(0)
     , mActiveAnyThreadCount(0)
     , mPendingCount(0)
 {
     mCreationTime = PR_Now();
 
     mLongIdleTimeout  = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
     mShortIdleTimeout = TimeDuration::FromSeconds(ShortIdleTimeoutSeconds);
 }
@@ -577,16 +579,23 @@ nsHostResolver::Init()
     // during application startup.
     static int initCount = 0;
     if (initCount++ > 0) {
         LOG(("Calling 'res_ninit'.\n"));
         res_ninit(&_res);
     }
 #endif
 
+    nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+    MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(MAX_RESOLVER_THREADS));
+    MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(MAX_RESOLVER_THREADS));
+    MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize));
+    MOZ_ALWAYS_SUCCEEDS(threadPool->SetName(NS_LITERAL_CSTRING("DNS Resolver")));
+    mResolverThreads = threadPool.forget();
+
     return NS_OK;
 }
 
 void
 nsHostResolver::ClearPendingQueue(LinkedList<RefPtr<nsHostRecord>>& aPendingQ)
 {
     // loop through pending queue, erroring out pending lookups.
     if (!aPendingQ.isEmpty()) {
@@ -659,18 +668,18 @@ nsHostResolver::Shutdown()
         pendingQHigh = std::move(mHighQ);
         pendingQMed = std::move(mMediumQ);
         pendingQLow = std::move(mLowQ);
         evictionQ = std::move(mEvictionQ);
 
         mEvictionQSize = 0;
         mPendingCount = 0;
 
-        if (mNumIdleThreads)
-            mIdleThreadCV.NotifyAll();
+        if (mNumIdleTasks)
+            mIdleTaskCV.NotifyAll();
 
         // empty host database
         mRecordDB.Clear();
     }
 
     ClearPendingQueue(pendingQHigh);
     ClearPendingQueue(pendingQMed);
     ClearPendingQueue(pendingQLow);
@@ -690,30 +699,32 @@ nsHostResolver::Shutdown()
         iter.UserData()->Cancel();
     }
 #ifdef NS_BUILD_REFCNT_LOGGING
 
     // Logically join the outstanding worker threads with a timeout.
     // Use this approach instead of PR_JoinThread() because that does
     // not allow a timeout which may be necessary for a semi-responsive
     // shutdown if the thread is blocked on a very slow DNS resolution.
-    // mThreadCount is read outside of mLock, but the worst case
+    // mActiveTaskCount is read outside of mLock, but the worst case
     // scenario for that race is one extra 25ms sleep.
 
     PRIntervalTime delay = PR_MillisecondsToInterval(25);
     PRIntervalTime stopTime = PR_IntervalNow() + PR_SecondsToInterval(20);
-    while (mThreadCount && PR_IntervalNow() < stopTime)
+    while (mActiveTaskCount && PR_IntervalNow() < stopTime)
         PR_Sleep(delay);
 #endif
 
     {
         mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                              "Failed to shutdown GetAddrInfo");
     }
+
+    mResolverThreads->Shutdown();
 }
 
 nsresult
 nsHostResolver::GetHostRecord(const nsACString &host,
                               uint16_t flags, uint16_t af, bool pb,
                               const nsCString &originSuffix,
                               nsHostRecord **result)
 {
@@ -974,17 +985,17 @@ nsHostResolver::ResolveHost(const nsACSt
                         ConditionallyCreateThread(rec);
                     } else if (IsMediumPriority(flags) &&
                                IsLowPriority(rec->flags)) {
                         // Move from low to med.
                         NS_ASSERTION(rec->onQueue, "Moving Host Record Not Currently Queued");
                         rec->remove();
                         mMediumQ.insertBack(rec);
                         rec->flags = flags;
-                        mIdleThreadCV.Notify();
+                        mIdleTaskCV.Notify();
                     }
                 }
             }
         }
     }
 
     if (result) {
         if (callback->isInList()) {
@@ -1036,41 +1047,30 @@ nsHostResolver::DetachCallback(const nsA
     if (rec) {
         callback->OnResolveHostComplete(this, rec, status);
     }
 }
 
 nsresult
 nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
 {
-    if (mNumIdleThreads) {
-        // wake up idle thread to process this lookup
-        mIdleThreadCV.Notify();
+    if (mNumIdleTasks) {
+        // wake up idle tasks to process this lookup
+        mIdleTaskCV.Notify();
     }
-    else if ((mThreadCount < HighThreadThreshold) ||
-             (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) {
-        static nsThreadPoolNaming naming;
-        nsCString name = naming.GetNextThreadName("DNS Resolver");
-
-        // dispatch new worker thread
-        nsCOMPtr<nsIThread> thread;
-        nsresult rv = NS_NewNamedThread(name, getter_AddRefs(thread), nullptr,
-                                        nsIThreadManager::kThreadPoolStackSize);
-        if (NS_WARN_IF(NS_FAILED(rv)) || !thread) {
-            return rv;
-        }
-
+    else if ((mActiveTaskCount < HighThreadThreshold) ||
+             (IsHighPriority(rec->flags) && mActiveTaskCount < MAX_RESOLVER_THREADS)) {
         nsCOMPtr<nsIRunnable> event =
             mozilla::NewRunnableMethod("nsHostResolver::ThreadFunc",
                                        this,
                                        &nsHostResolver::ThreadFunc);
-        mThreadCount++;
-        rv = thread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+        mActiveTaskCount++;
+        nsresult rv = mResolverThreads->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
         if (NS_FAILED(rv)) {
-            mThreadCount--;
+            mActiveTaskCount--;
         }
     }
     else {
         LOG(("  Unable to find a thread for looking up host [%s].\n", rec->host.get()));
     }
     return NS_OK;
 }
 
@@ -1211,19 +1211,19 @@ nsHostResolver::NativeLookup(nsHostRecor
     rec->mNative = true;
     rec->mNativeUsed = true;
     rec->onQueue = true;
     rec->mResolving++;
 
     nsresult rv = ConditionallyCreateThread(rec);
 
     LOG (("  DNS thread counters: total=%d any-live=%d idle=%d pending=%d\n",
-          static_cast<uint32_t>(mThreadCount),
+          static_cast<uint32_t>(mActiveTaskCount),
           static_cast<uint32_t>(mActiveAnyThreadCount),
-          static_cast<uint32_t>(mNumIdleThreads),
+          static_cast<uint32_t>(mNumIdleTasks),
           static_cast<uint32_t>(mPendingCount)));
 
     return rv;
 }
 
 ResolverMode
 nsHostResolver::Mode()
 {
@@ -1307,17 +1307,17 @@ bool
 nsHostResolver::GetHostToLookup(nsHostRecord **result)
 {
     bool timedOut = false;
     TimeDuration timeout;
     TimeStamp epoch, now;
 
     MutexAutoLock lock(mLock);
 
-    timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
+    timeout = (mNumIdleTasks >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
     epoch = TimeStamp::Now();
 
     while (!mShutdown) {
         // remove next record from Q; hand over owning reference. Check high, then med, then low
 
 #define SET_GET_TTL(var, val) (var)->mGetTtl = sGetTtlEnabled && (val)
 
         if (!mHighQ.isEmpty()) {
@@ -1349,19 +1349,19 @@ nsHostResolver::GetHostToLookup(nsHostRe
         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
 
-        mNumIdleThreads++;
-        mIdleThreadCV.Wait(timeout);
-        mNumIdleThreads--;
+        mNumIdleTasks++;
+        mIdleTaskCV.Wait(timeout);
+        mNumIdleTasks--;
 
         now = TimeStamp::Now();
 
         if (now - epoch >= timeout) {
             timedOut = true;
         } else {
             // It is possible that CondVar::Wait() was interrupted and returned
             // early, in which case we will loop back and re-enter it. In that
@@ -1861,22 +1861,18 @@ nsHostResolver::ThreadFunc()
         if (LOOKUP_RESOLVEAGAIN == CompleteLookup(rec, status, ai, rec->pb)) {
             // leave 'rec' assigned and loop to make a renewed host resolve
             LOG(("DNS lookup thread - Re-resolving host [%s].\n", rec->host.get()));
         } else {
             rec = nullptr;
         }
     } while(true);
 
-    nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
-    NS_DispatchToMainThread(NS_NewRunnableFunction("nsHostResolver::ThreadFunc::AsyncShutdown", [thread]() {
-        thread->AsyncShutdown();
-    }));
-    mThreadCount--;
-    LOG(("DNS lookup thread - queue empty, thread finished.\n"));
+    mActiveTaskCount--;
+    LOG(("DNS lookup thread - queue empty, task finished.\n"));
 }
 
 void
 nsHostResolver::SetCacheLimits(uint32_t aMaxCacheEntries,
                                uint32_t aDefaultCacheEntryLifetime,
                                uint32_t aDefaultGracePeriod)
 {
     MutexAutoLock lock(mLock);
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -18,16 +18,17 @@
 #include "GetAddrInfo.h"
 #include "mozilla/net/DNS.h"
 #include "mozilla/net/DashboardTypes.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "nsRefPtrHashtable.h"
+#include "nsIThreadPool.h"
 
 class nsHostResolver;
 class nsResolveHostCallback;
 namespace mozilla { namespace net {
 class TRR;
 enum ResolverMode {
   MODE_NATIVEONLY, // 0 - TRR OFF (by default)
   MODE_PARALLEL,   // 1 - race and use the first response
@@ -439,30 +440,32 @@ private:
         METHOD_NETWORK_FIRST = 6,
         METHOD_NETWORK_SHARED = 7
     };
 
     uint32_t      mMaxCacheEntries;
     uint32_t      mDefaultCacheLifetime; // granularity seconds
     uint32_t      mDefaultGracePeriod; // granularity seconds
     mutable Mutex mLock;    // mutable so SizeOfIncludingThis can be const
-    CondVar       mIdleThreadCV;
+    CondVar       mIdleTaskCV;
     nsRefPtrHashtable<nsGenericHashKey<nsHostKey>, nsHostRecord> mRecordDB;
     mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ;
     mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ;
     mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ;
     mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
     uint32_t      mEvictionQSize;
     PRTime        mCreationTime;
     mozilla::TimeDuration mLongIdleTimeout;
     mozilla::TimeDuration mShortIdleTimeout;
 
+    RefPtr<nsIThreadPool> mResolverThreads;
+
     mozilla::Atomic<bool>     mShutdown;
-    mozilla::Atomic<uint32_t> mNumIdleThreads;
-    mozilla::Atomic<uint32_t> mThreadCount;
+    mozilla::Atomic<uint32_t> mNumIdleTasks;
+    mozilla::Atomic<uint32_t> mActiveTaskCount;
     mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
     mozilla::Atomic<uint32_t> mPendingCount;
 
     // Set the expiration time stamps appropriately.
     void PrepareRecordExpiration(nsHostRecord* rec) const;
 
 public:
     /*