netwerk/base/src/nsSocketTransportService2.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsSocketTransportService2.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1250 @@
     1.4 +// vim:set sw=4 sts=4 et cin:
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifdef MOZ_LOGGING
    1.10 +#define FORCE_PR_LOG
    1.11 +#endif
    1.12 +
    1.13 +#include "nsSocketTransportService2.h"
    1.14 +#include "nsSocketTransport2.h"
    1.15 +#include "nsError.h"
    1.16 +#include "prnetdb.h"
    1.17 +#include "prerror.h"
    1.18 +#include "nsIPrefService.h"
    1.19 +#include "nsIPrefBranch.h"
    1.20 +#include "nsServiceManagerUtils.h"
    1.21 +#include "NetworkActivityMonitor.h"
    1.22 +#include "nsIObserverService.h"
    1.23 +#include "mozilla/Services.h"
    1.24 +#include "mozilla/Preferences.h"
    1.25 +#include "mozilla/Likely.h"
    1.26 +#include "mozilla/PublicSSL.h"
    1.27 +#include "mozilla/ChaosMode.h"
    1.28 +#include "mozilla/PodOperations.h"
    1.29 +#include "nsThreadUtils.h"
    1.30 +#include "nsIFile.h"
    1.31 +
    1.32 +using namespace mozilla;
    1.33 +using namespace mozilla::net;
    1.34 +
    1.35 +#if defined(PR_LOGGING)
    1.36 +PRLogModuleInfo *gSocketTransportLog = nullptr;
    1.37 +#endif
    1.38 +
    1.39 +nsSocketTransportService *gSocketTransportService = nullptr;
    1.40 +PRThread                 *gSocketThread           = nullptr;
    1.41 +
    1.42 +#define SEND_BUFFER_PREF "network.tcp.sendbuffer"
    1.43 +#define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
    1.44 +#define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
    1.45 +#define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
    1.46 +#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
    1.47 +#define SOCKET_LIMIT_TARGET 550U
    1.48 +#define SOCKET_LIMIT_MIN     50U
    1.49 +#define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
    1.50 +
    1.51 +uint32_t nsSocketTransportService::gMaxCount;
    1.52 +PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
    1.53 +
    1.54 +//-----------------------------------------------------------------------------
    1.55 +// ctor/dtor (called on the main/UI thread by the service manager)
    1.56 +
    1.57 +nsSocketTransportService::nsSocketTransportService()
    1.58 +    : mThread(nullptr)
    1.59 +    , mThreadEvent(nullptr)
    1.60 +    , mAutodialEnabled(false)
    1.61 +    , mLock("nsSocketTransportService::mLock")
    1.62 +    , mInitialized(false)
    1.63 +    , mShuttingDown(false)
    1.64 +    , mOffline(false)
    1.65 +    , mGoingOffline(false)
    1.66 +    , mActiveListSize(SOCKET_LIMIT_MIN)
    1.67 +    , mIdleListSize(SOCKET_LIMIT_MIN)
    1.68 +    , mActiveCount(0)
    1.69 +    , mIdleCount(0)
    1.70 +    , mSentBytesCount(0)
    1.71 +    , mReceivedBytesCount(0)
    1.72 +    , mSendBufferSize(0)
    1.73 +    , mKeepaliveIdleTimeS(600)
    1.74 +    , mKeepaliveRetryIntervalS(1)
    1.75 +    , mKeepaliveProbeCount(kDefaultTCPKeepCount)
    1.76 +    , mKeepaliveEnabledPref(false)
    1.77 +    , mProbedMaxCount(false)
    1.78 +{
    1.79 +#if defined(PR_LOGGING)
    1.80 +    gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
    1.81 +#endif
    1.82 +
    1.83 +    NS_ASSERTION(NS_IsMainThread(), "wrong thread");
    1.84 +
    1.85 +    PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
    1.86 +    mActiveList = (SocketContext *)
    1.87 +        moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
    1.88 +    mIdleList = (SocketContext *)
    1.89 +        moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
    1.90 +    mPollList = (PRPollDesc *)
    1.91 +        moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
    1.92 +
    1.93 +    NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
    1.94 +    gSocketTransportService = this;
    1.95 +}
    1.96 +
    1.97 +nsSocketTransportService::~nsSocketTransportService()
    1.98 +{
    1.99 +    NS_ASSERTION(NS_IsMainThread(), "wrong thread");
   1.100 +    NS_ASSERTION(!mInitialized, "not shutdown properly");
   1.101 +    
   1.102 +    if (mThreadEvent)
   1.103 +        PR_DestroyPollableEvent(mThreadEvent);
   1.104 +
   1.105 +    moz_free(mActiveList);
   1.106 +    moz_free(mIdleList);
   1.107 +    moz_free(mPollList);
   1.108 +    gSocketTransportService = nullptr;
   1.109 +}
   1.110 +
   1.111 +//-----------------------------------------------------------------------------
   1.112 +// event queue (any thread)
   1.113 +
   1.114 +already_AddRefed<nsIThread>
   1.115 +nsSocketTransportService::GetThreadSafely()
   1.116 +{
   1.117 +    MutexAutoLock lock(mLock);
   1.118 +    nsCOMPtr<nsIThread> result = mThread;
   1.119 +    return result.forget();
   1.120 +}
   1.121 +
   1.122 +NS_IMETHODIMP
   1.123 +nsSocketTransportService::Dispatch(nsIRunnable *event, uint32_t flags)
   1.124 +{
   1.125 +    SOCKET_LOG(("STS dispatch [%p]\n", event));
   1.126 +
   1.127 +    nsCOMPtr<nsIThread> thread = GetThreadSafely();
   1.128 +    nsresult rv;
   1.129 +    rv = thread ? thread->Dispatch(event, flags) : NS_ERROR_NOT_INITIALIZED;
   1.130 +    if (rv == NS_ERROR_UNEXPECTED) {
   1.131 +        // Thread is no longer accepting events. We must have just shut it
   1.132 +        // down on the main thread. Pretend we never saw it.
   1.133 +        rv = NS_ERROR_NOT_INITIALIZED;
   1.134 +    }
   1.135 +    return rv;
   1.136 +}
   1.137 +
   1.138 +NS_IMETHODIMP
   1.139 +nsSocketTransportService::IsOnCurrentThread(bool *result)
   1.140 +{
   1.141 +    nsCOMPtr<nsIThread> thread = GetThreadSafely();
   1.142 +    NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
   1.143 +    return thread->IsOnCurrentThread(result);
   1.144 +}
   1.145 +
   1.146 +//-----------------------------------------------------------------------------
   1.147 +// socket api (socket thread only)
   1.148 +
   1.149 +NS_IMETHODIMP
   1.150 +nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
   1.151 +{
   1.152 +    SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
   1.153 +
   1.154 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   1.155 +
   1.156 +    if (CanAttachSocket()) {
   1.157 +        return Dispatch(event, NS_DISPATCH_NORMAL);
   1.158 +    }
   1.159 +
   1.160 +    mPendingSocketQ.PutEvent(event);
   1.161 +    return NS_OK;
   1.162 +}
   1.163 +
   1.164 +NS_IMETHODIMP
   1.165 +nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
   1.166 +{
   1.167 +    SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%p]\n", handler));
   1.168 +
   1.169 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
   1.170 +
   1.171 +    if (!CanAttachSocket()) {
   1.172 +        return NS_ERROR_NOT_AVAILABLE;
   1.173 +    }
   1.174 +
   1.175 +    SocketContext sock;
   1.176 +    sock.mFD = fd;
   1.177 +    sock.mHandler = handler;
   1.178 +    sock.mElapsedTime = 0;
   1.179 +
   1.180 +    nsresult rv = AddToIdleList(&sock);
   1.181 +    if (NS_SUCCEEDED(rv))
   1.182 +        NS_ADDREF(handler);
   1.183 +    return rv;
   1.184 +}
   1.185 +
   1.186 +nsresult
   1.187 +nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
   1.188 +{
   1.189 +    SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", sock->mHandler));
   1.190 +    NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList),
   1.191 +                      "DetachSocket invalid head");
   1.192 +
   1.193 +    // inform the handler that this socket is going away
   1.194 +    sock->mHandler->OnSocketDetached(sock->mFD);
   1.195 +    mSentBytesCount += sock->mHandler->ByteCountSent();
   1.196 +    mReceivedBytesCount += sock->mHandler->ByteCountReceived();
   1.197 +
   1.198 +    // cleanup
   1.199 +    sock->mFD = nullptr;
   1.200 +    NS_RELEASE(sock->mHandler);
   1.201 +
   1.202 +    if (listHead == mActiveList)
   1.203 +        RemoveFromPollList(sock);
   1.204 +    else
   1.205 +        RemoveFromIdleList(sock);
   1.206 +
   1.207 +    // NOTE: sock is now an invalid pointer
   1.208 +    
   1.209 +    //
   1.210 +    // notify the first element on the pending socket queue...
   1.211 +    //
   1.212 +    nsCOMPtr<nsIRunnable> event;
   1.213 +    if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
   1.214 +        // move event from pending queue to dispatch queue
   1.215 +        return Dispatch(event, NS_DISPATCH_NORMAL);
   1.216 +    }
   1.217 +    return NS_OK;
   1.218 +}
   1.219 +
   1.220 +nsresult
   1.221 +nsSocketTransportService::AddToPollList(SocketContext *sock)
   1.222 +{
   1.223 +    NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mActiveList)) < mActiveListSize),
   1.224 +                      "AddToPollList Socket Already Active");
   1.225 +
   1.226 +    SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%p]\n", sock->mHandler));
   1.227 +    if (mActiveCount == mActiveListSize) {
   1.228 +        SOCKET_LOG(("  Active List size of %d met\n", mActiveCount));
   1.229 +        if (!GrowActiveList()) {
   1.230 +            NS_ERROR("too many active sockets");
   1.231 +            return NS_ERROR_OUT_OF_MEMORY;
   1.232 +        }
   1.233 +    }
   1.234 +    
   1.235 +    uint32_t newSocketIndex = mActiveCount;
   1.236 +    if (ChaosMode::isActive()) {
   1.237 +      newSocketIndex = ChaosMode::randomUint32LessThan(mActiveCount + 1);
   1.238 +      PodMove(mActiveList + newSocketIndex + 1, mActiveList + newSocketIndex,
   1.239 +              mActiveCount - newSocketIndex);
   1.240 +      PodMove(mPollList + newSocketIndex + 2, mPollList + newSocketIndex + 1,
   1.241 +              mActiveCount - newSocketIndex);
   1.242 +    }
   1.243 +    mActiveList[newSocketIndex] = *sock;
   1.244 +    mActiveCount++;
   1.245 +
   1.246 +    mPollList[newSocketIndex + 1].fd = sock->mFD;
   1.247 +    mPollList[newSocketIndex + 1].in_flags = sock->mHandler->mPollFlags;
   1.248 +    mPollList[newSocketIndex + 1].out_flags = 0;
   1.249 +
   1.250 +    SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   1.251 +    return NS_OK;
   1.252 +}
   1.253 +
   1.254 +void
   1.255 +nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
   1.256 +{
   1.257 +    SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%p]\n", sock->mHandler));
   1.258 +
   1.259 +    uint32_t index = sock - mActiveList;
   1.260 +    NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index");
   1.261 +
   1.262 +    SOCKET_LOG(("  index=%u mActiveCount=%u\n", index, mActiveCount));
   1.263 +
   1.264 +    if (index != mActiveCount-1) {
   1.265 +        mActiveList[index] = mActiveList[mActiveCount-1];
   1.266 +        mPollList[index+1] = mPollList[mActiveCount];
   1.267 +    }
   1.268 +    mActiveCount--;
   1.269 +
   1.270 +    SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   1.271 +}
   1.272 +
   1.273 +nsresult
   1.274 +nsSocketTransportService::AddToIdleList(SocketContext *sock)
   1.275 +{
   1.276 +    NS_ABORT_IF_FALSE(!(((uint32_t)(sock - mIdleList)) < mIdleListSize),
   1.277 +                      "AddToIdlelList Socket Already Idle");
   1.278 +
   1.279 +    SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%p]\n", sock->mHandler));
   1.280 +    if (mIdleCount == mIdleListSize) {
   1.281 +        SOCKET_LOG(("  Idle List size of %d met\n", mIdleCount));
   1.282 +        if (!GrowIdleList()) {
   1.283 +            NS_ERROR("too many idle sockets");
   1.284 +            return NS_ERROR_OUT_OF_MEMORY;
   1.285 +        }
   1.286 +    }
   1.287 +
   1.288 +    mIdleList[mIdleCount] = *sock;
   1.289 +    mIdleCount++;
   1.290 +
   1.291 +    SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   1.292 +    return NS_OK;
   1.293 +}
   1.294 +
   1.295 +void
   1.296 +nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
   1.297 +{
   1.298 +    SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", sock->mHandler));
   1.299 +
   1.300 +    uint32_t index = sock - mIdleList;
   1.301 +    NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
   1.302 +
   1.303 +    if (index != mIdleCount-1)
   1.304 +        mIdleList[index] = mIdleList[mIdleCount-1];
   1.305 +    mIdleCount--;
   1.306 +
   1.307 +    SOCKET_LOG(("  active=%u idle=%u\n", mActiveCount, mIdleCount));
   1.308 +}
   1.309 +
   1.310 +void
   1.311 +nsSocketTransportService::MoveToIdleList(SocketContext *sock)
   1.312 +{
   1.313 +    nsresult rv = AddToIdleList(sock);
   1.314 +    if (NS_FAILED(rv))
   1.315 +        DetachSocket(mActiveList, sock);
   1.316 +    else
   1.317 +        RemoveFromPollList(sock);
   1.318 +}
   1.319 +
   1.320 +void
   1.321 +nsSocketTransportService::MoveToPollList(SocketContext *sock)
   1.322 +{
   1.323 +    nsresult rv = AddToPollList(sock);
   1.324 +    if (NS_FAILED(rv))
   1.325 +        DetachSocket(mIdleList, sock);
   1.326 +    else
   1.327 +        RemoveFromIdleList(sock);
   1.328 +}
   1.329 +
   1.330 +bool
   1.331 +nsSocketTransportService::GrowActiveList()
   1.332 +{
   1.333 +    int32_t toAdd = gMaxCount - mActiveListSize;
   1.334 +    if (toAdd > 100)
   1.335 +        toAdd = 100;
   1.336 +    if (toAdd < 1)
   1.337 +        return false;
   1.338 +    
   1.339 +    mActiveListSize += toAdd;
   1.340 +    mActiveList = (SocketContext *)
   1.341 +        moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
   1.342 +    mPollList = (PRPollDesc *)
   1.343 +        moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
   1.344 +    return true;
   1.345 +}
   1.346 +
   1.347 +bool
   1.348 +nsSocketTransportService::GrowIdleList()
   1.349 +{
   1.350 +    int32_t toAdd = gMaxCount - mIdleListSize;
   1.351 +    if (toAdd > 100)
   1.352 +        toAdd = 100;
   1.353 +    if (toAdd < 1)
   1.354 +        return false;
   1.355 +
   1.356 +    mIdleListSize += toAdd;
   1.357 +    mIdleList = (SocketContext *)
   1.358 +        moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
   1.359 +    return true;
   1.360 +}
   1.361 +
   1.362 +PRIntervalTime
   1.363 +nsSocketTransportService::PollTimeout()
   1.364 +{
   1.365 +    if (mActiveCount == 0)
   1.366 +        return NS_SOCKET_POLL_TIMEOUT;
   1.367 +
   1.368 +    // compute minimum time before any socket timeout expires.
   1.369 +    uint32_t minR = UINT16_MAX;
   1.370 +    for (uint32_t i=0; i<mActiveCount; ++i) {
   1.371 +        const SocketContext &s = mActiveList[i];
   1.372 +        // mPollTimeout could be less than mElapsedTime if setTimeout
   1.373 +        // was called with a value smaller than mElapsedTime.
   1.374 +        uint32_t r = (s.mElapsedTime < s.mHandler->mPollTimeout)
   1.375 +          ? s.mHandler->mPollTimeout - s.mElapsedTime
   1.376 +          : 0;
   1.377 +        if (r < minR)
   1.378 +            minR = r;
   1.379 +    }
   1.380 +    // nsASocketHandler defines UINT16_MAX as do not timeout
   1.381 +    if (minR == UINT16_MAX) {
   1.382 +        SOCKET_LOG(("poll timeout: none\n"));
   1.383 +        return NS_SOCKET_POLL_TIMEOUT;
   1.384 +    }
   1.385 +    SOCKET_LOG(("poll timeout: %lu\n", minR));
   1.386 +    return PR_SecondsToInterval(minR);
   1.387 +}
   1.388 +
   1.389 +int32_t
   1.390 +nsSocketTransportService::Poll(bool wait, uint32_t *interval)
   1.391 +{
   1.392 +    PRPollDesc *pollList;
   1.393 +    uint32_t pollCount;
   1.394 +    PRIntervalTime pollTimeout;
   1.395 +
   1.396 +    if (mPollList[0].fd) {
   1.397 +        mPollList[0].out_flags = 0;
   1.398 +        pollList = mPollList;
   1.399 +        pollCount = mActiveCount + 1;
   1.400 +        pollTimeout = PollTimeout();
   1.401 +    }
   1.402 +    else {
   1.403 +        // no pollable event, so busy wait...
   1.404 +        pollCount = mActiveCount;
   1.405 +        if (pollCount)
   1.406 +            pollList = &mPollList[1];
   1.407 +        else
   1.408 +            pollList = nullptr;
   1.409 +        pollTimeout = PR_MillisecondsToInterval(25);
   1.410 +    }
   1.411 +
   1.412 +    if (!wait)
   1.413 +        pollTimeout = PR_INTERVAL_NO_WAIT;
   1.414 +
   1.415 +    PRIntervalTime ts = PR_IntervalNow();
   1.416 +
   1.417 +    SOCKET_LOG(("    timeout = %i milliseconds\n",
   1.418 +         PR_IntervalToMilliseconds(pollTimeout)));
   1.419 +    int32_t rv = PR_Poll(pollList, pollCount, pollTimeout);
   1.420 +
   1.421 +    PRIntervalTime passedInterval = PR_IntervalNow() - ts;
   1.422 +
   1.423 +    SOCKET_LOG(("    ...returned after %i milliseconds\n",
   1.424 +         PR_IntervalToMilliseconds(passedInterval))); 
   1.425 +
   1.426 +    *interval = PR_IntervalToSeconds(passedInterval);
   1.427 +    return rv;
   1.428 +}
   1.429 +
   1.430 +//-----------------------------------------------------------------------------
   1.431 +// xpcom api
   1.432 +
   1.433 +NS_IMPL_ISUPPORTS(nsSocketTransportService,
   1.434 +                  nsISocketTransportService,
   1.435 +                  nsIEventTarget,
   1.436 +                  nsIThreadObserver,
   1.437 +                  nsIRunnable,
   1.438 +                  nsPISocketTransportService,
   1.439 +                  nsIObserver)
   1.440 +
   1.441 +// called from main thread only
   1.442 +NS_IMETHODIMP
   1.443 +nsSocketTransportService::Init()
   1.444 +{
   1.445 +    if (!NS_IsMainThread()) {
   1.446 +        NS_ERROR("wrong thread");
   1.447 +        return NS_ERROR_UNEXPECTED;
   1.448 +    }
   1.449 +
   1.450 +    if (mInitialized)
   1.451 +        return NS_OK;
   1.452 +
   1.453 +    if (mShuttingDown)
   1.454 +        return NS_ERROR_UNEXPECTED;
   1.455 +
   1.456 +    if (!mThreadEvent) {
   1.457 +        mThreadEvent = PR_NewPollableEvent();
   1.458 +        //
   1.459 +        // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
   1.460 +        // or similar software.
   1.461 +        //
   1.462 +        // NOTE: per bug 191739, this failure could also be caused by lack
   1.463 +        // of a loopback device on Windows and OS/2 platforms (NSPR creates
   1.464 +        // a loopback socket pair on these platforms to implement a pollable
   1.465 +        // event object).  if we can't create a pollable event, then we'll
   1.466 +        // have to "busy wait" to implement the socket event queue :-(
   1.467 +        //
   1.468 +        if (!mThreadEvent) {
   1.469 +            NS_WARNING("running socket transport thread without a pollable event");
   1.470 +            SOCKET_LOG(("running socket transport thread without a pollable event"));
   1.471 +        }
   1.472 +    }
   1.473 +
   1.474 +    nsCOMPtr<nsIThread> thread;
   1.475 +    nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
   1.476 +    if (NS_FAILED(rv)) return rv;
   1.477 +    
   1.478 +    {
   1.479 +        MutexAutoLock lock(mLock);
   1.480 +        // Install our mThread, protecting against concurrent readers
   1.481 +        thread.swap(mThread);
   1.482 +    }
   1.483 +
   1.484 +    nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.485 +    if (tmpPrefService) {
   1.486 +        tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
   1.487 +        tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
   1.488 +        tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
   1.489 +        tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
   1.490 +        tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
   1.491 +    }
   1.492 +    UpdatePrefs();
   1.493 +
   1.494 +    nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   1.495 +    if (obsSvc) {
   1.496 +        obsSvc->AddObserver(this, "profile-initial-state", false);
   1.497 +        obsSvc->AddObserver(this, "last-pb-context-exited", false);
   1.498 +    }
   1.499 +
   1.500 +    mInitialized = true;
   1.501 +    return NS_OK;
   1.502 +}
   1.503 +
   1.504 +// called from main thread only
   1.505 +NS_IMETHODIMP
   1.506 +nsSocketTransportService::Shutdown()
   1.507 +{
   1.508 +    SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
   1.509 +
   1.510 +    NS_ENSURE_STATE(NS_IsMainThread());
   1.511 +
   1.512 +    if (!mInitialized)
   1.513 +        return NS_OK;
   1.514 +
   1.515 +    if (mShuttingDown)
   1.516 +        return NS_ERROR_UNEXPECTED;
   1.517 +
   1.518 +    {
   1.519 +        MutexAutoLock lock(mLock);
   1.520 +
   1.521 +        // signal the socket thread to shutdown
   1.522 +        mShuttingDown = true;
   1.523 +
   1.524 +        if (mThreadEvent)
   1.525 +            PR_SetPollableEvent(mThreadEvent);
   1.526 +        // else wait for Poll timeout
   1.527 +    }
   1.528 +
   1.529 +    // join with thread
   1.530 +    mThread->Shutdown();
   1.531 +    {
   1.532 +        MutexAutoLock lock(mLock);
   1.533 +        // Drop our reference to mThread and make sure that any concurrent
   1.534 +        // readers are excluded
   1.535 +        mThread = nullptr;
   1.536 +    }
   1.537 +
   1.538 +    nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.539 +    if (tmpPrefService) 
   1.540 +        tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
   1.541 +
   1.542 +    nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   1.543 +    if (obsSvc) {
   1.544 +        obsSvc->RemoveObserver(this, "profile-initial-state");
   1.545 +        obsSvc->RemoveObserver(this, "last-pb-context-exited");
   1.546 +    }
   1.547 +
   1.548 +    mozilla::net::NetworkActivityMonitor::Shutdown();
   1.549 +
   1.550 +    mInitialized = false;
   1.551 +    mShuttingDown = false;
   1.552 +
   1.553 +    return NS_OK;
   1.554 +}
   1.555 +
   1.556 +NS_IMETHODIMP
   1.557 +nsSocketTransportService::GetOffline(bool *offline)
   1.558 +{
   1.559 +    *offline = mOffline;
   1.560 +    return NS_OK;
   1.561 +}
   1.562 +
   1.563 +NS_IMETHODIMP
   1.564 +nsSocketTransportService::SetOffline(bool offline)
   1.565 +{
   1.566 +    MutexAutoLock lock(mLock);
   1.567 +    if (!mOffline && offline) {
   1.568 +        // signal the socket thread to go offline, so it will detach sockets
   1.569 +        mGoingOffline = true;
   1.570 +        mOffline = true;
   1.571 +    }
   1.572 +    else if (mOffline && !offline) {
   1.573 +        mOffline = false;
   1.574 +    }
   1.575 +    if (mThreadEvent)
   1.576 +        PR_SetPollableEvent(mThreadEvent);
   1.577 +
   1.578 +    return NS_OK;
   1.579 +}
   1.580 +
   1.581 +NS_IMETHODIMP
   1.582 +nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
   1.583 +{
   1.584 +    MOZ_ASSERT(aKeepaliveIdleTimeS);
   1.585 +    if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
   1.586 +        return NS_ERROR_NULL_POINTER;
   1.587 +    }
   1.588 +    *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
   1.589 +    return NS_OK;
   1.590 +}
   1.591 +
   1.592 +NS_IMETHODIMP
   1.593 +nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
   1.594 +{
   1.595 +    MOZ_ASSERT(aKeepaliveRetryIntervalS);
   1.596 +    if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
   1.597 +        return NS_ERROR_NULL_POINTER;
   1.598 +    }
   1.599 +    *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
   1.600 +    return NS_OK;
   1.601 +}
   1.602 +
   1.603 +NS_IMETHODIMP
   1.604 +nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
   1.605 +{
   1.606 +    MOZ_ASSERT(aKeepaliveProbeCount);
   1.607 +    if (NS_WARN_IF(!aKeepaliveProbeCount)) {
   1.608 +        return NS_ERROR_NULL_POINTER;
   1.609 +    }
   1.610 +    *aKeepaliveProbeCount = mKeepaliveProbeCount;
   1.611 +    return NS_OK;
   1.612 +}
   1.613 +
   1.614 +NS_IMETHODIMP
   1.615 +nsSocketTransportService::CreateTransport(const char **types,
   1.616 +                                          uint32_t typeCount,
   1.617 +                                          const nsACString &host,
   1.618 +                                          int32_t port,
   1.619 +                                          nsIProxyInfo *proxyInfo,
   1.620 +                                          nsISocketTransport **result)
   1.621 +{
   1.622 +    NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
   1.623 +    NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
   1.624 +
   1.625 +    nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
   1.626 +    nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
   1.627 +    if (NS_FAILED(rv)) {
   1.628 +        return rv;
   1.629 +    }
   1.630 +
   1.631 +    trans.forget(result);
   1.632 +    return NS_OK;
   1.633 +}
   1.634 +
   1.635 +NS_IMETHODIMP
   1.636 +nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath,
   1.637 +                                                    nsISocketTransport **result)
   1.638 +{
   1.639 +    nsresult rv;
   1.640 +
   1.641 +    NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
   1.642 +
   1.643 +    nsAutoCString path;
   1.644 +    rv = aPath->GetNativePath(path);
   1.645 +    if (NS_FAILED(rv))
   1.646 +        return rv;
   1.647 +
   1.648 +    nsRefPtr<nsSocketTransport> trans = new nsSocketTransport();
   1.649 +
   1.650 +    rv = trans->InitWithFilename(path.get());
   1.651 +    if (NS_FAILED(rv))
   1.652 +        return rv;
   1.653 +
   1.654 +    trans.forget(result);
   1.655 +    return NS_OK;
   1.656 +}
   1.657 +
   1.658 +NS_IMETHODIMP
   1.659 +nsSocketTransportService::GetAutodialEnabled(bool *value)
   1.660 +{
   1.661 +    *value = mAutodialEnabled;
   1.662 +    return NS_OK;
   1.663 +}
   1.664 +
   1.665 +NS_IMETHODIMP
   1.666 +nsSocketTransportService::SetAutodialEnabled(bool value)
   1.667 +{
   1.668 +    mAutodialEnabled = value;
   1.669 +    return NS_OK;
   1.670 +}
   1.671 +
   1.672 +NS_IMETHODIMP
   1.673 +nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
   1.674 +{
   1.675 +    MutexAutoLock lock(mLock);
   1.676 +    if (mThreadEvent)
   1.677 +        PR_SetPollableEvent(mThreadEvent);
   1.678 +    return NS_OK;
   1.679 +}
   1.680 +
   1.681 +NS_IMETHODIMP
   1.682 +nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
   1.683 +                                             bool mayWait, uint32_t depth)
   1.684 +{
   1.685 +    return NS_OK;
   1.686 +}
   1.687 +
   1.688 +NS_IMETHODIMP
   1.689 +nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
   1.690 +                                                uint32_t depth,
   1.691 +                                                bool eventWasProcessed)
   1.692 +{
   1.693 +    return NS_OK;
   1.694 +}
   1.695 +
   1.696 +#ifdef MOZ_NUWA_PROCESS
   1.697 +#include "ipc/Nuwa.h"
   1.698 +#endif
   1.699 +
   1.700 +NS_IMETHODIMP
   1.701 +nsSocketTransportService::Run()
   1.702 +{
   1.703 +    PR_SetCurrentThreadName("Socket Thread");
   1.704 +
   1.705 +#ifdef MOZ_NUWA_PROCESS
   1.706 +    if (IsNuwaProcess()) {
   1.707 +        NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
   1.708 +                     "NuwaMarkCurrentThread is undefined!");
   1.709 +        NuwaMarkCurrentThread(nullptr, nullptr);
   1.710 +    }
   1.711 +#endif
   1.712 +
   1.713 +    SOCKET_LOG(("STS thread init\n"));
   1.714 +
   1.715 +    psm::InitializeSSLServerCertVerificationThreads();
   1.716 +
   1.717 +    gSocketThread = PR_GetCurrentThread();
   1.718 +
   1.719 +    // add thread event to poll list (mThreadEvent may be nullptr)
   1.720 +    mPollList[0].fd = mThreadEvent;
   1.721 +    mPollList[0].in_flags = PR_POLL_READ;
   1.722 +    mPollList[0].out_flags = 0;
   1.723 +
   1.724 +    nsIThread *thread = NS_GetCurrentThread();
   1.725 +
   1.726 +    // hook ourselves up to observe event processing for this thread
   1.727 +    nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
   1.728 +    threadInt->SetObserver(this);
   1.729 +
   1.730 +    // make sure the pseudo random number generator is seeded on this thread
   1.731 +    srand(static_cast<unsigned>(PR_Now()));
   1.732 +
   1.733 +    for (;;) {
   1.734 +        bool pendingEvents = false;
   1.735 +        thread->HasPendingEvents(&pendingEvents);
   1.736 +
   1.737 +        do {
   1.738 +            // If there are pending events for this thread then
   1.739 +            // DoPollIteration() should service the network without blocking.
   1.740 +            DoPollIteration(!pendingEvents);
   1.741 +            
   1.742 +            // If nothing was pending before the poll, it might be now
   1.743 +            if (!pendingEvents)
   1.744 +                thread->HasPendingEvents(&pendingEvents);
   1.745 +
   1.746 +            if (pendingEvents) {
   1.747 +                NS_ProcessNextEvent(thread);
   1.748 +                pendingEvents = false;
   1.749 +                thread->HasPendingEvents(&pendingEvents);
   1.750 +            }
   1.751 +        } while (pendingEvents);
   1.752 +
   1.753 +        bool goingOffline = false;
   1.754 +        // now that our event queue is empty, check to see if we should exit
   1.755 +        {
   1.756 +            MutexAutoLock lock(mLock);
   1.757 +            if (mShuttingDown)
   1.758 +                break;
   1.759 +            if (mGoingOffline) {
   1.760 +                mGoingOffline = false;
   1.761 +                goingOffline = true;
   1.762 +            }
   1.763 +        }
   1.764 +        // Avoid potential deadlock
   1.765 +        if (goingOffline)
   1.766 +            Reset(true);
   1.767 +    }
   1.768 +
   1.769 +    SOCKET_LOG(("STS shutting down thread\n"));
   1.770 +
   1.771 +    // detach all sockets, including locals
   1.772 +    Reset(false);
   1.773 +
   1.774 +    // Final pass over the event queue. This makes sure that events posted by
   1.775 +    // socket detach handlers get processed.
   1.776 +    NS_ProcessPendingEvents(thread);
   1.777 +
   1.778 +    gSocketThread = nullptr;
   1.779 +
   1.780 +    psm::StopSSLServerCertVerificationThreads();
   1.781 +
   1.782 +    SOCKET_LOG(("STS thread exit\n"));
   1.783 +    return NS_OK;
   1.784 +}
   1.785 +
   1.786 +void
   1.787 +nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
   1.788 +                                                SocketContext *socketList,
   1.789 +                                                int32_t index)
   1.790 +{
   1.791 +    bool isGuarded = false;
   1.792 +    if (aGuardLocals) {
   1.793 +        socketList[index].mHandler->IsLocal(&isGuarded);
   1.794 +        if (!isGuarded)
   1.795 +            socketList[index].mHandler->KeepWhenOffline(&isGuarded);
   1.796 +    }
   1.797 +    if (!isGuarded)
   1.798 +        DetachSocket(socketList, &socketList[index]);
   1.799 +}
   1.800 +
   1.801 +void
   1.802 +nsSocketTransportService::Reset(bool aGuardLocals)
   1.803 +{
   1.804 +    // detach any sockets
   1.805 +    int32_t i;
   1.806 +    for (i = mActiveCount - 1; i >= 0; --i) {
   1.807 +        DetachSocketWithGuard(aGuardLocals, mActiveList, i);
   1.808 +    }
   1.809 +    for (i = mIdleCount - 1; i >= 0; --i) {
   1.810 +        DetachSocketWithGuard(aGuardLocals, mIdleList, i);
   1.811 +    }
   1.812 +}
   1.813 +
   1.814 +nsresult
   1.815 +nsSocketTransportService::DoPollIteration(bool wait)
   1.816 +{
   1.817 +    SOCKET_LOG(("STS poll iter [%d]\n", wait));
   1.818 +
   1.819 +    int32_t i, count;
   1.820 +
   1.821 +    //
   1.822 +    // poll loop
   1.823 +    //
   1.824 +    // walk active list backwards to see if any sockets should actually be
   1.825 +    // idle, then walk the idle list backwards to see if any idle sockets
   1.826 +    // should become active.  take care to check only idle sockets that
   1.827 +    // were idle to begin with ;-)
   1.828 +    //
   1.829 +    count = mIdleCount;
   1.830 +    for (i=mActiveCount-1; i>=0; --i) {
   1.831 +        //---
   1.832 +        SOCKET_LOG(("  active [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
   1.833 +            mActiveList[i].mHandler,
   1.834 +            mActiveList[i].mHandler->mCondition,
   1.835 +            mActiveList[i].mHandler->mPollFlags));
   1.836 +        //---
   1.837 +        if (NS_FAILED(mActiveList[i].mHandler->mCondition))
   1.838 +            DetachSocket(mActiveList, &mActiveList[i]);
   1.839 +        else {
   1.840 +            uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
   1.841 +            if (in_flags == 0)
   1.842 +                MoveToIdleList(&mActiveList[i]);
   1.843 +            else {
   1.844 +                // update poll flags
   1.845 +                mPollList[i+1].in_flags = in_flags;
   1.846 +                mPollList[i+1].out_flags = 0;
   1.847 +            }
   1.848 +        }
   1.849 +    }
   1.850 +    for (i=count-1; i>=0; --i) {
   1.851 +        //---
   1.852 +        SOCKET_LOG(("  idle [%u] { handler=%p condition=%x pollflags=%hu }\n", i,
   1.853 +            mIdleList[i].mHandler,
   1.854 +            mIdleList[i].mHandler->mCondition,
   1.855 +            mIdleList[i].mHandler->mPollFlags));
   1.856 +        //---
   1.857 +        if (NS_FAILED(mIdleList[i].mHandler->mCondition))
   1.858 +            DetachSocket(mIdleList, &mIdleList[i]);
   1.859 +        else if (mIdleList[i].mHandler->mPollFlags != 0)
   1.860 +            MoveToPollList(&mIdleList[i]);
   1.861 +    }
   1.862 +
   1.863 +    SOCKET_LOG(("  calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
   1.864 +
   1.865 +#if defined(XP_WIN)
   1.866 +    // 30 active connections is the historic limit before firefox 7's 256. A few
   1.867 +    //  windows systems have troubles with the higher limit, so actively probe a
   1.868 +    // limit the first time we exceed 30.
   1.869 +    if ((mActiveCount > 30) && !mProbedMaxCount)
   1.870 +        ProbeMaxCount();
   1.871 +#endif
   1.872 +
   1.873 +    // Measures seconds spent while blocked on PR_Poll
   1.874 +    uint32_t pollInterval;
   1.875 +
   1.876 +    int32_t n = Poll(wait, &pollInterval);
   1.877 +    if (n < 0) {
   1.878 +        SOCKET_LOG(("  PR_Poll error [%d]\n", PR_GetError()));
   1.879 +    }
   1.880 +    else {
   1.881 +        //
   1.882 +        // service "active" sockets...
   1.883 +        //
   1.884 +        for (i=0; i<int32_t(mActiveCount); ++i) {
   1.885 +            PRPollDesc &desc = mPollList[i+1];
   1.886 +            SocketContext &s = mActiveList[i];
   1.887 +            if (n > 0 && desc.out_flags != 0) {
   1.888 +                s.mElapsedTime = 0;
   1.889 +                s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
   1.890 +            }
   1.891 +            // check for timeout errors unless disabled...
   1.892 +            else if (s.mHandler->mPollTimeout != UINT16_MAX) {
   1.893 +                // update elapsed time counter
   1.894 +                // (NOTE: We explicitly cast UINT16_MAX to be an unsigned value
   1.895 +                // here -- otherwise, some compilers will treat it as signed,
   1.896 +                // which makes them fire signed/unsigned-comparison build
   1.897 +                // warnings for the comparison against 'pollInterval'.)
   1.898 +                if (MOZ_UNLIKELY(pollInterval >
   1.899 +                                static_cast<uint32_t>(UINT16_MAX) -
   1.900 +                                s.mElapsedTime))
   1.901 +                    s.mElapsedTime = UINT16_MAX;
   1.902 +                else
   1.903 +                    s.mElapsedTime += uint16_t(pollInterval);
   1.904 +                // check for timeout expiration 
   1.905 +                if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
   1.906 +                    s.mElapsedTime = 0;
   1.907 +                    s.mHandler->OnSocketReady(desc.fd, -1);
   1.908 +                }
   1.909 +            }
   1.910 +        }
   1.911 +
   1.912 +        //
   1.913 +        // check for "dead" sockets and remove them (need to do this in
   1.914 +        // reverse order obviously).
   1.915 +        //
   1.916 +        for (i=mActiveCount-1; i>=0; --i) {
   1.917 +            if (NS_FAILED(mActiveList[i].mHandler->mCondition))
   1.918 +                DetachSocket(mActiveList, &mActiveList[i]);
   1.919 +        }
   1.920 +
   1.921 +        if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
   1.922 +            // acknowledge pollable event (wait should not block)
   1.923 +            if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
   1.924 +                // On Windows, the TCP loopback connection in the
   1.925 +                // pollable event may become broken when a laptop
   1.926 +                // switches between wired and wireless networks or
   1.927 +                // wakes up from hibernation.  We try to create a
   1.928 +                // new pollable event.  If that fails, we fall back
   1.929 +                // on "busy wait".
   1.930 +                {
   1.931 +                    MutexAutoLock lock(mLock);
   1.932 +                    PR_DestroyPollableEvent(mThreadEvent);
   1.933 +                    mThreadEvent = PR_NewPollableEvent();
   1.934 +                }
   1.935 +                if (!mThreadEvent) {
   1.936 +                    NS_WARNING("running socket transport thread without "
   1.937 +                               "a pollable event");
   1.938 +                    SOCKET_LOG(("running socket transport thread without "
   1.939 +                         "a pollable event"));
   1.940 +                }
   1.941 +                mPollList[0].fd = mThreadEvent;
   1.942 +                // mPollList[0].in_flags was already set to PR_POLL_READ
   1.943 +                // in Run().
   1.944 +                mPollList[0].out_flags = 0;
   1.945 +            }
   1.946 +        }
   1.947 +    }
   1.948 +
   1.949 +    return NS_OK;
   1.950 +}
   1.951 +
   1.952 +nsresult
   1.953 +nsSocketTransportService::UpdatePrefs()
   1.954 +{
   1.955 +    mSendBufferSize = 0;
   1.956 +    
   1.957 +    nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   1.958 +    if (tmpPrefService) {
   1.959 +        int32_t bufferSize;
   1.960 +        nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
   1.961 +        if (NS_SUCCEEDED(rv) && bufferSize > 0)
   1.962 +            mSendBufferSize = bufferSize;
   1.963 +
   1.964 +        // Default TCP Keepalive Values.
   1.965 +        int32_t keepaliveIdleTimeS;
   1.966 +        rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
   1.967 +                                        &keepaliveIdleTimeS);
   1.968 +        if (NS_SUCCEEDED(rv))
   1.969 +            mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
   1.970 +                                          1, kMaxTCPKeepIdle);
   1.971 +
   1.972 +        int32_t keepaliveRetryIntervalS;
   1.973 +        rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
   1.974 +                                        &keepaliveRetryIntervalS);
   1.975 +        if (NS_SUCCEEDED(rv))
   1.976 +            mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
   1.977 +                                               1, kMaxTCPKeepIntvl);
   1.978 +
   1.979 +        int32_t keepaliveProbeCount;
   1.980 +        rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
   1.981 +                                        &keepaliveProbeCount);
   1.982 +        if (NS_SUCCEEDED(rv))
   1.983 +            mKeepaliveProbeCount = clamped(keepaliveProbeCount,
   1.984 +                                           1, kMaxTCPKeepCount);
   1.985 +        bool keepaliveEnabled = false;
   1.986 +        rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
   1.987 +                                         &keepaliveEnabled);
   1.988 +        if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
   1.989 +            mKeepaliveEnabledPref = keepaliveEnabled;
   1.990 +            OnKeepaliveEnabledPrefChange();
   1.991 +        }
   1.992 +    }
   1.993 +    
   1.994 +    return NS_OK;
   1.995 +}
   1.996 +
   1.997 +void
   1.998 +nsSocketTransportService::OnKeepaliveEnabledPrefChange()
   1.999 +{
  1.1000 +    // Dispatch to socket thread if we're not executing there.
  1.1001 +    if (PR_GetCurrentThread() != gSocketThread) {
  1.1002 +        gSocketTransportService->Dispatch(
  1.1003 +            NS_NewRunnableMethod(
  1.1004 +                this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
  1.1005 +            NS_DISPATCH_NORMAL);
  1.1006 +        return;
  1.1007 +    }
  1.1008 +
  1.1009 +    SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
  1.1010 +                mKeepaliveEnabledPref ? "enabled" : "disabled"));
  1.1011 +
  1.1012 +    // Notify each socket that keepalive has been en/disabled globally.
  1.1013 +    for (int32_t i = mActiveCount - 1; i >= 0; --i) {
  1.1014 +        NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
  1.1015 +    }
  1.1016 +    for (int32_t i = mIdleCount - 1; i >= 0; --i) {
  1.1017 +        NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
  1.1018 +    }
  1.1019 +}
  1.1020 +
  1.1021 +void
  1.1022 +nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
  1.1023 +{
  1.1024 +    MOZ_ASSERT(sock, "SocketContext cannot be null!");
  1.1025 +    MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
  1.1026 +
  1.1027 +    if (!sock || !sock->mHandler) {
  1.1028 +        return;
  1.1029 +    }
  1.1030 +
  1.1031 +    sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
  1.1032 +}
  1.1033 +
  1.1034 +NS_IMETHODIMP
  1.1035 +nsSocketTransportService::Observe(nsISupports *subject,
  1.1036 +                                  const char *topic,
  1.1037 +                                  const char16_t *data)
  1.1038 +{
  1.1039 +    if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  1.1040 +        UpdatePrefs();
  1.1041 +        return NS_OK;
  1.1042 +    }
  1.1043 +
  1.1044 +    if (!strcmp(topic, "profile-initial-state")) {
  1.1045 +        int32_t blipInterval = Preferences::GetInt(BLIP_INTERVAL_PREF, 0);
  1.1046 +        if (blipInterval <= 0) {
  1.1047 +            return NS_OK;
  1.1048 +        }
  1.1049 +
  1.1050 +        return net::NetworkActivityMonitor::Init(blipInterval);
  1.1051 +    }
  1.1052 +
  1.1053 +    if (!strcmp(topic, "last-pb-context-exited")) {
  1.1054 +        nsCOMPtr<nsIRunnable> ev =
  1.1055 +          NS_NewRunnableMethod(this,
  1.1056 +                               &nsSocketTransportService::ClosePrivateConnections);
  1.1057 +        nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
  1.1058 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1059 +    }
  1.1060 +
  1.1061 +    return NS_OK;
  1.1062 +}
  1.1063 +
  1.1064 +void
  1.1065 +nsSocketTransportService::ClosePrivateConnections()
  1.1066 +{
  1.1067 +    // Must be called on the socket thread.
  1.1068 +#ifdef DEBUG
  1.1069 +    bool onSTSThread;
  1.1070 +    IsOnCurrentThread(&onSTSThread);
  1.1071 +    MOZ_ASSERT(onSTSThread);
  1.1072 +#endif
  1.1073 +
  1.1074 +    for (int32_t i = mActiveCount - 1; i >= 0; --i) {
  1.1075 +        if (mActiveList[i].mHandler->mIsPrivate) {
  1.1076 +            DetachSocket(mActiveList, &mActiveList[i]);
  1.1077 +        }
  1.1078 +    }
  1.1079 +    for (int32_t i = mIdleCount - 1; i >= 0; --i) {
  1.1080 +        if (mIdleList[i].mHandler->mIsPrivate) {
  1.1081 +            DetachSocket(mIdleList, &mIdleList[i]);
  1.1082 +        }
  1.1083 +    }
  1.1084 +
  1.1085 +    mozilla::ClearPrivateSSLState();
  1.1086 +}
  1.1087 +
  1.1088 +NS_IMETHODIMP
  1.1089 +nsSocketTransportService::GetSendBufferSize(int32_t *value)
  1.1090 +{
  1.1091 +    *value = mSendBufferSize;
  1.1092 +    return NS_OK;
  1.1093 +}
  1.1094 +
  1.1095 +
  1.1096 +/// ugly OS specific includes are placed at the bottom of the src for clarity
  1.1097 +
  1.1098 +#if defined(XP_WIN)
  1.1099 +#include <windows.h>
  1.1100 +#elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
  1.1101 +#include <sys/resource.h>
  1.1102 +#endif
  1.1103 +
  1.1104 +// Right now the only need to do this is on windows.
  1.1105 +#if defined(XP_WIN)
  1.1106 +void
  1.1107 +nsSocketTransportService::ProbeMaxCount()
  1.1108 +{
  1.1109 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.1110 +
  1.1111 +    if (mProbedMaxCount)
  1.1112 +        return;
  1.1113 +    mProbedMaxCount = true;
  1.1114 +
  1.1115 +    // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
  1.1116 +    // sockets. See bug 692260 - windows should be able to handle 1000 sockets
  1.1117 +    // in select() without a problem, but LSPs have been known to balk at lower
  1.1118 +    // numbers. (64 in the bug).
  1.1119 +
  1.1120 +    // Allocate
  1.1121 +    struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
  1.1122 +    uint32_t numAllocated = 0;
  1.1123 +
  1.1124 +    for (uint32_t index = 0 ; index < gMaxCount; ++index) {
  1.1125 +        pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
  1.1126 +        pfd[index].out_flags = 0;
  1.1127 +        pfd[index].fd =  PR_OpenTCPSocket(PR_AF_INET);
  1.1128 +        if (!pfd[index].fd) {
  1.1129 +            SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
  1.1130 +            if (index < SOCKET_LIMIT_MIN)
  1.1131 +                gMaxCount = SOCKET_LIMIT_MIN;
  1.1132 +            else
  1.1133 +                gMaxCount = index;
  1.1134 +            break;
  1.1135 +        }
  1.1136 +        ++numAllocated;
  1.1137 +    }
  1.1138 +
  1.1139 +    // Test
  1.1140 +    PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U);
  1.1141 +    while (gMaxCount <= numAllocated) {
  1.1142 +        int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
  1.1143 +        
  1.1144 +        SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
  1.1145 +                    gMaxCount, rv));
  1.1146 +
  1.1147 +        if (rv >= 0)
  1.1148 +            break;
  1.1149 +
  1.1150 +        SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
  1.1151 +                    gMaxCount, rv, PR_GetError()));
  1.1152 +
  1.1153 +        gMaxCount -= 32;
  1.1154 +        if (gMaxCount <= SOCKET_LIMIT_MIN) {
  1.1155 +            gMaxCount = SOCKET_LIMIT_MIN;
  1.1156 +            break;
  1.1157 +        }
  1.1158 +    }
  1.1159 +
  1.1160 +    // Free
  1.1161 +    for (uint32_t index = 0 ; index < numAllocated; ++index)
  1.1162 +        if (pfd[index].fd)
  1.1163 +            PR_Close(pfd[index].fd);
  1.1164 +
  1.1165 +    SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
  1.1166 +}
  1.1167 +#endif // windows
  1.1168 +
  1.1169 +PRStatus
  1.1170 +nsSocketTransportService::DiscoverMaxCount()
  1.1171 +{
  1.1172 +    gMaxCount = SOCKET_LIMIT_MIN;
  1.1173 +
  1.1174 +#if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
  1.1175 +    // On unix and os x network sockets and file
  1.1176 +    // descriptors are the same. OS X comes defaulted at 256,
  1.1177 +    // most linux at 1000. We can reliably use [sg]rlimit to
  1.1178 +    // query that and raise it. We will try to raise it 250 past
  1.1179 +    // our target number of SOCKET_LIMIT_TARGET so that some descriptors
  1.1180 +    // are still available for other things.
  1.1181 +
  1.1182 +    struct rlimit rlimitData;
  1.1183 +    if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
  1.1184 +        return PR_SUCCESS;
  1.1185 +    if (rlimitData.rlim_cur >=  SOCKET_LIMIT_TARGET + 250) {
  1.1186 +        gMaxCount = SOCKET_LIMIT_TARGET;
  1.1187 +        return PR_SUCCESS;
  1.1188 +    }
  1.1189 +
  1.1190 +    int32_t maxallowed = rlimitData.rlim_max;
  1.1191 +    if (maxallowed == -1) {                       /* no limit */
  1.1192 +        maxallowed = SOCKET_LIMIT_TARGET + 250;
  1.1193 +    } else if ((uint32_t)maxallowed < SOCKET_LIMIT_MIN + 250) {
  1.1194 +        return PR_SUCCESS;
  1.1195 +    } else if ((uint32_t)maxallowed > SOCKET_LIMIT_TARGET + 250) {
  1.1196 +        maxallowed = SOCKET_LIMIT_TARGET + 250;
  1.1197 +    }
  1.1198 +
  1.1199 +    rlimitData.rlim_cur = maxallowed;
  1.1200 +    setrlimit(RLIMIT_NOFILE, &rlimitData);
  1.1201 +    if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1)
  1.1202 +        if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250)
  1.1203 +            gMaxCount = rlimitData.rlim_cur - 250;
  1.1204 +
  1.1205 +#elif defined(XP_WIN) && !defined(WIN_CE)
  1.1206 +    // >= XP is confirmed to have at least 1000
  1.1207 +    gMaxCount = SOCKET_LIMIT_TARGET;
  1.1208 +#else
  1.1209 +    // other platforms are harder to test - so leave at safe legacy value
  1.1210 +#endif
  1.1211 +
  1.1212 +    return PR_SUCCESS;
  1.1213 +}
  1.1214 +
  1.1215 +
  1.1216 +// Used to return connection info to Dashboard.cpp
  1.1217 +void
  1.1218 +nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo> *data,
  1.1219 +        struct SocketContext *context, bool aActive)
  1.1220 +{
  1.1221 +    if (context->mHandler->mIsPrivate)
  1.1222 +        return;
  1.1223 +    PRFileDesc *aFD = context->mFD;
  1.1224 +    bool tcp = (PR_GetDescType(aFD) == PR_DESC_SOCKET_TCP);
  1.1225 +
  1.1226 +    PRNetAddr peer_addr;
  1.1227 +    PR_GetPeerName(aFD, &peer_addr);
  1.1228 +
  1.1229 +    char host[64] = {0};
  1.1230 +    PR_NetAddrToString(&peer_addr, host, sizeof(host));
  1.1231 +
  1.1232 +    uint16_t port;
  1.1233 +    if (peer_addr.raw.family == PR_AF_INET)
  1.1234 +        port = peer_addr.inet.port;
  1.1235 +    else
  1.1236 +        port = peer_addr.ipv6.port;
  1.1237 +    port = PR_ntohs(port);
  1.1238 +    uint64_t sent = context->mHandler->ByteCountSent();
  1.1239 +    uint64_t received = context->mHandler->ByteCountReceived();
  1.1240 +    SocketInfo info = { nsCString(host), sent, received, port, aActive, tcp };
  1.1241 +
  1.1242 +    data->AppendElement(info);
  1.1243 +}
  1.1244 +
  1.1245 +void
  1.1246 +nsSocketTransportService::GetSocketConnections(nsTArray<SocketInfo> *data)
  1.1247 +{
  1.1248 +    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
  1.1249 +    for (uint32_t i = 0; i < mActiveCount; i++)
  1.1250 +        AnalyzeConnection(data, &mActiveList[i], true);
  1.1251 +    for (uint32_t i = 0; i < mIdleCount; i++)
  1.1252 +        AnalyzeConnection(data, &mIdleList[i], false);
  1.1253 +}

mercurial