netwerk/base/src/nsSocketTransportService2.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 // vim:set sw=4 sts=4 et cin:
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifdef MOZ_LOGGING
     7 #define FORCE_PR_LOG
     8 #endif
    10 #include "nsSocketTransportService2.h"
    11 #include "nsSocketTransport2.h"
    12 #include "nsError.h"
    13 #include "prnetdb.h"
    14 #include "prerror.h"
    15 #include "nsIPrefService.h"
    16 #include "nsIPrefBranch.h"
    17 #include "nsServiceManagerUtils.h"
    18 #include "NetworkActivityMonitor.h"
    19 #include "nsIObserverService.h"
    20 #include "mozilla/Services.h"
    21 #include "mozilla/Preferences.h"
    22 #include "mozilla/Likely.h"
    23 #include "mozilla/PublicSSL.h"
    24 #include "mozilla/ChaosMode.h"
    25 #include "mozilla/PodOperations.h"
    26 #include "nsThreadUtils.h"
    27 #include "nsIFile.h"
    29 using namespace mozilla;
    30 using namespace mozilla::net;
    32 #if defined(PR_LOGGING)
    33 PRLogModuleInfo *gSocketTransportLog = nullptr;
    34 #endif
    36 nsSocketTransportService *gSocketTransportService = nullptr;
    37 PRThread                 *gSocketThread           = nullptr;
    39 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
    40 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
    41 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
    42 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
    43 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
    44 #define SOCKET_LIMIT_TARGET 550U
    45 #define SOCKET_LIMIT_MIN     50U
    46 #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
    48 uint32_t nsSocketTransportService::gMaxCount;
    49 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
    51 //-----------------------------------------------------------------------------
    52 // ctor/dtor (called on the main/UI thread by the service manager)
    54 nsSocketTransportService::nsSocketTransportService()
    55     : mThread(nullptr)
    56     , mThreadEvent(nullptr)
    57     , mAutodialEnabled(false)
    58     , mLock("nsSocketTransportService::mLock")
    59     , mInitialized(false)
    60     , mShuttingDown(false)
    61     , mOffline(false)
    62     , mGoingOffline(false)
    63     , mActiveListSize(SOCKET_LIMIT_MIN)
    64     , mIdleListSize(SOCKET_LIMIT_MIN)
    65     , mActiveCount(0)
    66     , mIdleCount(0)
    67     , mSentBytesCount(0)
    68     , mReceivedBytesCount(0)
    69     , mSendBufferSize(0)
    70     , mKeepaliveIdleTimeS(600)
    71     , mKeepaliveRetryIntervalS(1)
    72     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
    73     , mKeepaliveEnabledPref(false)
    74     , mProbedMaxCount(false)
    75 {
    76 #if defined(PR_LOGGING)
    77     gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
    78 #endif
    80     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
    82     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
    83     mActiveList = (SocketContext *)
    84         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
    85     mIdleList = (SocketContext *)
    86         moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
    87     mPollList = (PRPollDesc *)
    88         moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
    90     NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
    91     gSocketTransportService = this;
    92 }
    94 nsSocketTransportService::~nsSocketTransportService()
    95 {
    96     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
    97     NS_ASSERTION(!mInitialized, "not shutdown properly");
    99     if (mThreadEvent)
   100         PR_DestroyPollableEvent(mThreadEvent);
   102     moz_free(mActiveList);
   103     moz_free(mIdleList);
   104     moz_free(mPollList);
   105     gSocketTransportService = nullptr;
   106 }
   108 //-----------------------------------------------------------------------------
   109 // event queue (any thread)
   111 already_AddRefed<nsIThread>
   112 nsSocketTransportService::GetThreadSafely()
   113 {
   114     MutexAutoLock lock(mLock);
   115     nsCOMPtr<nsIThread> result = mThread;
   116     return result.forget();
   117 }
   119 NS_IMETHODIMP
   120 nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags)
   121 {
   122     SOCKET_LOG(("STS dispatch [%p]\n", event));
   124     nsCOMPtr<nsIThread> thread = GetThreadSafely();
   125     nsresult rv;
   126     rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED;
   127     if (rv == NS_ERROR_UNEXPECTED) {
   128         // Thread is no longer accepting events. We must have just shut it
   129         // down on the main thread. Pretend we never saw it.
   130         rv = NS_ERROR_NOT_INITIALIZED;
   131     }
   132     return rv;
   133 }
   135 NS_IMETHODIMP
   136 nsSocketTransportService::IsOnCurrentThread(bool *result)
   137 {
   138     nsCOMPtr<nsIThread> thread = GetThreadSafely();
   139     NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
   140     return thread->IsOnCurrentThread(result);
   141 }
   143 //-----------------------------------------------------------------------------
   144 // socket api (socket thread only)
   146 NS_IMETHODIMP
   147 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
   148 {
   149     SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
   151     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   153     if (CanAttachSocket()) {
   154         return Dispatch(event, NS_DISPATCH_NORMAL);
   155     }
   157     mPendingSocketQ.PutEvent(event);
   158     return NS_OK;
   159 }
   161 NS_IMETHODIMP
   162 nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
   163 {
   164     SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
   166     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   168     if (!CanAttachSocket()) {
   169         return NS_ERROR_NOT_AVAILABLE;
   170     }
   172     SocketContext sock;
   173     sock.mFD = fd;
   174     sock.mHandler = handler;
   175     sock.mElapsedTime = 0;
   177     nsresult rv = AddToIdleList(&sock);
   178     if (NS_SUCCEEDED(rv))
   179         NS_ADDREF(handler);
   180     return rv;
   181 }
   183 nsresult
   184 nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
   185 {
   186     SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
   187     NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList),
   188                       "DetachSocket invalid head");
   190     // inform the handler that this socket is going away
   191     sock->mHandler->OnSocketDetached(sock->mFD);
   192     mSentBytesCount += sock->mHandler->ByteCountSent();
   193     mReceivedBytesCount += sock->mHandler->ByteCountReceived();
   195     // cleanup
   196     sock->mFD = nullptr;
   197     NS_RELEASE(sock->mHandler);
   199     if (listHead == mActiveList)
   200         RemoveFromPollList(sock);
   201     else
   202         RemoveFromIdleList(sock);
   204     // NOTE: sock is now an invalid pointer
   206     //
   207     // notify the first element on the pending socket queue...
   208     //
   209     nsCOMPtr<nsIRunnable> event;
   210     if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
   211         // move event from pending queue to dispatch queue
   212         return Dispatch(event, NS_DISPATCH_NORMAL);
   213     }
   214     return NS_OK;
   215 }
   217 nsresult
   218 nsSocketTransportService::AddToPollList(SocketContext *sock)
   219 {
   220     NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mActiveList)) < mActiveListSize),
   221                       "AddToPollList Socket Already Active");
   223     SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler));
   224     if (mActiveCount == mActiveListSize) {
   225         SOCKET_LOG(("  Active List size of %d met\n", mActiveCount));
   226         if (!GrowActiveList()) {
   227             NS_ERROR("too many active sockets");
   228             return NS_ERROR_OUT_OF_MEMORY;
   229         }
   230     }
   232     uint32_t newSocketIndex = mActiveCount;
   233     if (ChaosMode::isActive()) {
   234       newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
   235       PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
   236               mActiveCount - newSocketIndex);
   237       PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
   238               mActiveCount - newSocketIndex);
   239     }
   240     mActiveList[newSocketIndex] = *sock;
   241     mActiveCount++;
   243     mPollList[newSocketIndex + 1].fd = sock->mFD;
   244     mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
   245     mPollList[newSocketIndex + 1].out_flags = 0;
   247     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   248     return NS_OK;
   249 }
   251 void
   252 nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
   253 {
   254     SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler));
   256     uint32_t index = sock - mActiveList;
   257     NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index");
   259     SOCKET_LOG(("  index=%u mActiveCount=%u\n", index, mActiveCount));
   261     if (index != mActiveCount-1) {
   262         mActiveList[index] = mActiveList[mActiveCount-1];
   263         mPollList[index+1] = mPollList[mActiveCount];
   264     }
   265     mActiveCount--;
   267     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   268 }
   270 nsresult
   271 nsSocketTransportService::AddToIdleList(SocketContext *sock)
   272 {
   273     NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mIdleList)) < mIdleListSize),
   274                       "AddToIdlelList Socket Already Idle");
   276     SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler));
   277     if (mIdleCount == mIdleListSize) {
   278         SOCKET_LOG(("  Idle List size of %d met\n", mIdleCount));
   279         if (!GrowIdleList()) {
   280             NS_ERROR("too many idle sockets");
   281             return NS_ERROR_OUT_OF_MEMORY;
   282         }
   283     }
   285     mIdleList[mIdleCount] = *sock;
   286     mIdleCount++;
   288     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   289     return NS_OK;
   290 }
   292 void
   293 nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
   294 {
   295     SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler));
   297     uint32_t index = sock - mIdleList;
   298     NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
   300     if (index != mIdleCount-1)
   301         mIdleList[index] = mIdleList[mIdleCount-1];
   302     mIdleCount--;
   304     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   305 }
   307 void
   308 nsSocketTransportService::MoveToIdleList(SocketContext *sock)
   309 {
   310     nsresult rv = AddToIdleList(sock);
   311     if (NS_FAILED(rv))
   312         DetachSocket(mActiveList, sock);
   313     else
   314         RemoveFromPollList(sock);
   315 }
   317 void
   318 nsSocketTransportService::MoveToPollList(SocketContext *sock)
   319 {
   320     nsresult rv = AddToPollList(sock);
   321     if (NS_FAILED(rv))
   322         DetachSocket(mIdleList, sock);
   323     else
   324         RemoveFromIdleList(sock);
   325 }
   327 bool
   328 nsSocketTransportService::GrowActiveList()
   329 {
   330     int32_t toAdd = gMaxCount - mActiveListSize;
   331     if (toAdd > 100)
   332         toAdd = 100;
   333     if (toAdd < 1)
   334         return false;
   336     mActiveListSize += toAdd;
   337     mActiveList = (SocketContext *)
   338         moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
   339     mPollList = (PRPollDesc *)
   340         moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
   341     return true;
   342 }
   344 bool
   345 nsSocketTransportService::GrowIdleList()
   346 {
   347     int32_t toAdd = gMaxCount - mIdleListSize;
   348     if (toAdd > 100)
   349         toAdd = 100;
   350     if (toAdd < 1)
   351         return false;
   353     mIdleListSize += toAdd;
   354     mIdleList = (SocketContext *)
   355         moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
   356     return true;
   357 }
   359 PRIntervalTime
   360 nsSocketTransportService::PollTimeout()
   361 {
   362     if (mActiveCount == 0)
   363         return NS_SOCKET_POLL_TIMEOUT;
   365     // compute minimum time before any socket timeout expires.
   366     uint32_t minR = UINT16_MAX;
   367     for (uint32_t i=0; i<mActiveCount; ++i) {
   368         const SocketContext &s = mActiveList[i];
   369         // mPollTimeout could be less than mElapsedTime if setTimeout
   370         // was called with a value smaller than mElapsedTime.
   371         uint32_t r = (s.mElapsedTime < s.mHandler->mPollTimeout)
   372           ? s.mHandler->mPollTimeout - s.mElapsedTime
   373           : 0;
   374         if (r < minR)
   375             minR = r;
   376     }
   377     // nsASocketHandler defines UINT16_MAX as do not timeout
   378     if (minR == UINT16_MAX) {
   379         SOCKET_LOG(("poll timeout: none\n"));
   380         return NS_SOCKET_POLL_TIMEOUT;
   381     }
   382     SOCKET_LOG(("poll timeout: %lu\n", minR));
   383     return PR_SecondsToInterval(minR);
   384 }
   386 int32_t
   387 nsSocketTransportService::Poll(bool wait, uint32_t *interval)
   388 {
   389     PRPollDesc *pollList;
   390     uint32_t pollCount;
   391     PRIntervalTime pollTimeout;
   393     if (mPollList[0].fd) {
   394         mPollList[0].out_flags = 0;
   395         pollList = mPollList;
   396         pollCount = mActiveCount + 1;
   397         pollTimeout = PollTimeout();
   398     }
   399     else {
   400         // no pollable event, so busy wait...
   401         pollCount = mActiveCount;
   402         if (pollCount)
   403             pollList = &mPollList[1];
   404         else
   405             pollList = nullptr;
   406         pollTimeout = PR_MillisecondsToInterval(25);
   407     }
   409     if (!wait)
   410         pollTimeout = PR_INTERVAL_NO_WAIT;
   412     PRIntervalTime ts = PR_IntervalNow();
   414     SOCKET_LOG(("    timeout = %i milliseconds\n",
   415          PR_IntervalToMilliseconds(pollTimeout)));
   416     int32_t rv = PR_Poll(pollList, pollCount, pollTimeout);
   418     PRIntervalTime passedInterval = PR_IntervalNow() - ts;
   420     SOCKET_LOG(("    ...returned after %i milliseconds\n",
   421          PR_IntervalToMilliseconds(passedInterval))); 
   423     *interval = PR_IntervalToSeconds(passedInterval);
   424     return rv;
   425 }
   427 //-----------------------------------------------------------------------------
   428 // xpcom api
   430 NS_IMPL_ISUPPORTS(nsSocketTransportService,
   431                   nsISocketTransportService,
   432                   nsIEventTarget,
   433                   nsIThreadObserver,
   434                   nsIRunnable,
   435                   nsPISocketTransportService,
   436                   nsIObserver)
   438 // called from main thread only
   439 NS_IMETHODIMP
   440 nsSocketTransportService::Init()
   441 {
   442     if (!NS_IsMainThread()) {
   443         NS_ERROR("wrong thread");
   444         return NS_ERROR_UNEXPECTED;
   445     }
   447     if (mInitialized)
   448         return NS_OK;
   450     if (mShuttingDown)
   451         return NS_ERROR_UNEXPECTED;
   453     if (!mThreadEvent) {
   454         mThreadEvent = PR_NewPollableEvent();
   455         //
   456         // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
   457         // or similar software.
   458         //
   459         // NOTE: per bug 191739, this failure could also be caused by lack
   460         // of a loopback device on Windows and OS/2 platforms (NSPR creates
   461         // a loopback socket pair on these platforms to implement a pollable
   462         // event object).  if we can't create a pollable event, then we'll
   463         // have to "busy wait" to implement the socket event queue :-(
   464         //
   465         if (!mThreadEvent) {
   466             NS_WARNING("running socket transport thread without a pollable event");
   467             SOCKET_LOG(("running socket transport thread without a pollable event"));
   468         }
   469     }
   471     nsCOMPtr<nsIThread> thread;
   472     nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
   473     if (NS_FAILED(rv)) return rv;
   475     {
   476         MutexAutoLock lock(mLock);
   477         // Install our mThread, protecting against concurrent readers
   478         thread.swap(mThread);
   479     }
   481     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   482     if (tmpPrefService) {
   483         tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
   484         tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
   485         tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
   486         tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
   487         tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
   488     }
   489     UpdatePrefs();
   491     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   492     if (obsSvc) {
   493         obsSvc->AddObserver(this, "profile-initial-state", false);
   494         obsSvc->AddObserver(this, "last-pb-context-exited", false);
   495     }
   497     mInitialized = true;
   498     return NS_OK;
   499 }
   501 // called from main thread only
   502 NS_IMETHODIMP
   503 nsSocketTransportService::Shutdown()
   504 {
   505     SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
   507     NS_ENSURE_STATE(NS_IsMainThread());
   509     if (!mInitialized)
   510         return NS_OK;
   512     if (mShuttingDown)
   513         return NS_ERROR_UNEXPECTED;
   515     {
   516         MutexAutoLock lock(mLock);
   518         // signal the socket thread to shutdown
   519         mShuttingDown = true;
   521         if (mThreadEvent)
   522             PR_SetPollableEvent(mThreadEvent);
   523         // else wait for Poll timeout
   524     }
   526     // join with thread
   527     mThread->Shutdown();
   528     {
   529         MutexAutoLock lock(mLock);
   530         // Drop our reference to mThread and make sure that any concurrent
   531         // readers are excluded
   532         mThread = nullptr;
   533     }
   535     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   536     if (tmpPrefService) 
   537         tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
   539     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   540     if (obsSvc) {
   541         obsSvc->RemoveObserver(this, "profile-initial-state");
   542         obsSvc->RemoveObserver(this, "last-pb-context-exited");
   543     }
   545     mozilla::net::NetworkActivityMonitor::Shutdown();
   547     mInitialized = false;
   548     mShuttingDown = false;
   550     return NS_OK;
   551 }
   553 NS_IMETHODIMP
   554 nsSocketTransportService::GetOffline(bool *offline)
   555 {
   556     *offline = mOffline;
   557     return NS_OK;
   558 }
   560 NS_IMETHODIMP
   561 nsSocketTransportService::SetOffline(bool offline)
   562 {
   563     MutexAutoLock lock(mLock);
   564     if (!mOffline && offline) {
   565         // signal the socket thread to go offline, so it will detach sockets
   566         mGoingOffline = true;
   567         mOffline = true;
   568     }
   569     else if (mOffline && !offline) {
   570         mOffline = false;
   571     }
   572     if (mThreadEvent)
   573         PR_SetPollableEvent(mThreadEvent);
   575     return NS_OK;
   576 }
   578 NS_IMETHODIMP
   579 nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
   580 {
   581     MOZ_ASSERT(aKeepaliveIdleTimeS);
   582     if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
   583         return NS_ERROR_NULL_POINTER;
   584     }
   585     *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
   586     return NS_OK;
   587 }
   589 NS_IMETHODIMP
   590 nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
   591 {
   592     MOZ_ASSERT(aKeepaliveRetryIntervalS);
   593     if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
   594         return NS_ERROR_NULL_POINTER;
   595     }
   596     *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
   597     return NS_OK;
   598 }
   600 NS_IMETHODIMP
   601 nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
   602 {
   603     MOZ_ASSERT(aKeepaliveProbeCount);
   604     if (NS_WARN_IF(!aKeepaliveProbeCount)) {
   605         return NS_ERROR_NULL_POINTER;
   606     }
   607     *aKeepaliveProbeCount = mKeepaliveProbeCount;
   608     return NS_OK;
   609 }
   611 NS_IMETHODIMP
   612 nsSocketTransportService::CreateTransport(const char **types,
   613                                           uint32_t typeCount,
   614                                           const nsACString &host,
   615                                           int32_t port,
   616                                           nsIProxyInfo *proxyInfo,
   617                                           nsISocketTransport **result)
   618 {
   619     NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
   620     NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
   622     nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
   623     nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
   624     if (NS_FAILED(rv)) {
   625         return rv;
   626     }
   628     trans.forget(result);
   629     return NS_OK;
   630 }
   632 NS_IMETHODIMP
   633 nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath,
   634                                                     nsISocketTransport **result)
   635 {
   636     nsresult rv;
   638     NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
   640     nsAutoCString path;
   641     rv = aPath->GetNativePath(path);
   642     if (NS_FAILED(rv))
   643         return rv;
   645     nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
   647     rv = trans->InitWithFilename(path.get());
   648     if (NS_FAILED(rv))
   649         return rv;
   651     trans.forget(result);
   652     return NS_OK;
   653 }
   655 NS_IMETHODIMP
   656 nsSocketTransportService::GetAutodialEnabled(bool *value)
   657 {
   658     *value = mAutodialEnabled;
   659     return NS_OK;
   660 }
   662 NS_IMETHODIMP
   663 nsSocketTransportService::SetAutodialEnabled(bool value)
   664 {
   665     mAutodialEnabled = value;
   666     return NS_OK;
   667 }
   669 NS_IMETHODIMP
   670 nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
   671 {
   672     MutexAutoLock lock(mLock);
   673     if (mThreadEvent)
   674         PR_SetPollableEvent(mThreadEvent);
   675     return NS_OK;
   676 }
   678 NS_IMETHODIMP
   679 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
   680                                              bool mayWait, uint32_t depth)
   681 {
   682     return NS_OK;
   683 }
   685 NS_IMETHODIMP
   686 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
   687                                                 uint32_t depth,
   688                                                 bool eventWasProcessed)
   689 {
   690     return NS_OK;
   691 }
   693 #ifdef MOZ_NUWA_PROCESS
   694 #include "ipc/Nuwa.h"
   695 #endif
   697 NS_IMETHODIMP
   698 nsSocketTransportService::Run()
   699 {
   700     PR_SetCurrentThreadName("Socket Thread");
   702 #ifdef MOZ_NUWA_PROCESS
   703     if (IsNuwaProcess()) {
   704         NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
   705                      "NuwaMarkCurrentThread is undefined!");
   706         NuwaMarkCurrentThread(nullptr, nullptr);
   707     }
   708 #endif
   710     SOCKET_LOG(("STS thread init\n"));
   712     psm::InitializeSSLServerCertVerificationThreads();
   714     gSocketThread = PR_GetCurrentThread();
   716     // add thread event to poll list (mThreadEvent may be nullptr)
   717     mPollList[0].fd = mThreadEvent;
   718     mPollList[0].in_flags = PR_POLL_READ;
   719     mPollList[0].out_flags = 0;
   721     nsIThread *thread = NS_GetCurrentThread();
   723     // hook ourselves up to observe event processing for this thread
   724     nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
   725     threadInt->SetObserver(this);
   727     // make sure the pseudo random number generator is seeded on this thread
   728     srand(static_cast<unsigned>(PR_Now()));
   730     for (;;) {
   731         bool pendingEvents = false;
   732         thread->HasPendingEvents(&pendingEvents);
   734         do {
   735             // If there are pending events for this thread then
   736             // DoPollIteration() should service the network without blocking.
   737             DoPollIteration(!pendingEvents);
   739             // If nothing was pending before the poll, it might be now
   740             if (!pendingEvents)
   741                 thread->HasPendingEvents(&pendingEvents);
   743             if (pendingEvents) {
   744                 NS_ProcessNextEvent(thread);
   745                 pendingEvents = false;
   746                 thread->HasPendingEvents(&pendingEvents);
   747             }
   748         } while (pendingEvents);
   750         bool goingOffline = false;
   751         // now that our event queue is empty, check to see if we should exit
   752         {
   753             MutexAutoLock lock(mLock);
   754             if (mShuttingDown)
   755                 break;
   756             if (mGoingOffline) {
   757                 mGoingOffline = false;
   758                 goingOffline = true;
   759             }
   760         }
   761         // Avoid potential deadlock
   762         if (goingOffline)
   763             Reset(true);
   764     }
   766     SOCKET_LOG(("STS shutting down thread\n"));
   768     // detach all sockets, including locals
   769     Reset(false);
   771     // Final pass over the event queue. This makes sure that events posted by
   772     // socket detach handlers get processed.
   773     NS_ProcessPendingEvents(thread);
   775     gSocketThread = nullptr;
   777     psm::StopSSLServerCertVerificationThreads();
   779     SOCKET_LOG(("STS thread exit\n"));
   780     return NS_OK;
   781 }
   783 void
   784 nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
   785                                                 SocketContext *socketList,
   786                                                 int32_t index)
   787 {
   788     bool isGuarded = false;
   789     if (aGuardLocals) {
   790         socketList[index].mHandler->IsLocal(&isGuarded);
   791         if (!isGuarded)
   792             socketList[index].mHandler->KeepWhenOffline(&isGuarded);
   793     }
   794     if (!isGuarded)
   795         DetachSocket(socketList, &socketList[index]);
   796 }
   798 void
   799 nsSocketTransportService::Reset(bool aGuardLocals)
   800 {
   801     // detach any sockets
   802     int32_t i;
   803     for (i = mActiveCount - 1; i >= 0; --i) {
   804         DetachSocketWithGuard(aGuardLocals, mActiveList, i);
   805     }
   806     for (i = mIdleCount - 1; i >= 0; --i) {
   807         DetachSocketWithGuard(aGuardLocals, mIdleList, i);
   808     }
   809 }
   811 nsresult
   812 nsSocketTransportService::DoPollIteration(bool wait)
   813 {
   814     SOCKET_LOG(("STS poll iter [%d]\n", wait));
   816     int32_t i, count;
   818     //
   819     // poll loop
   820     //
   821     // walk active list backwards to see if any sockets should actually be
   822     // idle, then walk the idle list backwards to see if any idle sockets
   823     // should become active.  take care to check only idle sockets that
   824     // were idle to begin with ;-)
   825     //
   826     count = mIdleCount;
   827     for (i=mActiveCount-1; i>=0; --i) {
   828         //---
   829         SOCKET_LOG(("  active [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
   830             mActiveList[i].mHandler,
   831             mActiveList[i].mHandler->mCondition,
   832             mActiveList[i].mHandler->mPollFlags));
   833         //---
   834         if (NS_FAILED(mActiveList[i].mHandler->mCondition))
   835             DetachSocket(mActiveList, &mActiveList[i]);
   836         else {
   837             uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
   838             if (in_flags == 0)
   839                 MoveToIdleList(&mActiveList[i]);
   840             else {
   841                 // update poll flags
   842                 mPollList[i+1].in_flags = in_flags;
   843                 mPollList[i+1].out_flags = 0;
   844             }
   845         }
   846     }
   847     for (i=count-1; i>=0; --i) {
   848         //---
   849         SOCKET_LOG(("  idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
   850             mIdleList[i].mHandler,
   851             mIdleList[i].mHandler->mCondition,
   852             mIdleList[i].mHandler->mPollFlags));
   853         //---
   854         if (NS_FAILED(mIdleList[i].mHandler->mCondition))
   855             DetachSocket(mIdleList, &mIdleList[i]);
   856         else if (mIdleList[i].mHandler->mPollFlags != 0)
   857             MoveToPollList(&mIdleList[i]);
   858     }
   860     SOCKET_LOG(("  calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
   862 #if defined(XP_WIN)
   863     // 30 active connections is the historic limit before firefox 7's 256. A few
   864     //  windows systems have troubles with the higher limit, so actively probe a
   865     // limit the first time we exceed 30.
   866     if ((mActiveCount > 30) && !mProbedMaxCount)
   867         ProbeMaxCount();
   868 #endif
   870     // Measures seconds spent while blocked on PR_Poll
   871     uint32_t pollInterval;
   873     int32_t n = Poll(wait, &pollInterval);
   874     if (n < 0) {
   875         SOCKET_LOG(("  PR_Poll error [%d]\n", PR_GetError()));
   876     }
   877     else {
   878         //
   879         // service "active" sockets...
   880         //
   881         for (i=0; i<int32_t(mActiveCount); ++i) {
   882             PRPollDesc &desc = mPollList[i+1];
   883             SocketContext &s = mActiveList[i];
   884             if (n > 0 && desc.out_flags != 0) {
   885                 s.mElapsedTime = 0;
   886                 s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
   887             }
   888             // check for timeout errors unless disabled...
   889             else if (s.mHandler->mPollTimeout != UINT16_MAX) {
   890                 // update elapsed time counter
   891                 // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value
   892                 // here -- otherwise, some compilers will treat it as signed,
   893                 // which makes them fire signed/unsigned-comparison build
   894                 // warnings for the comparison against 'pollInterval'.)
   895                 if (MOZ_UNLIKELY(pollInterval >
   896                                 static_cast<uint32_t>(UINT16_MAX) -
   897                                 s.mElapsedTime))
   898                     s.mElapsedTime = UINT16_MAX;
   899                 else
   900                     s.mElapsedTime += uint16_t(pollInterval);
   901                 // check for timeout expiration 
   902                 if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
   903                     s.mElapsedTime = 0;
   904                     s.mHandler->OnSocketReady(desc.fd, -1);
   905                 }
   906             }
   907         }
   909         //
   910         // check for "dead" sockets and remove them (need to do this in
   911         // reverse order obviously).
   912         //
   913         for (i=mActiveCount-1; i>=0; --i) {
   914             if (NS_FAILED(mActiveList[i].mHandler->mCondition))
   915                 DetachSocket(mActiveList, &mActiveList[i]);
   916         }
   918         if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
   919             // acknowledge pollable event (wait should not block)
   920             if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
   921                 // On Windows, the TCP loopback connection in the
   922                 // pollable event may become broken when a laptop
   923                 // switches between wired and wireless networks or
   924                 // wakes up from hibernation.  We try to create a
   925                 // new pollable event.  If that fails, we fall back
   926                 // on "busy wait".
   927                 {
   928                     MutexAutoLock lock(mLock);
   929                     PR_DestroyPollableEvent(mThreadEvent);
   930                     mThreadEvent = PR_NewPollableEvent();
   931                 }
   932                 if (!mThreadEvent) {
   933                     NS_WARNING("running socket transport thread without "
   934                                "a pollable event");
   935                     SOCKET_LOG(("running socket transport thread without "
   936                          "a pollable event"));
   937                 }
   938                 mPollList[0].fd = mThreadEvent;
   939                 // mPollList[0].in_flags was already set to PR_POLL_READ
   940                 // in Run().
   941                 mPollList[0].out_flags = 0;
   942             }
   943         }
   944     }
   946     return NS_OK;
   947 }
   949 nsresult
   950 nsSocketTransportService::UpdatePrefs()
   951 {
   952     mSendBufferSize = 0;
   954     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   955     if (tmpPrefService) {
   956         int32_t bufferSize;
   957         nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
   958         if (NS_SUCCEEDED(rv) && bufferSize > 0)
   959             mSendBufferSize = bufferSize;
   961         // Default TCP Keepalive Values.
   962         int32_t keepaliveIdleTimeS;
   963         rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
   964                                         &keepaliveIdleTimeS);
   965         if (NS_SUCCEEDED(rv))
   966             mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
   967                                           1, kMaxTCPKeepIdle);
   969         int32_t keepaliveRetryIntervalS;
   970         rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
   971                                         &keepaliveRetryIntervalS);
   972         if (NS_SUCCEEDED(rv))
   973             mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
   974                                                1, kMaxTCPKeepIntvl);
   976         int32_t keepaliveProbeCount;
   977         rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
   978                                         &keepaliveProbeCount);
   979         if (NS_SUCCEEDED(rv))
   980             mKeepaliveProbeCount = clamped(keepaliveProbeCount,
   981                                            1, kMaxTCPKeepCount);
   982         bool keepaliveEnabled = false;
   983         rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
   984                                          &keepaliveEnabled);
   985         if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
   986             mKeepaliveEnabledPref = keepaliveEnabled;
   987             OnKeepaliveEnabledPrefChange();
   988         }
   989     }
   991     return NS_OK;
   992 }
   994 void
   995 nsSocketTransportService::OnKeepaliveEnabledPrefChange()
   996 {
   997     // Dispatch to socket thread if we're not executing there.
   998     if (PR_GetCurrentThread() != gSocketThread) {
   999         gSocketTransportService->Dispatch(
  1000             NS_NewRunnableMethod(
  1001                 this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
  1002             NS_DISPATCH_NORMAL);
  1003         return;
  1006     SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
  1007                 mKeepaliveEnabledPref ? "enabled" : "disabled"));
  1009     // Notify each socket that keepalive has been en/disabled globally.
  1010     for (int32_t i = mActiveCount - 1; i >= 0; --i) {
  1011         NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
  1013     for (int32_t i = mIdleCount - 1; i >= 0; --i) {
  1014         NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
  1018 void
  1019 nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
  1021     MOZ_ASSERT(sock, "SocketContext cannot be null!");
  1022     MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
  1024     if (!sock || !sock->mHandler) {
  1025         return;
  1028     sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
  1031 NS_IMETHODIMP
  1032 nsSocketTransportService::Observe(nsISupports *subject,
  1033                                   const char *topic,
  1034                                   const char16_t *data)
  1036     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  1037         UpdatePrefs();
  1038         return NS_OK;
  1041     if (!strcmp(topic, "profile-initial-state")) {
  1042         int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0);
  1043         if (blipInterval <= 0) {
  1044             return NS_OK;
  1047         return net::NetworkActivityMonitor::Init(blipInterval);
  1050     if (!strcmp(topic, "last-pb-context-exited")) {
  1051         nsCOMPtr<nsIRunnable> ev =
  1052           NS_NewRunnableMethod(this,
  1053                                &nsSocketTransportService::ClosePrivateConnections);
  1054         nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
  1055         NS_ENSURE_SUCCESS(rv, rv);
  1058     return NS_OK;
  1061 void
  1062 nsSocketTransportService::ClosePrivateConnections()
  1064     // Must be called on the socket thread.
  1065 #ifdef DEBUG
  1066     bool onSTSThread;
  1067     IsOnCurrentThread(&onSTSThread);
  1068     MOZ_ASSERT(onSTSThread);
  1069 #endif
  1071     for (int32_t i = mActiveCount - 1; i >= 0; --i) {
  1072         if (mActiveList[i].mHandler->mIsPrivate) {
  1073             DetachSocket(mActiveList, &mActiveList[i]);
  1076     for (int32_t i = mIdleCount - 1; i >= 0; --i) {
  1077         if (mIdleList[i].mHandler->mIsPrivate) {
  1078             DetachSocket(mIdleList, &mIdleList[i]);
  1082     mozilla::ClearPrivateSSLState();
  1085 NS_IMETHODIMP
  1086 nsSocketTransportService::GetSendBufferSize(int32_t *value)
  1088     *value = mSendBufferSize;
  1089     return NS_OK;
  1093 /// ugly OS specific includes are placed at the bottom of the src for clarity
  1095 #if defined(XP_WIN)
  1096 #include <windows.h>
  1097 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
  1098 #include <sys/resource.h>
  1099 #endif
  1101 // Right now the only need to do this is on windows.
  1102 #if defined(XP_WIN)
  1103 void
  1104 nsSocketTransportService::ProbeMaxCount()
  1106     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1108     if (mProbedMaxCount)
  1109         return;
  1110     mProbedMaxCount = true;
  1112     // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
  1113     // sockets. See bug 692260 - windows should be able to handle 1000 sockets
  1114     // in select() without a problem, but LSPs have been known to balk at lower
  1115     // numbers. (64 in the bug).
  1117     // Allocate
  1118     struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
  1119     uint32_t numAllocated = 0;
  1121     for (uint32_t index = 0 ; index < gMaxCount; ++index) {
  1122         pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
  1123         pfd[index].out_flags = 0;
  1124         pfd[index].fd =  PR_OpenTCPSocket(PR_AF_INET);
  1125         if (!pfd[index].fd) {
  1126             SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
  1127             if (index < SOCKET_LIMIT_MIN)
  1128                 gMaxCount = SOCKET_LIMIT_MIN;
  1129             else
  1130                 gMaxCount = index;
  1131             break;
  1133         ++numAllocated;
  1136     // Test
  1137     PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U);
  1138     while (gMaxCount <= numAllocated) {
  1139         int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
  1141         SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
  1142                     gMaxCount, rv));
  1144         if (rv >= 0)
  1145             break;
  1147         SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
  1148                     gMaxCount, rv, PR_GetError()));
  1150         gMaxCount -= 32;
  1151         if (gMaxCount <= SOCKET_LIMIT_MIN) {
  1152             gMaxCount = SOCKET_LIMIT_MIN;
  1153             break;
  1157     // Free
  1158     for (uint32_t index = 0 ; index < numAllocated; ++index)
  1159         if (pfd[index].fd)
  1160             PR_Close(pfd[index].fd);
  1162     SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
  1164 #endif // windows
  1166 PRStatus
  1167 nsSocketTransportService::DiscoverMaxCount()
  1169     gMaxCount = SOCKET_LIMIT_MIN;
  1171 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
  1172     // On unix and os x network sockets and file
  1173     // descriptors are the same. OS X comes defaulted at 256,
  1174     // most linux at 1000. We can reliably use [sg]rlimit to
  1175     // query that and raise it. We will try to raise it 250 past
  1176     // our target number of SOCKET_LIMIT_TARGET so that some descriptors
  1177     // are still available for other things.
  1179     struct rlimit rlimitData;
  1180     if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
  1181         return PR_SUCCESS;
  1182     if (rlimitData.rlim_cur >=  SOCKET_LIMIT_TARGET + 250) {
  1183         gMaxCount = SOCKET_LIMIT_TARGET;
  1184         return PR_SUCCESS;
  1187     int32_t maxallowed = rlimitData.rlim_max;
  1188     if (maxallowed == -1) {                       /* no limit */
  1189         maxallowed = SOCKET_LIMIT_TARGET + 250;
  1190     } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) {
  1191         return PR_SUCCESS;
  1192     } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) {
  1193         maxallowed = SOCKET_LIMIT_TARGET + 250;
  1196     rlimitData.rlim_cur = maxallowed;
  1197     setrlimit(RLIMIT_NOFILE, &rlimitData);
  1198     if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1)
  1199         if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250)
  1200             gMaxCount = rlimitData.rlim_cur - 250;
  1202 #elif defined(XP_WIN) && !defined(WIN_CE)
  1203     // >= XP is confirmed to have at least 1000
  1204     gMaxCount = SOCKET_LIMIT_TARGET;
  1205 #else
  1206     // other platforms are harder to test - so leave at safe legacy value
  1207 #endif
  1209     return PR_SUCCESS;
  1213 // Used to return connection info to Dashboard.cpp
  1214 void
  1215 nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
  1216         struct SocketContext *context, bool aActive)
  1218     if (context->mHandler->mIsPrivate)
  1219         return;
  1220     PRFileDesc *aFD = context->mFD;
  1221     bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP);
  1223     PRNetAddr peer_addr;
  1224     PR_GetPeerName(aFD, &peer_addr);
  1226     char host[64] = {0};
  1227     PR_NetAddrToString(&peer_addr, host, sizeof(host));
  1229     uint16_t port;
  1230     if (peer_addr.raw.family == PR_AF_INET)
  1231         port = peer_addr.inet.port;
  1232     else
  1233         port = peer_addr.ipv6.port;
  1234     port = PR_ntohs(port);
  1235     uint64_t sent = context->mHandler->ByteCountSent();
  1236     uint64_t received = context->mHandler->ByteCountReceived();
  1237     SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
  1239     data->AppendElement(info);
  1242 void
  1243 nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
  1245     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1246     for (uint32_t i = 0; i < mActiveCount; i++)
  1247         AnalyzeConnection(data, &mActiveList[i], true);
  1248     for (uint32_t i = 0; i < mIdleCount; i++)
  1249         AnalyzeConnection(data, &mIdleList[i], false);

mercurial