Bug 1471280 - Use nsThreadPool for DNS resolver threads r=bagder
authorValentin Gosu <valentin.gosu@gmail.com>
Wed, 04 Jul 2018 20:36:58 +0200
changeset 425156 db553cb4218deaae330d6a0692b7a00e22ecc03e
parent 425155 1fc5a72a842f4581dec2f6c88d309e982facd8c0
child 425157 d7788f2fd8a2d23d5cdd179115b2d7258d6d4f02
push id34237
push userapavel@mozilla.com
push dateThu, 05 Jul 2018 16:28:59 +0000
treeherdermozilla-central@ce32b59b5a49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbagder
bugs1471280
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1471280 - Use nsThreadPool for DNS resolver threads r=bagder 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"
@@ -531,21 +533,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);
 }
@@ -587,16 +589,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()) {
@@ -669,18 +678,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);
@@ -700,30 +709,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)
 {
@@ -984,17 +995,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()) {
@@ -1046,41 +1057,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;
 }
 
@@ -1221,19 +1221,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()
 {
@@ -1317,17 +1317,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()) {
@@ -1359,19 +1359,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
@@ -1874,22 +1874,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:
     /*