netwerk/base/src/nsSocketTransportService2.cpp

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

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

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

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

mercurial