LCOV - code coverage report
Current view: top level - netwerk/base - nsSocketTransportService2.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 368 704 52.3 %
Date: 2017-07-14 16:53:18 Functions: 34 56 60.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include "nsSocketTransportService2.h"
       7             : #include "nsSocketTransport2.h"
       8             : #include "NetworkActivityMonitor.h"
       9             : #include "mozilla/IntegerPrintfMacros.h"
      10             : #include "mozilla/Preferences.h"
      11             : #include "nsIOService.h"
      12             : #include "nsASocketHandler.h"
      13             : #include "nsError.h"
      14             : #include "prnetdb.h"
      15             : #include "prerror.h"
      16             : #include "nsIPrefService.h"
      17             : #include "nsIPrefBranch.h"
      18             : #include "nsServiceManagerUtils.h"
      19             : #include "nsIObserverService.h"
      20             : #include "mozilla/Atomics.h"
      21             : #include "mozilla/Services.h"
      22             : #include "mozilla/Likely.h"
      23             : #include "mozilla/PublicSSL.h"
      24             : #include "mozilla/ChaosMode.h"
      25             : #include "mozilla/PodOperations.h"
      26             : #include "mozilla/Telemetry.h"
      27             : #include "nsThreadUtils.h"
      28             : #include "nsIFile.h"
      29             : #include "nsIWidget.h"
      30             : #include "mozilla/dom/FlyWebService.h"
      31             : 
      32             : #ifdef MOZ_TASK_TRACER
      33             : #include "GeckoTaskTracer.h"
      34             : #endif
      35             : 
      36             : namespace mozilla {
      37             : namespace net {
      38             : 
      39             : LazyLogModule gSocketTransportLog("nsSocketTransport");
      40             : LazyLogModule gUDPSocketLog("UDPSocket");
      41             : LazyLogModule gTCPSocketLog("TCPSocket");
      42             : 
      43             : nsSocketTransportService *gSocketTransportService = nullptr;
      44             : static Atomic<PRThread*, Relaxed> gSocketThread;
      45             : 
      46             : #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
      47             : #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
      48             : #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
      49             : #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
      50             : #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
      51             : #define SOCKET_LIMIT_TARGET 1000U
      52             : #define SOCKET_LIMIT_MIN      50U
      53             : #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
      54             : #define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
      55             : #define TELEMETRY_PREF "toolkit.telemetry.enabled"
      56             : #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
      57             : 
      58             : #define REPAIR_POLLABLE_EVENT_TIME 10
      59             : 
      60             : uint32_t nsSocketTransportService::gMaxCount;
      61             : PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
      62             : 
      63             : // Utility functions
      64             : bool
      65         592 : OnSocketThread()
      66             : {
      67         592 :   return PR_GetCurrentThread() == gSocketThread;
      68             : }
      69             : 
      70             : //-----------------------------------------------------------------------------
      71             : // ctor/dtor (called on the main/UI thread by the service manager)
      72             : 
      73           3 : nsSocketTransportService::nsSocketTransportService()
      74             :     : mThread(nullptr)
      75             :     , mLock("nsSocketTransportService::mLock")
      76             :     , mInitialized(false)
      77             :     , mShuttingDown(false)
      78             :     , mOffline(false)
      79             :     , mGoingOffline(false)
      80             :     , mRawThread(nullptr)
      81             :     , mActiveListSize(SOCKET_LIMIT_MIN)
      82             :     , mIdleListSize(SOCKET_LIMIT_MIN)
      83             :     , mActiveCount(0)
      84             :     , mIdleCount(0)
      85             :     , mSentBytesCount(0)
      86             :     , mReceivedBytesCount(0)
      87             :     , mSendBufferSize(0)
      88             :     , mKeepaliveIdleTimeS(600)
      89             :     , mKeepaliveRetryIntervalS(1)
      90             :     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
      91             :     , mKeepaliveEnabledPref(false)
      92             :     , mServingPendingQueue(false)
      93             :     , mMaxTimePerPollIter(100)
      94             :     , mTelemetryEnabledPref(false)
      95             :     , mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
      96             :     , mSleepPhase(false)
      97           3 :     , mProbedMaxCount(false)
      98             : #if defined(XP_WIN)
      99             :     , mPolling(false)
     100             : #endif
     101             : {
     102           3 :     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
     103             : 
     104           3 :     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
     105           3 :     mActiveList = (SocketContext *)
     106           3 :         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
     107           3 :     mIdleList = (SocketContext *)
     108           3 :         moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
     109           3 :     mPollList = (PRPollDesc *)
     110           3 :         moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
     111             : 
     112           3 :     NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
     113           3 :     gSocketTransportService = this;
     114           3 : }
     115             : 
     116           0 : nsSocketTransportService::~nsSocketTransportService()
     117             : {
     118           0 :     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
     119           0 :     NS_ASSERTION(!mInitialized, "not shutdown properly");
     120             : 
     121           0 :     free(mActiveList);
     122           0 :     free(mIdleList);
     123           0 :     free(mPollList);
     124           0 :     gSocketTransportService = nullptr;
     125           0 : }
     126             : 
     127             : //-----------------------------------------------------------------------------
     128             : // event queue (any thread)
     129             : 
     130             : already_AddRefed<nsIThread>
     131          91 : nsSocketTransportService::GetThreadSafely()
     132             : {
     133         182 :     MutexAutoLock lock(mLock);
     134         182 :     nsCOMPtr<nsIThread> result = mThread;
     135         182 :     return result.forget();
     136             : }
     137             : 
     138             : NS_IMETHODIMP
     139           0 : nsSocketTransportService::DispatchFromScript(nsIRunnable *event, uint32_t flags)
     140             : {
     141           0 :   nsCOMPtr<nsIRunnable> event_ref(event);
     142           0 :   return Dispatch(event_ref.forget(), flags);
     143             : }
     144             : 
     145             : NS_IMETHODIMP
     146          88 : nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags)
     147             : {
     148         176 :     nsCOMPtr<nsIRunnable> event_ref(event);
     149          88 :     SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get()));
     150             : 
     151         176 :     nsCOMPtr<nsIThread> thread = GetThreadSafely();
     152             :     nsresult rv;
     153          88 :     rv = thread ? thread->Dispatch(event_ref.forget(), flags) : NS_ERROR_NOT_INITIALIZED;
     154          88 :     if (rv == NS_ERROR_UNEXPECTED) {
     155             :         // Thread is no longer accepting events. We must have just shut it
     156             :         // down on the main thread. Pretend we never saw it.
     157           0 :         rv = NS_ERROR_NOT_INITIALIZED;
     158             :     }
     159         176 :     return rv;
     160             : }
     161             : 
     162             : NS_IMETHODIMP
     163           0 : nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
     164             : {
     165           0 :     return NS_ERROR_NOT_IMPLEMENTED;
     166             : }
     167             : 
     168             : NS_IMETHODIMP
     169           0 : nsSocketTransportService::IsOnCurrentThread(bool *result)
     170             : {
     171           0 :     nsCOMPtr<nsIThread> thread = GetThreadSafely();
     172           0 :     NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
     173           0 :     return thread->IsOnCurrentThread(result);
     174             : }
     175             : 
     176             : NS_IMETHODIMP_(bool)
     177           3 : nsSocketTransportService::IsOnCurrentThreadInfallible()
     178             : {
     179           6 :     nsCOMPtr<nsIThread> thread = GetThreadSafely();
     180           3 :     NS_ENSURE_TRUE(thread, false);
     181           3 :     return thread->IsOnCurrentThread();
     182             : }
     183             : 
     184             : //-----------------------------------------------------------------------------
     185             : // socket api (socket thread only)
     186             : 
     187             : NS_IMETHODIMP
     188           0 : nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
     189             : {
     190           0 :     SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
     191             : 
     192           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     193             : 
     194           0 :     if (CanAttachSocket()) {
     195           0 :         return Dispatch(event, NS_DISPATCH_NORMAL);
     196             :     }
     197             : 
     198           0 :     auto *runnable = new LinkedRunnableEvent(event);
     199           0 :     mPendingSocketQueue.insertBack(runnable);
     200           0 :     return NS_OK;
     201             : }
     202             : 
     203             : NS_IMETHODIMP
     204           3 : nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
     205             : {
     206           3 :     SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
     207             : 
     208           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     209             : 
     210           3 :     if (!CanAttachSocket()) {
     211           0 :         return NS_ERROR_NOT_AVAILABLE;
     212             :     }
     213             : 
     214             :     SocketContext sock;
     215           3 :     sock.mFD = fd;
     216           3 :     sock.mHandler = handler;
     217           3 :     sock.mElapsedTime = 0;
     218             : 
     219           3 :     nsresult rv = AddToIdleList(&sock);
     220           3 :     if (NS_SUCCEEDED(rv))
     221           3 :         NS_ADDREF(handler);
     222           3 :     return rv;
     223             : }
     224             : 
     225             : // the number of sockets that can be attached at any given time is
     226             : // limited.  this is done because some operating systems (e.g., Win9x)
     227             : // limit the number of sockets that can be created by an application.
     228             : // AttachSocket will fail if the limit is exceeded.  consumers should
     229             : // call CanAttachSocket and check the result before creating a socket.
     230             : 
     231             : bool
     232           6 : nsSocketTransportService::CanAttachSocket()
     233             : {
     234             :     static bool reported900FDLimit = false;
     235             : 
     236           6 :     uint32_t total = mActiveCount + mIdleCount;
     237           6 :     bool rv = total < gMaxCount;
     238             : 
     239           6 :     if (mTelemetryEnabledPref &&
     240           6 :         (((total >= 900) || !rv) && !reported900FDLimit)) {
     241           0 :         reported900FDLimit = true;
     242           0 :         Telemetry::Accumulate(Telemetry::NETWORK_SESSION_AT_900FD, true);
     243             :     }
     244             : 
     245           6 :     return rv;
     246             : }
     247             : 
     248             : nsresult
     249           2 : nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
     250             : {
     251           2 :     SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
     252           2 :     MOZ_ASSERT((listHead == mActiveList) || (listHead == mIdleList),
     253             :                "DetachSocket invalid head");
     254             : 
     255             :     {
     256             : #ifdef MOZ_TASK_TRACER
     257             :         tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO);
     258             : #endif
     259             :         // inform the handler that this socket is going away
     260           2 :         sock->mHandler->OnSocketDetached(sock->mFD);
     261             :     }
     262           2 :     mSentBytesCount += sock->mHandler->ByteCountSent();
     263           2 :     mReceivedBytesCount += sock->mHandler->ByteCountReceived();
     264             : 
     265             :     // cleanup
     266           2 :     sock->mFD = nullptr;
     267           2 :     NS_RELEASE(sock->mHandler);
     268             : 
     269           2 :     if (listHead == mActiveList)
     270           2 :         RemoveFromPollList(sock);
     271             :     else
     272           0 :         RemoveFromIdleList(sock);
     273             : 
     274             :     // NOTE: sock is now an invalid pointer
     275             : 
     276             :     //
     277             :     // notify the first element on the pending socket queue...
     278             :     //
     279           4 :     nsCOMPtr<nsIRunnable> event;
     280           2 :     LinkedRunnableEvent *runnable = mPendingSocketQueue.getFirst();
     281           2 :     if (runnable) {
     282           0 :         event = runnable->TakeEvent();
     283           0 :         runnable->remove();
     284           0 :         delete runnable;
     285             :     }
     286           2 :     if (event) {
     287             :         // move event from pending queue to dispatch queue
     288           0 :         return Dispatch(event, NS_DISPATCH_NORMAL);
     289             :     }
     290           2 :     return NS_OK;
     291             : }
     292             : 
     293             : nsresult
     294           3 : nsSocketTransportService::AddToPollList(SocketContext *sock)
     295             : {
     296           3 :     MOZ_ASSERT(!(static_cast<uint32_t>(sock - mActiveList) < mActiveListSize),
     297             :                "AddToPollList Socket Already Active");
     298             : 
     299           3 :     SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler));
     300           3 :     if (mActiveCount == mActiveListSize) {
     301           0 :         SOCKET_LOG(("  Active List size of %d met\n", mActiveCount));
     302           0 :         if (!GrowActiveList()) {
     303           0 :             NS_ERROR("too many active sockets");
     304           0 :             return NS_ERROR_OUT_OF_MEMORY;
     305             :         }
     306             :     }
     307             : 
     308           3 :     uint32_t newSocketIndex = mActiveCount;
     309           3 :     if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) {
     310           0 :       newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
     311           0 :       PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
     312           0 :               mActiveCount - newSocketIndex);
     313           0 :       PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
     314           0 :               mActiveCount - newSocketIndex);
     315             :     }
     316           3 :     mActiveList[newSocketIndex] = *sock;
     317           3 :     mActiveCount++;
     318             : 
     319           3 :     mPollList[newSocketIndex + 1].fd = sock->mFD;
     320           3 :     mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
     321           3 :     mPollList[newSocketIndex + 1].out_flags = 0;
     322             : 
     323           3 :     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
     324           3 :     return NS_OK;
     325             : }
     326             : 
     327             : void
     328           2 : nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
     329             : {
     330           2 :     SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler));
     331             : 
     332           2 :     uint32_t index = sock - mActiveList;
     333           2 :     MOZ_ASSERT(index < mActiveListSize, "invalid index");
     334             : 
     335           2 :     SOCKET_LOG(("  index=%u mActiveCount=%u\n", index, mActiveCount));
     336             : 
     337           2 :     if (index != mActiveCount-1) {
     338           0 :         mActiveList[index] = mActiveList[mActiveCount-1];
     339           0 :         mPollList[index+1] = mPollList[mActiveCount];
     340             :     }
     341           2 :     mActiveCount--;
     342             : 
     343           2 :     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
     344           2 : }
     345             : 
     346             : nsresult
     347           3 : nsSocketTransportService::AddToIdleList(SocketContext *sock)
     348             : {
     349           3 :     MOZ_ASSERT(!(static_cast<uint32_t>(sock - mIdleList) < mIdleListSize),
     350             :                "AddToIdlelList Socket Already Idle");
     351             : 
     352           3 :     SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler));
     353           3 :     if (mIdleCount == mIdleListSize) {
     354           0 :         SOCKET_LOG(("  Idle List size of %d met\n", mIdleCount));
     355           0 :         if (!GrowIdleList()) {
     356           0 :             NS_ERROR("too many idle sockets");
     357           0 :             return NS_ERROR_OUT_OF_MEMORY;
     358             :         }
     359             :     }
     360             : 
     361           3 :     mIdleList[mIdleCount] = *sock;
     362           3 :     mIdleCount++;
     363             : 
     364           3 :     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
     365           3 :     return NS_OK;
     366             : }
     367             : 
     368             : void
     369           3 : nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
     370             : {
     371           3 :     SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler));
     372             : 
     373           3 :     uint32_t index = sock - mIdleList;
     374           3 :     NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
     375             : 
     376           3 :     if (index != mIdleCount-1)
     377           0 :         mIdleList[index] = mIdleList[mIdleCount-1];
     378           3 :     mIdleCount--;
     379             : 
     380           3 :     SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
     381           3 : }
     382             : 
     383             : void
     384           0 : nsSocketTransportService::MoveToIdleList(SocketContext *sock)
     385             : {
     386           0 :     nsresult rv = AddToIdleList(sock);
     387           0 :     if (NS_FAILED(rv))
     388           0 :         DetachSocket(mActiveList, sock);
     389             :     else
     390           0 :         RemoveFromPollList(sock);
     391           0 : }
     392             : 
     393             : void
     394           3 : nsSocketTransportService::MoveToPollList(SocketContext *sock)
     395             : {
     396           3 :     nsresult rv = AddToPollList(sock);
     397           3 :     if (NS_FAILED(rv))
     398           0 :         DetachSocket(mIdleList, sock);
     399             :     else
     400           3 :         RemoveFromIdleList(sock);
     401           3 : }
     402             : 
     403             : bool
     404           0 : nsSocketTransportService::GrowActiveList()
     405             : {
     406           0 :     int32_t toAdd = gMaxCount - mActiveListSize;
     407           0 :     if (toAdd > 100) {
     408           0 :         toAdd = 100;
     409           0 :     } else if (toAdd < 1) {
     410           0 :         MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
     411             :         return false;
     412             :     }
     413             : 
     414           0 :     mActiveListSize += toAdd;
     415           0 :     mActiveList = (SocketContext *)
     416           0 :         moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
     417           0 :     mPollList = (PRPollDesc *)
     418           0 :         moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
     419           0 :     return true;
     420             : }
     421             : 
     422             : bool
     423           0 : nsSocketTransportService::GrowIdleList()
     424             : {
     425           0 :     int32_t toAdd = gMaxCount - mIdleListSize;
     426           0 :     if (toAdd > 100) {
     427           0 :         toAdd = 100;
     428           0 :     } else if (toAdd < 1) {
     429           0 :         MOZ_ASSERT(false, "CanAttachSocket() should prevent this");
     430             :         return false;
     431             :     }
     432             : 
     433           0 :     mIdleListSize += toAdd;
     434           0 :     mIdleList = (SocketContext *)
     435           0 :         moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
     436           0 :     return true;
     437             : }
     438             : 
     439             : PRIntervalTime
     440          47 : nsSocketTransportService::PollTimeout()
     441             : {
     442          47 :     if (mActiveCount == 0)
     443          33 :         return NS_SOCKET_POLL_TIMEOUT;
     444             : 
     445             :     // compute minimum time before any socket timeout expires.
     446          14 :     uint32_t minR = UINT16_MAX;
     447          31 :     for (uint32_t i=0; i<mActiveCount; ++i) {
     448          17 :         const SocketContext &s = mActiveList[i];
     449             :         // mPollTimeout could be less than mElapsedTime if setTimeout
     450             :         // was called with a value smaller than mElapsedTime.
     451          17 :         uint32_t r = (s.mElapsedTime < s.mHandler->mPollTimeout)
     452          17 :           ? s.mHandler->mPollTimeout - s.mElapsedTime
     453          17 :           : 0;
     454          17 :         if (r < minR)
     455           0 :             minR = r;
     456             :     }
     457             :     // nsASocketHandler defines UINT16_MAX as do not timeout
     458          14 :     if (minR == UINT16_MAX) {
     459          14 :         SOCKET_LOG(("poll timeout: none\n"));
     460          14 :         return NS_SOCKET_POLL_TIMEOUT;
     461             :     }
     462           0 :     SOCKET_LOG(("poll timeout: %" PRIu32 "\n", minR));
     463           0 :     return PR_SecondsToInterval(minR);
     464             : }
     465             : 
     466             : int32_t
     467          59 : nsSocketTransportService::Poll(uint32_t *interval,
     468             :                                TimeDuration *pollDuration)
     469             : {
     470             :     PRPollDesc *pollList;
     471             :     uint32_t pollCount;
     472             :     PRIntervalTime pollTimeout;
     473          59 :     *pollDuration = 0;
     474             : 
     475             :     // If there are pending events for this thread then
     476             :     // DoPollIteration() should service the network without blocking.
     477          59 :     bool pendingEvents = false;
     478          59 :     mRawThread->HasPendingEvents(&pendingEvents);
     479             : 
     480          59 :     if (mPollList[0].fd) {
     481          59 :         mPollList[0].out_flags = 0;
     482          59 :         pollList = mPollList;
     483          59 :         pollCount = mActiveCount + 1;
     484          59 :         pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout();
     485             :     }
     486             :     else {
     487             :         // no pollable event, so busy wait...
     488           0 :         pollCount = mActiveCount;
     489           0 :         if (pollCount)
     490           0 :             pollList = &mPollList[1];
     491             :         else
     492           0 :             pollList = nullptr;
     493           0 :         pollTimeout =
     494           0 :             pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
     495             :     }
     496             : 
     497          59 :     PRIntervalTime ts = PR_IntervalNow();
     498             : 
     499          59 :     TimeStamp pollStart;
     500          59 :     if (mTelemetryEnabledPref) {
     501           0 :         pollStart = TimeStamp::NowLoRes();
     502             :     }
     503             : 
     504          59 :     SOCKET_LOG(("    timeout = %i milliseconds\n",
     505             :          PR_IntervalToMilliseconds(pollTimeout)));
     506          59 :     int32_t rv = PR_Poll(pollList, pollCount, pollTimeout);
     507             : 
     508          56 :     PRIntervalTime passedInterval = PR_IntervalNow() - ts;
     509             : 
     510          56 :     if (mTelemetryEnabledPref && !pollStart.IsNull()) {
     511           0 :         *pollDuration = TimeStamp::NowLoRes() - pollStart;
     512             :     }
     513             : 
     514          56 :     SOCKET_LOG(("    ...returned after %i milliseconds\n",
     515             :          PR_IntervalToMilliseconds(passedInterval)));
     516             : 
     517          56 :     *interval = PR_IntervalToSeconds(passedInterval);
     518          56 :     return rv;
     519             : }
     520             : 
     521             : //-----------------------------------------------------------------------------
     522             : // xpcom api
     523             : 
     524        1050 : NS_IMPL_ISUPPORTS(nsSocketTransportService,
     525             :                   nsISocketTransportService,
     526             :                   nsIRoutedSocketTransportService,
     527             :                   nsIEventTarget,
     528             :                   nsIThreadObserver,
     529             :                   nsIRunnable,
     530             :                   nsPISocketTransportService,
     531             :                   nsIObserver)
     532             : 
     533             : // called from main thread only
     534             : NS_IMETHODIMP
     535           6 : nsSocketTransportService::Init()
     536             : {
     537           6 :     if (!NS_IsMainThread()) {
     538           0 :         NS_ERROR("wrong thread");
     539           0 :         return NS_ERROR_UNEXPECTED;
     540             :     }
     541             : 
     542           6 :     if (mInitialized)
     543           3 :         return NS_OK;
     544             : 
     545           3 :     if (mShuttingDown)
     546           0 :         return NS_ERROR_UNEXPECTED;
     547             : 
     548           6 :     nsCOMPtr<nsIThread> thread;
     549           3 :     nsresult rv = NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), this);
     550           3 :     if (NS_FAILED(rv)) return rv;
     551             : 
     552             :     {
     553           6 :         MutexAutoLock lock(mLock);
     554             :         // Install our mThread, protecting against concurrent readers
     555           3 :         thread.swap(mThread);
     556             :     }
     557             : 
     558           6 :     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
     559           3 :     if (tmpPrefService) {
     560           3 :         tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
     561           3 :         tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
     562           3 :         tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
     563           3 :         tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
     564           3 :         tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
     565           3 :         tmpPrefService->AddObserver(MAX_TIME_BETWEEN_TWO_POLLS, this, false);
     566           3 :         tmpPrefService->AddObserver(TELEMETRY_PREF, this, false);
     567           3 :         tmpPrefService->AddObserver(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, this, false);
     568             :     }
     569           3 :     UpdatePrefs();
     570             : 
     571           6 :     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     572           3 :     if (obsSvc) {
     573           3 :         obsSvc->AddObserver(this, "profile-initial-state", false);
     574           3 :         obsSvc->AddObserver(this, "last-pb-context-exited", false);
     575           3 :         obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
     576           3 :         obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
     577           3 :         obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
     578             :     }
     579             : 
     580           3 :     mInitialized = true;
     581           3 :     return NS_OK;
     582             : }
     583             : 
     584             : // called from main thread only
     585             : NS_IMETHODIMP
     586           0 : nsSocketTransportService::Shutdown(bool aXpcomShutdown)
     587             : {
     588           0 :     SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
     589             : 
     590           0 :     NS_ENSURE_STATE(NS_IsMainThread());
     591             : 
     592           0 :     if (!mInitialized)
     593           0 :         return NS_OK;
     594             : 
     595           0 :     if (mShuttingDown)
     596           0 :         return NS_ERROR_UNEXPECTED;
     597             : 
     598             :     {
     599           0 :         MutexAutoLock lock(mLock);
     600             : 
     601             :         // signal the socket thread to shutdown
     602           0 :         mShuttingDown = true;
     603             : 
     604           0 :         if (mPollableEvent) {
     605           0 :             mPollableEvent->Signal();
     606             :         }
     607             :     }
     608             : 
     609           0 :     if (!aXpcomShutdown) {
     610           0 :         return ShutdownThread();
     611             :     }
     612             : 
     613           0 :     return NS_OK;
     614             : }
     615             : 
     616             : nsresult
     617           0 : nsSocketTransportService::ShutdownThread()
     618             : {
     619           0 :     SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
     620             : 
     621           0 :     NS_ENSURE_STATE(NS_IsMainThread());
     622             : 
     623           0 :     if (!mInitialized || !mShuttingDown)
     624           0 :         return NS_OK;
     625             : 
     626             :     // join with thread
     627           0 :     mThread->Shutdown();
     628             :     {
     629           0 :         MutexAutoLock lock(mLock);
     630             :         // Drop our reference to mThread and make sure that any concurrent
     631             :         // readers are excluded
     632           0 :         mThread = nullptr;
     633             :     }
     634             : 
     635           0 :     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
     636           0 :     if (tmpPrefService)
     637           0 :         tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
     638             : 
     639           0 :     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     640           0 :     if (obsSvc) {
     641           0 :         obsSvc->RemoveObserver(this, "profile-initial-state");
     642           0 :         obsSvc->RemoveObserver(this, "last-pb-context-exited");
     643           0 :         obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
     644           0 :         obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
     645           0 :         obsSvc->RemoveObserver(this, "xpcom-shutdown-threads");
     646             :     }
     647             : 
     648           0 :     if (mAfterWakeUpTimer) {
     649           0 :         mAfterWakeUpTimer->Cancel();
     650           0 :         mAfterWakeUpTimer = nullptr;
     651             :     }
     652             : 
     653           0 :     NetworkActivityMonitor::Shutdown();
     654             : 
     655           0 :     mInitialized = false;
     656           0 :     mShuttingDown = false;
     657             : 
     658           0 :     return NS_OK;
     659             : }
     660             : 
     661             : NS_IMETHODIMP
     662           0 : nsSocketTransportService::GetOffline(bool *offline)
     663             : {
     664           0 :     *offline = mOffline;
     665           0 :     return NS_OK;
     666             : }
     667             : 
     668             : NS_IMETHODIMP
     669           3 : nsSocketTransportService::SetOffline(bool offline)
     670             : {
     671           6 :     MutexAutoLock lock(mLock);
     672           3 :     if (!mOffline && offline) {
     673             :         // signal the socket thread to go offline, so it will detach sockets
     674           0 :         mGoingOffline = true;
     675           0 :         mOffline = true;
     676             :     }
     677           3 :     else if (mOffline && !offline) {
     678           0 :         mOffline = false;
     679             :     }
     680           3 :     if (mPollableEvent) {
     681           3 :         mPollableEvent->Signal();
     682             :     }
     683             : 
     684           6 :     return NS_OK;
     685             : }
     686             : 
     687             : NS_IMETHODIMP
     688           0 : nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
     689             : {
     690           0 :     MOZ_ASSERT(aKeepaliveIdleTimeS);
     691           0 :     if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
     692           0 :         return NS_ERROR_NULL_POINTER;
     693             :     }
     694           0 :     *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
     695           0 :     return NS_OK;
     696             : }
     697             : 
     698             : NS_IMETHODIMP
     699           0 : nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
     700             : {
     701           0 :     MOZ_ASSERT(aKeepaliveRetryIntervalS);
     702           0 :     if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
     703           0 :         return NS_ERROR_NULL_POINTER;
     704             :     }
     705           0 :     *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
     706           0 :     return NS_OK;
     707             : }
     708             : 
     709             : NS_IMETHODIMP
     710           6 : nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
     711             : {
     712           6 :     MOZ_ASSERT(aKeepaliveProbeCount);
     713           6 :     if (NS_WARN_IF(!aKeepaliveProbeCount)) {
     714           0 :         return NS_ERROR_NULL_POINTER;
     715             :     }
     716           6 :     *aKeepaliveProbeCount = mKeepaliveProbeCount;
     717           6 :     return NS_OK;
     718             : }
     719             : 
     720             : NS_IMETHODIMP
     721           0 : nsSocketTransportService::CreateTransport(const char **types,
     722             :                                           uint32_t typeCount,
     723             :                                           const nsACString &host,
     724             :                                           int32_t port,
     725             :                                           nsIProxyInfo *proxyInfo,
     726             :                                           nsISocketTransport **result)
     727             : {
     728           0 :     return CreateRoutedTransport(types, typeCount, host, port, NS_LITERAL_CSTRING(""), 0,
     729           0 :                                  proxyInfo, result);
     730             : }
     731             : 
     732             : NS_IMETHODIMP
     733           3 : nsSocketTransportService::CreateRoutedTransport(const char **types,
     734             :                                                 uint32_t typeCount,
     735             :                                                 const nsACString &host,
     736             :                                                 int32_t port,
     737             :                                                 const nsACString &hostRoute,
     738             :                                                 int32_t portRoute,
     739             :                                                 nsIProxyInfo *proxyInfo,
     740             :                                                 nsISocketTransport **result)
     741             : {
     742             :     // Check FlyWeb table for host mappings.  If one exists, then use that.
     743             :     RefPtr<mozilla::dom::FlyWebService> fws =
     744           6 :         mozilla::dom::FlyWebService::GetExisting();
     745           3 :     if (fws) {
     746           0 :         nsresult rv = fws->CreateTransportForHost(types, typeCount, host, port,
     747             :                                                   hostRoute, portRoute,
     748           0 :                                                   proxyInfo, result);
     749           0 :         NS_ENSURE_SUCCESS(rv, rv);
     750             : 
     751           0 :         if (*result) {
     752           0 :             return NS_OK;
     753             :         }
     754             :     }
     755             : 
     756           3 :     NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
     757           3 :     NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
     758             : 
     759           6 :     RefPtr<nsSocketTransport> trans = new nsSocketTransport();
     760           3 :     nsresult rv = trans->Init(types, typeCount, host, port, hostRoute, portRoute, proxyInfo);
     761           3 :     if (NS_FAILED(rv)) {
     762           0 :         return rv;
     763             :     }
     764             : 
     765           3 :     trans.forget(result);
     766           3 :     return NS_OK;
     767             : }
     768             : 
     769             : NS_IMETHODIMP
     770           0 : nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath,
     771             :                                                     nsISocketTransport **result)
     772             : {
     773             :     nsresult rv;
     774             : 
     775           0 :     NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
     776             : 
     777           0 :     nsAutoCString path;
     778           0 :     rv = aPath->GetNativePath(path);
     779           0 :     if (NS_FAILED(rv))
     780           0 :         return rv;
     781             : 
     782           0 :     RefPtr<nsSocketTransport> trans = new nsSocketTransport();
     783             : 
     784           0 :     rv = trans->InitWithFilename(path.get());
     785           0 :     if (NS_FAILED(rv))
     786           0 :         return rv;
     787             : 
     788           0 :     trans.forget(result);
     789           0 :     return NS_OK;
     790             : }
     791             : 
     792             : NS_IMETHODIMP
     793         107 : nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
     794             : {
     795             : #ifndef XP_WIN
     796             :     // On windows poll can hang and this became worse when we introduced the
     797             :     // patch for bug 698882 (see also bug 1292181), therefore we reverted the
     798             :     // behavior on windows to be as before bug 698882, e.g. write to the socket
     799             :     // also if an event dispatch is on the socket thread and writing to the
     800             :     // socket for each event.
     801         107 :     if (OnSocketThread()) {
     802             :         // this check is redundant to one done inside ::Signal(), but
     803             :         // we can do it here and skip obtaining the lock - given that
     804             :         // this is a relatively common occurance its worth the
     805             :         // redundant code
     806          67 :         SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
     807          67 :         return NS_OK;
     808             :     }
     809             : #else
     810             :     if (gIOService->IsNetTearingDown()) {
     811             :         // Poll can hang sometimes. If we are in shutdown, we are going to
     812             :         // start a watchdog. If we do not exit poll within
     813             :         // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
     814             :         StartPollWatchdog();
     815             :     }
     816             : #endif
     817             : 
     818          80 :     MutexAutoLock lock(mLock);
     819          40 :     if (mPollableEvent) {
     820          40 :         mPollableEvent->Signal();
     821             :     }
     822          40 :     return NS_OK;
     823             : }
     824             : 
     825             : NS_IMETHODIMP
     826         109 : nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
     827             :                                              bool mayWait)
     828             : {
     829         109 :     return NS_OK;
     830             : }
     831             : 
     832             : NS_IMETHODIMP
     833         109 : nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
     834             :                                                 bool eventWasProcessed)
     835             : {
     836         109 :     return NS_OK;
     837             : }
     838             : 
     839             : void
     840          51 : nsSocketTransportService::MarkTheLastElementOfPendingQueue()
     841             : {
     842          51 :     mServingPendingQueue = false;
     843          51 : }
     844             : 
     845             : NS_IMETHODIMP
     846           3 : nsSocketTransportService::Run()
     847             : {
     848           3 :     SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
     849             : 
     850             : #if defined(XP_WIN)
     851             :     // see bug 1361495, gethostname() triggers winsock initialization.
     852             :     // so do it here (on parent and child) to protect against it being done first
     853             :     // accidentally on the main thread.. especially via PR_GetSystemInfo(). This
     854             :     // will also improve latency of first real winsock operation
     855             :     // ..
     856             :     // If STS-thread is no longer needed this should still be run before exiting
     857             : 
     858             :     char ignoredStackBuffer[255];
     859             :     Unused << gethostname(ignoredStackBuffer, 255);
     860             : #endif
     861             : 
     862           3 :     psm::InitializeSSLServerCertVerificationThreads();
     863             : 
     864           3 :     gSocketThread = PR_GetCurrentThread();
     865             : 
     866             :     {
     867           6 :         MutexAutoLock lock(mLock);
     868           3 :         mPollableEvent.reset(new PollableEvent());
     869             :         //
     870             :         // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
     871             :         // or similar software.
     872             :         //
     873             :         // NOTE: per bug 191739, this failure could also be caused by lack
     874             :         // of a loopback device on Windows and OS/2 platforms (it creates
     875             :         // a loopback socket pair on these platforms to implement a pollable
     876             :         // event object).  if we can't create a pollable event, then we'll
     877             :         // have to "busy wait" to implement the socket event queue :-(
     878             :         //
     879           3 :         if (!mPollableEvent->Valid()) {
     880           0 :             mPollableEvent = nullptr;
     881           0 :             NS_WARNING("running socket transport thread without a pollable event");
     882           0 :             SOCKET_LOG(("running socket transport thread without a pollable event"));
     883             :         }
     884             : 
     885           3 :         mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
     886           3 :         mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
     887           3 :         mPollList[0].out_flags = 0;
     888             :     }
     889             : 
     890           3 :     mRawThread = NS_GetCurrentThread();
     891             : 
     892             :     // hook ourselves up to observe event processing for this thread
     893           3 :     nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
     894           3 :     threadInt->SetObserver(this);
     895             : 
     896             :     // make sure the pseudo random number generator is seeded on this thread
     897           3 :     srand(static_cast<unsigned>(PR_Now()));
     898             : 
     899             :     // For the calculation of the duration of the last cycle (i.e. the last for-loop
     900             :     // iteration before shutdown).
     901           3 :     TimeStamp startOfCycleForLastCycleCalc;
     902             :     int numberOfPendingEventsLastCycle;
     903             : 
     904             :     // For measuring of the poll iteration duration without time spent blocked
     905             :     // in poll().
     906           3 :     TimeStamp pollCycleStart;
     907             :     // Time blocked in poll().
     908           3 :     TimeDuration singlePollDuration;
     909             : 
     910             :     // For calculating the time needed for a new element to run.
     911           3 :     TimeStamp startOfIteration;
     912           3 :     TimeStamp startOfNextIteration;
     913             :     int numberOfPendingEvents;
     914             : 
     915             :     // If there is too many pending events queued, we will run some poll()
     916             :     // between them and the following variable is cumulative time spent
     917             :     // blocking in poll().
     918           3 :     TimeDuration pollDuration;
     919             : 
     920             :     for (;;) {
     921          50 :         bool pendingEvents = false;
     922             : 
     923          50 :         numberOfPendingEvents = 0;
     924          50 :         numberOfPendingEventsLastCycle = 0;
     925          50 :         if (mTelemetryEnabledPref) {
     926           0 :             startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
     927           0 :             startOfNextIteration = TimeStamp::NowLoRes();
     928             :         }
     929          50 :         pollDuration = 0;
     930             : 
     931          56 :         do {
     932          59 :             if (mTelemetryEnabledPref) {
     933           0 :                 pollCycleStart = TimeStamp::NowLoRes();
     934             :             }
     935             : 
     936          59 :             DoPollIteration(&singlePollDuration);
     937             : 
     938          56 :             if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) {
     939           0 :                 Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME,
     940           0 :                                       singlePollDuration.ToMilliseconds());
     941           0 :                 Telemetry::AccumulateTimeDelta(
     942             :                     Telemetry::STS_POLL_CYCLE,
     943             :                     pollCycleStart + singlePollDuration,
     944           0 :                     TimeStamp::NowLoRes());
     945           0 :                 pollDuration += singlePollDuration;
     946             :             }
     947             : 
     948          56 :             mRawThread->HasPendingEvents(&pendingEvents);
     949          56 :             if (pendingEvents) {
     950          51 :                 if (!mServingPendingQueue) {
     951         102 :                   nsresult rv = Dispatch(
     952         102 :                     NewRunnableMethod("net::nsSocketTransportService::"
     953             :                                       "MarkTheLastElementOfPendingQueue",
     954             :                                       this,
     955             :                                       &nsSocketTransportService::
     956             :                                         MarkTheLastElementOfPendingQueue),
     957          51 :                     nsIEventTarget::DISPATCH_NORMAL);
     958          51 :                   if (NS_FAILED(rv)) {
     959             :                     NS_WARNING("Could not dispatch a new event on the "
     960           0 :                                "socket thread.");
     961             :                     } else {
     962          51 :                         mServingPendingQueue = true;
     963             :                     }
     964             : 
     965          51 :                     if (mTelemetryEnabledPref) {
     966           0 :                         startOfIteration = startOfNextIteration;
     967             :                         // Everything that comes after this point will
     968             :                         // be served in the next iteration. If no even
     969             :                         // arrives, startOfNextIteration will be reset at the
     970             :                         // beginning of each for-loop.
     971           0 :                         startOfNextIteration = TimeStamp::NowLoRes();
     972             :                     }
     973             :                 }
     974          51 :                 TimeStamp eventQueueStart = TimeStamp::NowLoRes();
     975         269 :                 do {
     976         109 :                     NS_ProcessNextEvent(mRawThread);
     977         109 :                     numberOfPendingEvents++;
     978         109 :                     pendingEvents = false;
     979         109 :                     mRawThread->HasPendingEvents(&pendingEvents);
     980         225 :                 } while (pendingEvents && mServingPendingQueue &&
     981         225 :                          ((TimeStamp::NowLoRes() -
     982         116 :                            eventQueueStart).ToMilliseconds() <
     983          58 :                           mMaxTimePerPollIter));
     984             : 
     985          51 :                 if (mTelemetryEnabledPref && !mServingPendingQueue &&
     986           0 :                     !startOfIteration.IsNull()) {
     987           0 :                     Telemetry::AccumulateTimeDelta(
     988             :                         Telemetry::STS_POLL_AND_EVENTS_CYCLE,
     989             :                         startOfIteration + pollDuration,
     990           0 :                         TimeStamp::NowLoRes());
     991             : 
     992           0 :                     Telemetry::Accumulate(
     993             :                         Telemetry::STS_NUMBER_OF_PENDING_EVENTS,
     994           0 :                         numberOfPendingEvents);
     995             : 
     996           0 :                     numberOfPendingEventsLastCycle += numberOfPendingEvents;
     997           0 :                     numberOfPendingEvents = 0;
     998           0 :                     pollDuration = 0;
     999             :                 }
    1000             :             }
    1001             :         } while (pendingEvents);
    1002             : 
    1003          47 :         bool goingOffline = false;
    1004             :         // now that our event queue is empty, check to see if we should exit
    1005             :         {
    1006          94 :             MutexAutoLock lock(mLock);
    1007          47 :             if (mShuttingDown) {
    1008           0 :                 if (mTelemetryEnabledPref &&
    1009           0 :                     !startOfCycleForLastCycleCalc.IsNull()) {
    1010           0 :                     Telemetry::Accumulate(
    1011             :                         Telemetry::STS_NUMBER_OF_PENDING_EVENTS_IN_THE_LAST_CYCLE,
    1012           0 :                         numberOfPendingEventsLastCycle);
    1013           0 :                     Telemetry::AccumulateTimeDelta(
    1014             :                         Telemetry::STS_POLL_AND_EVENT_THE_LAST_CYCLE,
    1015             :                         startOfCycleForLastCycleCalc,
    1016           0 :                         TimeStamp::NowLoRes());
    1017             :                 }
    1018           0 :                 break;
    1019             :             }
    1020          47 :             if (mGoingOffline) {
    1021           0 :                 mGoingOffline = false;
    1022           0 :                 goingOffline = true;
    1023             :             }
    1024             :         }
    1025             :         // Avoid potential deadlock
    1026          47 :         if (goingOffline)
    1027           0 :             Reset(true);
    1028          47 :     }
    1029             : 
    1030           0 :     SOCKET_LOG(("STS shutting down thread\n"));
    1031             : 
    1032             :     // detach all sockets, including locals
    1033           0 :     Reset(false);
    1034             : 
    1035             :     // Final pass over the event queue. This makes sure that events posted by
    1036             :     // socket detach handlers get processed.
    1037           0 :     NS_ProcessPendingEvents(mRawThread);
    1038             : 
    1039           0 :     gSocketThread = nullptr;
    1040             : 
    1041           0 :     psm::StopSSLServerCertVerificationThreads();
    1042             : 
    1043           0 :     SOCKET_LOG(("STS thread exit\n"));
    1044             : 
    1045           0 :     return NS_OK;
    1046             : }
    1047             : 
    1048             : void
    1049           0 : nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
    1050             :                                                 SocketContext *socketList,
    1051             :                                                 int32_t index)
    1052             : {
    1053           0 :     bool isGuarded = false;
    1054           0 :     if (aGuardLocals) {
    1055           0 :         socketList[index].mHandler->IsLocal(&isGuarded);
    1056           0 :         if (!isGuarded)
    1057           0 :             socketList[index].mHandler->KeepWhenOffline(&isGuarded);
    1058             :     }
    1059           0 :     if (!isGuarded)
    1060           0 :         DetachSocket(socketList, &socketList[index]);
    1061           0 : }
    1062             : 
    1063             : void
    1064           0 : nsSocketTransportService::Reset(bool aGuardLocals)
    1065             : {
    1066             :     // detach any sockets
    1067             :     int32_t i;
    1068           0 :     for (i = mActiveCount - 1; i >= 0; --i) {
    1069           0 :         DetachSocketWithGuard(aGuardLocals, mActiveList, i);
    1070             :     }
    1071           0 :     for (i = mIdleCount - 1; i >= 0; --i) {
    1072           0 :         DetachSocketWithGuard(aGuardLocals, mIdleList, i);
    1073             :     }
    1074           0 : }
    1075             : 
    1076             : nsresult
    1077          59 : nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
    1078             : {
    1079          59 :     SOCKET_LOG(("STS poll iter\n"));
    1080             : 
    1081             :     int32_t i, count;
    1082             :     //
    1083             :     // poll loop
    1084             :     //
    1085             :     // walk active list backwards to see if any sockets should actually be
    1086             :     // idle, then walk the idle list backwards to see if any idle sockets
    1087             :     // should become active.  take care to check only idle sockets that
    1088             :     // were idle to begin with ;-)
    1089             :     //
    1090          59 :     count = mIdleCount;
    1091          77 :     for (i=mActiveCount-1; i>=0; --i) {
    1092             :         //---
    1093          18 :         SOCKET_LOG(("  active [%u] { handler=%p condition=%" PRIx32 " pollflags=%hu }\n", i,
    1094             :             mActiveList[i].mHandler,
    1095             :             static_cast<uint32_t>(mActiveList[i].mHandler->mCondition),
    1096             :             mActiveList[i].mHandler->mPollFlags));
    1097             :         //---
    1098          18 :         if (NS_FAILED(mActiveList[i].mHandler->mCondition))
    1099           2 :             DetachSocket(mActiveList, &mActiveList[i]);
    1100             :         else {
    1101          16 :             uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
    1102          16 :             if (in_flags == 0)
    1103           0 :                 MoveToIdleList(&mActiveList[i]);
    1104             :             else {
    1105             :                 // update poll flags
    1106          16 :                 mPollList[i+1].in_flags = in_flags;
    1107          16 :                 mPollList[i+1].out_flags = 0;
    1108             :             }
    1109             :         }
    1110             :     }
    1111          62 :     for (i=count-1; i>=0; --i) {
    1112             :         //---
    1113           3 :         SOCKET_LOG(("  idle [%u] { handler=%p condition=%" PRIx32 " pollflags=%hu }\n", i,
    1114             :             mIdleList[i].mHandler,
    1115             :             static_cast<uint32_t>(mIdleList[i].mHandler->mCondition),
    1116             :             mIdleList[i].mHandler->mPollFlags));
    1117             :         //---
    1118           3 :         if (NS_FAILED(mIdleList[i].mHandler->mCondition))
    1119           0 :             DetachSocket(mIdleList, &mIdleList[i]);
    1120           3 :         else if (mIdleList[i].mHandler->mPollFlags != 0)
    1121           3 :             MoveToPollList(&mIdleList[i]);
    1122             :     }
    1123             : 
    1124          59 :     SOCKET_LOG(("  calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
    1125             : 
    1126             : #if defined(XP_WIN)
    1127             :     // 30 active connections is the historic limit before firefox 7's 256. A few
    1128             :     //  windows systems have troubles with the higher limit, so actively probe a
    1129             :     // limit the first time we exceed 30.
    1130             :     if ((mActiveCount > 30) && !mProbedMaxCount)
    1131             :         ProbeMaxCount();
    1132             : #endif
    1133             : 
    1134             :     // Measures seconds spent while blocked on PR_Poll
    1135          59 :     uint32_t pollInterval = 0;
    1136          59 :     int32_t n = 0;
    1137          59 :     *pollDuration = 0;
    1138          59 :     if (!gIOService->IsNetTearingDown()) {
    1139             :         // Let's not do polling during shutdown.
    1140             : #if defined(XP_WIN)
    1141             :         StartPolling();
    1142             : #endif
    1143          59 :         n = Poll(&pollInterval, pollDuration);
    1144             : #if defined(XP_WIN)
    1145             :         EndPolling();
    1146             : #endif
    1147             :     }
    1148             : 
    1149          56 :     if (n < 0) {
    1150           0 :         SOCKET_LOG(("  PR_Poll error [%d] os error [%d]\n", PR_GetError(),
    1151             :                     PR_GetOSError()));
    1152             :     }
    1153             :     else {
    1154             :         //
    1155             :         // service "active" sockets...
    1156             :         //
    1157          56 :         uint32_t numberOfOnSocketReadyCalls = 0;
    1158          74 :         for (i=0; i<int32_t(mActiveCount); ++i) {
    1159          18 :             PRPollDesc &desc = mPollList[i+1];
    1160          18 :             SocketContext &s = mActiveList[i];
    1161          18 :             if (n > 0 && desc.out_flags != 0) {
    1162             : #ifdef MOZ_TASK_TRACER
    1163             :                 tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO);
    1164             : #endif
    1165           9 :                 s.mElapsedTime = 0;
    1166           9 :                 s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
    1167           9 :                 numberOfOnSocketReadyCalls++;
    1168             :             }
    1169             :             // check for timeout errors unless disabled...
    1170           9 :             else if (s.mHandler->mPollTimeout != UINT16_MAX) {
    1171             :                 // update elapsed time counter
    1172             :                 // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value
    1173             :                 // here -- otherwise, some compilers will treat it as signed,
    1174             :                 // which makes them fire signed/unsigned-comparison build
    1175             :                 // warnings for the comparison against 'pollInterval'.)
    1176           0 :                 if (MOZ_UNLIKELY(pollInterval >
    1177             :                                 static_cast<uint32_t>(UINT16_MAX) -
    1178             :                                 s.mElapsedTime))
    1179           0 :                     s.mElapsedTime = UINT16_MAX;
    1180             :                 else
    1181           0 :                     s.mElapsedTime += uint16_t(pollInterval);
    1182             :                 // check for timeout expiration
    1183           0 :                 if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
    1184             : #ifdef MOZ_TASK_TRACER
    1185             :                     tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO);
    1186             : #endif
    1187           0 :                     s.mElapsedTime = 0;
    1188           0 :                     s.mHandler->OnSocketReady(desc.fd, -1);
    1189           0 :                     numberOfOnSocketReadyCalls++;
    1190             :                 }
    1191             :             }
    1192             :         }
    1193          56 :         if (mTelemetryEnabledPref) {
    1194             :             Telemetry::Accumulate(
    1195             :                 Telemetry::STS_NUMBER_OF_ONSOCKETREADY_CALLS,
    1196           0 :                 numberOfOnSocketReadyCalls);
    1197             :         }
    1198             : 
    1199             :         //
    1200             :         // check for "dead" sockets and remove them (need to do this in
    1201             :         // reverse order obviously).
    1202             :         //
    1203          74 :         for (i=mActiveCount-1; i>=0; --i) {
    1204          18 :             if (NS_FAILED(mActiveList[i].mHandler->mCondition))
    1205           0 :                 DetachSocket(mActiveList, &mActiveList[i]);
    1206             :         }
    1207             : 
    1208          56 :         if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) {
    1209          80 :             MutexAutoLock lock(mLock);
    1210             : 
    1211             :             // acknowledge pollable event (should not block)
    1212          80 :             if (mPollableEvent &&
    1213          80 :                 ((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
    1214          80 :                  !mPollableEvent->Clear())) {
    1215             :                 // On Windows, the TCP loopback connection in the
    1216             :                 // pollable event may become broken when a laptop
    1217             :                 // switches between wired and wireless networks or
    1218             :                 // wakes up from hibernation.  We try to create a
    1219             :                 // new pollable event.  If that fails, we fall back
    1220             :                 // on "busy wait".
    1221           0 :                 NS_WARNING("Trying to repair mPollableEvent");
    1222           0 :                 mPollableEvent.reset(new PollableEvent());
    1223           0 :                 if (!mPollableEvent->Valid()) {
    1224           0 :                     mPollableEvent = nullptr;
    1225             :                 }
    1226           0 :                 SOCKET_LOG(("running socket transport thread without "
    1227             :                             "a pollable event now valid=%d", !!mPollableEvent));
    1228           0 :                 mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
    1229           0 :                 mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
    1230           0 :                 mPollList[0].out_flags = 0;
    1231             :             }
    1232             :         }
    1233             :     }
    1234             : 
    1235          56 :     return NS_OK;
    1236             : }
    1237             : 
    1238             : void
    1239           3 : nsSocketTransportService::UpdateSendBufferPref(nsIPrefBranch *pref)
    1240             : {
    1241             :     int32_t bufferSize;
    1242             : 
    1243             :     // If the pref is set, honor it. 0 means use OS defaults.
    1244           3 :     nsresult rv = pref->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
    1245           3 :     if (NS_SUCCEEDED(rv)) {
    1246           0 :         mSendBufferSize = bufferSize;
    1247           0 :         return;
    1248             :     }
    1249             : 
    1250             : #if defined(XP_WIN)
    1251             :     mSendBufferSize = 131072 * 4;
    1252             : #endif
    1253             : }
    1254             : 
    1255             : nsresult
    1256           3 : nsSocketTransportService::UpdatePrefs()
    1257             : {
    1258           3 :     mSendBufferSize = 0;
    1259             : 
    1260           6 :     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
    1261           3 :     if (tmpPrefService) {
    1262           3 :         UpdateSendBufferPref(tmpPrefService);
    1263             : 
    1264             :         // Default TCP Keepalive Values.
    1265             :         int32_t keepaliveIdleTimeS;
    1266           3 :         nsresult rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
    1267           3 :                                         &keepaliveIdleTimeS);
    1268           3 :         if (NS_SUCCEEDED(rv))
    1269           3 :             mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
    1270           6 :                                           1, kMaxTCPKeepIdle);
    1271             : 
    1272             :         int32_t keepaliveRetryIntervalS;
    1273           3 :         rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
    1274           3 :                                         &keepaliveRetryIntervalS);
    1275           3 :         if (NS_SUCCEEDED(rv))
    1276           3 :             mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
    1277           6 :                                                1, kMaxTCPKeepIntvl);
    1278             : 
    1279             :         int32_t keepaliveProbeCount;
    1280           3 :         rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
    1281           3 :                                         &keepaliveProbeCount);
    1282           3 :         if (NS_SUCCEEDED(rv))
    1283           3 :             mKeepaliveProbeCount = clamped(keepaliveProbeCount,
    1284           6 :                                            1, kMaxTCPKeepCount);
    1285           3 :         bool keepaliveEnabled = false;
    1286           3 :         rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
    1287           3 :                                          &keepaliveEnabled);
    1288           3 :         if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
    1289           3 :             mKeepaliveEnabledPref = keepaliveEnabled;
    1290           3 :             OnKeepaliveEnabledPrefChange();
    1291             :         }
    1292             : 
    1293             :         int32_t maxTimePref;
    1294           3 :         rv = tmpPrefService->GetIntPref(MAX_TIME_BETWEEN_TWO_POLLS,
    1295           3 :                                         &maxTimePref);
    1296           3 :         if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
    1297           3 :             mMaxTimePerPollIter = maxTimePref;
    1298             :         }
    1299             : 
    1300           3 :         bool telemetryPref = false;
    1301           3 :         rv = tmpPrefService->GetBoolPref(TELEMETRY_PREF,
    1302           3 :                                          &telemetryPref);
    1303           3 :         if (NS_SUCCEEDED(rv)) {
    1304           3 :             mTelemetryEnabledPref = telemetryPref;
    1305             :         }
    1306             : 
    1307             :         int32_t maxTimeForPrClosePref;
    1308           3 :         rv = tmpPrefService->GetIntPref(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
    1309           3 :                                         &maxTimeForPrClosePref);
    1310           3 :         if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >=0) {
    1311           3 :             mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
    1312             :         }
    1313             :     }
    1314             : 
    1315           6 :     return NS_OK;
    1316             : }
    1317             : 
    1318             : void
    1319           6 : nsSocketTransportService::OnKeepaliveEnabledPrefChange()
    1320             : {
    1321             :     // Dispatch to socket thread if we're not executing there.
    1322           6 :     if (!OnSocketThread()) {
    1323           6 :       gSocketTransportService->Dispatch(
    1324           6 :         NewRunnableMethod(
    1325             :           "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange",
    1326             :           this,
    1327             :           &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
    1328           3 :         NS_DISPATCH_NORMAL);
    1329           3 :       return;
    1330             :     }
    1331             : 
    1332           3 :     SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
    1333             :                 mKeepaliveEnabledPref ? "enabled" : "disabled"));
    1334             : 
    1335             :     // Notify each socket that keepalive has been en/disabled globally.
    1336           3 :     for (int32_t i = mActiveCount - 1; i >= 0; --i) {
    1337           0 :         NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
    1338             :     }
    1339           3 :     for (int32_t i = mIdleCount - 1; i >= 0; --i) {
    1340           0 :         NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
    1341             :     }
    1342             : }
    1343             : 
    1344             : void
    1345           0 : nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
    1346             : {
    1347           0 :     MOZ_ASSERT(sock, "SocketContext cannot be null!");
    1348           0 :     MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
    1349             : 
    1350           0 :     if (!sock || !sock->mHandler) {
    1351           0 :         return;
    1352             :     }
    1353             : 
    1354             : #ifdef MOZ_TASK_TRACER
    1355             :     tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO);
    1356             : #endif
    1357           0 :     sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
    1358             : }
    1359             : 
    1360             : NS_IMETHODIMP
    1361           1 : nsSocketTransportService::Observe(nsISupports *subject,
    1362             :                                   const char *topic,
    1363             :                                   const char16_t *data)
    1364             : {
    1365           1 :     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    1366           0 :         UpdatePrefs();
    1367           0 :         return NS_OK;
    1368             :     }
    1369             : 
    1370           1 :     if (!strcmp(topic, "profile-initial-state")) {
    1371           1 :         int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0);
    1372           1 :         if (blipInterval <= 0) {
    1373           1 :             return NS_OK;
    1374             :         }
    1375             : 
    1376           0 :         return net::NetworkActivityMonitor::Init(blipInterval);
    1377             :     }
    1378             : 
    1379           0 :     if (!strcmp(topic, "last-pb-context-exited")) {
    1380           0 :       nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
    1381             :         "net::nsSocketTransportService::ClosePrivateConnections",
    1382             :         this,
    1383           0 :         &nsSocketTransportService::ClosePrivateConnections);
    1384           0 :       nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
    1385           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1386             :     }
    1387             : 
    1388           0 :     if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
    1389           0 :         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
    1390           0 :         if (timer == mAfterWakeUpTimer) {
    1391           0 :             mAfterWakeUpTimer = nullptr;
    1392           0 :             mSleepPhase = false;
    1393             :         }
    1394             : 
    1395             : #if defined(XP_WIN)
    1396             :         if (timer == mPollRepairTimer) {
    1397             :             DoPollRepair();
    1398             :         }
    1399             : #endif
    1400             : 
    1401           0 :     } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
    1402           0 :         mSleepPhase = true;
    1403           0 :         if (mAfterWakeUpTimer) {
    1404           0 :             mAfterWakeUpTimer->Cancel();
    1405           0 :             mAfterWakeUpTimer = nullptr;
    1406             :         }
    1407           0 :     } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
    1408           0 :         if (mSleepPhase && !mAfterWakeUpTimer) {
    1409           0 :             mAfterWakeUpTimer = do_CreateInstance("@mozilla.org/timer;1");
    1410           0 :             if (mAfterWakeUpTimer) {
    1411           0 :                 mAfterWakeUpTimer->Init(this, 2000, nsITimer::TYPE_ONE_SHOT);
    1412             :             }
    1413             :         }
    1414           0 :     } else if (!strcmp(topic, "xpcom-shutdown-threads")) {
    1415           0 :         ShutdownThread();
    1416             :     }
    1417             : 
    1418           0 :     return NS_OK;
    1419             : }
    1420             : 
    1421             : void
    1422           0 : nsSocketTransportService::ClosePrivateConnections()
    1423             : {
    1424             :     // Must be called on the socket thread.
    1425             : #ifdef DEBUG
    1426             :     bool onSTSThread;
    1427           0 :     IsOnCurrentThread(&onSTSThread);
    1428           0 :     MOZ_ASSERT(onSTSThread);
    1429             : #endif
    1430             : 
    1431           0 :     for (int32_t i = mActiveCount - 1; i >= 0; --i) {
    1432           0 :         if (mActiveList[i].mHandler->mIsPrivate) {
    1433           0 :             DetachSocket(mActiveList, &mActiveList[i]);
    1434             :         }
    1435             :     }
    1436           0 :     for (int32_t i = mIdleCount - 1; i >= 0; --i) {
    1437           0 :         if (mIdleList[i].mHandler->mIsPrivate) {
    1438           0 :             DetachSocket(mIdleList, &mIdleList[i]);
    1439             :         }
    1440             :     }
    1441             : 
    1442           0 :     ClearPrivateSSLState();
    1443           0 : }
    1444             : 
    1445             : NS_IMETHODIMP
    1446           3 : nsSocketTransportService::GetSendBufferSize(int32_t *value)
    1447             : {
    1448           3 :     *value = mSendBufferSize;
    1449           3 :     return NS_OK;
    1450             : }
    1451             : 
    1452             : 
    1453             : /// ugly OS specific includes are placed at the bottom of the src for clarity
    1454             : 
    1455             : #if defined(XP_WIN)
    1456             : #include <windows.h>
    1457             : #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
    1458             : #include <sys/resource.h>
    1459             : #endif
    1460             : 
    1461             : // Right now the only need to do this is on windows.
    1462             : #if defined(XP_WIN)
    1463             : void
    1464             : nsSocketTransportService::ProbeMaxCount()
    1465             : {
    1466             :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1467             : 
    1468             :     if (mProbedMaxCount)
    1469             :         return;
    1470             :     mProbedMaxCount = true;
    1471             : 
    1472             :     // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
    1473             :     // sockets. See bug 692260 - windows should be able to handle 1000 sockets
    1474             :     // in select() without a problem, but LSPs have been known to balk at lower
    1475             :     // numbers. (64 in the bug).
    1476             : 
    1477             :     // Allocate
    1478             :     struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
    1479             :     uint32_t numAllocated = 0;
    1480             : 
    1481             :     for (uint32_t index = 0 ; index < gMaxCount; ++index) {
    1482             :         pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
    1483             :         pfd[index].out_flags = 0;
    1484             :         pfd[index].fd =  PR_OpenTCPSocket(PR_AF_INET);
    1485             :         if (!pfd[index].fd) {
    1486             :             SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
    1487             :             if (index < SOCKET_LIMIT_MIN)
    1488             :                 gMaxCount = SOCKET_LIMIT_MIN;
    1489             :             else
    1490             :                 gMaxCount = index;
    1491             :             break;
    1492             :         }
    1493             :         ++numAllocated;
    1494             :     }
    1495             : 
    1496             :     // Test
    1497             :     static_assert(SOCKET_LIMIT_MIN >= 32U, "Minimum Socket Limit is >= 32");
    1498             :     while (gMaxCount <= numAllocated) {
    1499             :         int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
    1500             : 
    1501             :         SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
    1502             :                     gMaxCount, rv));
    1503             : 
    1504             :         if (rv >= 0)
    1505             :             break;
    1506             : 
    1507             :         SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
    1508             :                     gMaxCount, rv, PR_GetError()));
    1509             : 
    1510             :         gMaxCount -= 32;
    1511             :         if (gMaxCount <= SOCKET_LIMIT_MIN) {
    1512             :             gMaxCount = SOCKET_LIMIT_MIN;
    1513             :             break;
    1514             :         }
    1515             :     }
    1516             : 
    1517             :     // Free
    1518             :     for (uint32_t index = 0 ; index < numAllocated; ++index)
    1519             :         if (pfd[index].fd)
    1520             :             PR_Close(pfd[index].fd);
    1521             : 
    1522             :     Telemetry::Accumulate(Telemetry::NETWORK_PROBE_MAXCOUNT, gMaxCount);
    1523             :     SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
    1524             : }
    1525             : #endif // windows
    1526             : 
    1527             : PRStatus
    1528           3 : nsSocketTransportService::DiscoverMaxCount()
    1529             : {
    1530           3 :     gMaxCount = SOCKET_LIMIT_MIN;
    1531             : 
    1532             : #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
    1533             :     // On unix and os x network sockets and file
    1534             :     // descriptors are the same. OS X comes defaulted at 256,
    1535             :     // most linux at 1000. We can reliably use [sg]rlimit to
    1536             :     // query that and raise it if needed.
    1537             : 
    1538             :     struct rlimit rlimitData;
    1539           3 :     if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) // rlimit broken - use min
    1540           0 :         return PR_SUCCESS;
    1541             : 
    1542           3 :     if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
    1543           3 :         gMaxCount = SOCKET_LIMIT_TARGET;
    1544           3 :         return PR_SUCCESS;
    1545             :     }
    1546             : 
    1547           0 :     int32_t maxallowed = rlimitData.rlim_max;
    1548           0 :     if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
    1549           0 :         return PR_SUCCESS; // so small treat as if rlimit is broken
    1550             :     }
    1551             : 
    1552           0 :     if ((maxallowed == -1) || // no hard cap - ok to set target
    1553           0 :         ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
    1554           0 :         maxallowed = SOCKET_LIMIT_TARGET;
    1555             :     }
    1556             : 
    1557           0 :     rlimitData.rlim_cur = maxallowed;
    1558           0 :     setrlimit(RLIMIT_NOFILE, &rlimitData);
    1559           0 :     if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
    1560           0 :         (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
    1561           0 :         gMaxCount = rlimitData.rlim_cur;
    1562             :     }
    1563             : 
    1564             : #elif defined(XP_WIN) && !defined(WIN_CE)
    1565             :     // >= XP is confirmed to have at least 1000
    1566             :     static_assert(SOCKET_LIMIT_TARGET <= 1000, "SOCKET_LIMIT_TARGET max value is 1000");
    1567             :     gMaxCount = SOCKET_LIMIT_TARGET;
    1568             : #else
    1569             :     // other platforms are harder to test - so leave at safe legacy value
    1570             : #endif
    1571             : 
    1572           0 :     return PR_SUCCESS;
    1573             : }
    1574             : 
    1575             : 
    1576             : // Used to return connection info to Dashboard.cpp
    1577             : void
    1578           0 : nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
    1579             :         struct SocketContext *context, bool aActive)
    1580             : {
    1581           0 :     if (context->mHandler->mIsPrivate)
    1582           0 :         return;
    1583           0 :     PRFileDesc *aFD = context->mFD;
    1584             : 
    1585           0 :     PRFileDesc *idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER);
    1586             : 
    1587           0 :     NS_ENSURE_TRUE_VOID(idLayer);
    1588             : 
    1589           0 :     bool tcp = PR_GetDescType(idLayer) == PR_DESC_SOCKET_TCP;
    1590             : 
    1591             :     PRNetAddr peer_addr;
    1592           0 :     PodZero(&peer_addr);
    1593           0 :     PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
    1594           0 :     if (rv != PR_SUCCESS)
    1595           0 :        return;
    1596             : 
    1597           0 :     char host[64] = {0};
    1598           0 :     rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
    1599           0 :     if (rv != PR_SUCCESS)
    1600           0 :        return;
    1601             : 
    1602             :     uint16_t port;
    1603           0 :     if (peer_addr.raw.family == PR_AF_INET)
    1604           0 :         port = peer_addr.inet.port;
    1605             :     else
    1606           0 :         port = peer_addr.ipv6.port;
    1607           0 :     port = PR_ntohs(port);
    1608           0 :     uint64_t sent = context->mHandler->ByteCountSent();
    1609           0 :     uint64_t received = context->mHandler->ByteCountReceived();
    1610           0 :     SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
    1611             : 
    1612           0 :     data->AppendElement(info);
    1613             : }
    1614             : 
    1615             : void
    1616           0 : nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
    1617             : {
    1618           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1619           0 :     for (uint32_t i = 0; i < mActiveCount; i++)
    1620           0 :         AnalyzeConnection(data, &mActiveList[i], true);
    1621           0 :     for (uint32_t i = 0; i < mIdleCount; i++)
    1622           0 :         AnalyzeConnection(data, &mIdleList[i], false);
    1623           0 : }
    1624             : 
    1625             : #if defined(XP_WIN)
    1626             : void
    1627             : nsSocketTransportService::StartPollWatchdog()
    1628             : {
    1629             :     // Start off the timer from a runnable off of the main thread in order to
    1630             :     // avoid a deadlock, see bug 1370448.
    1631             :     RefPtr<nsSocketTransportService> self(this);
    1632             :     NS_DispatchToMainThread(NS_NewRunnableFunction("nsSocketTransportService::StartPollWatchdog",
    1633             :                                                    [self] {
    1634             :          MutexAutoLock lock(self->mLock);
    1635             : 
    1636             :          // Poll can hang sometimes. If we are in shutdown, we are going to start a
    1637             :          // watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
    1638             :          // signal a pollable event again.
    1639             :          MOZ_ASSERT(gIOService->IsNetTearingDown());
    1640             :          if (self->mPolling && !self->mPollRepairTimer) {
    1641             :              self->mPollRepairTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    1642             :              self->mPollRepairTimer->Init(self, REPAIR_POLLABLE_EVENT_TIME,
    1643             :                                           nsITimer::TYPE_REPEATING_SLACK);
    1644             :          }
    1645             :     }));
    1646             : }
    1647             : 
    1648             : void
    1649             : nsSocketTransportService::DoPollRepair()
    1650             : {
    1651             :     MutexAutoLock lock(mLock);
    1652             :     if (mPolling && mPollableEvent) {
    1653             :         mPollableEvent->Signal();
    1654             :     } else if (mPollRepairTimer) {
    1655             :         mPollRepairTimer->Cancel();
    1656             :     }
    1657             : }
    1658             : 
    1659             : void
    1660             : nsSocketTransportService::StartPolling()
    1661             : {
    1662             :     MutexAutoLock lock(mLock);
    1663             :     mPolling = true;
    1664             : }
    1665             : 
    1666             : void
    1667             : nsSocketTransportService::EndPolling()
    1668             : {
    1669             :     MutexAutoLock lock(mLock);
    1670             :     mPolling = false;
    1671             :     if (mPollRepairTimer) {
    1672             :         mPollRepairTimer->Cancel();
    1673             :     }
    1674             : }
    1675             : #endif
    1676             : 
    1677             : } // namespace net
    1678             : } // namespace mozilla

Generated by: LCOV version 1.13