nsSocketTransportService2.cpp (60094B)
1 // vim:set sw=2 sts=2 et cin: 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsSocketTransportService2.h" 7 8 #include "mozilla/Atomics.h" 9 #include "mozilla/ChaosMode.h" 10 #include "mozilla/glean/NetwerkMetrics.h" 11 #include "mozilla/IntegerPrintfMacros.h" 12 #include "mozilla/MaybeLeakRefPtr.h" 13 #include "mozilla/PodOperations.h" 14 #include "mozilla/Preferences.h" 15 #include "mozilla/ProfilerMarkers.h" 16 #include "mozilla/ProfilerThreadSleep.h" 17 #include "mozilla/PublicSSL.h" 18 #include "mozilla/ReverseIterator.h" 19 #include "mozilla/Services.h" 20 #include "mozilla/StaticPrefs_network.h" 21 #include "mozilla/Tokenizer.h" 22 #include "mozilla/Telemetry.h" 23 #include "nsASocketHandler.h" 24 #include "nsError.h" 25 #include "nsIEventTarget.h" 26 #include "nsIFile.h" 27 #include "nsINetworkLinkService.h" 28 #include "nsIOService.h" 29 #include "nsIObserverService.h" 30 #include "nsIWidget.h" 31 #include "nsServiceManagerUtils.h" 32 #include "nsSocketTransport2.h" 33 #include "nsThreadUtils.h" 34 #include "prerror.h" 35 #include "prnetdb.h" 36 #ifdef XP_UNIX 37 # include "private/pprio.h" 38 #endif 39 40 namespace mozilla { 41 namespace net { 42 43 #define SOCKET_THREAD_LONGTASK_MS 3 44 45 LazyLogModule gSocketTransportLog("nsSocketTransport"); 46 LazyLogModule gUDPSocketLog("UDPSocket"); 47 LazyLogModule gTCPSocketLog("TCPSocket"); 48 49 nsSocketTransportService* gSocketTransportService = nullptr; 50 static Atomic<PRThread*, Relaxed> gSocketThread(nullptr); 51 52 #define SEND_BUFFER_PREF "network.tcp.sendbuffer" 53 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled" 54 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time" 55 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval" 56 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count" 57 #define SOCKET_LIMIT_TARGET 1000U 58 #define MAX_TIME_BETWEEN_TWO_POLLS \ 59 "network.sts.max_time_for_events_between_two_polls" 60 #define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period" 61 #define POLL_BUSY_WAIT_PERIOD_TIMEOUT \ 62 "network.sts.poll_busy_wait_period_timeout" 63 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN \ 64 "network.sts.max_time_for_pr_close_during_shutdown" 65 #define POLLABLE_EVENT_TIMEOUT "network.sts.pollable_event_timeout" 66 67 #define REPAIR_POLLABLE_EVENT_TIME 10 68 69 uint32_t nsSocketTransportService::gMaxCount; 70 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce; 71 72 // Utility functions 73 bool OnSocketThread() { return PR_GetCurrentThread() == gSocketThread; } 74 75 //----------------------------------------------------------------------------- 76 77 bool nsSocketTransportService::SocketContext::IsTimedOut( 78 PRIntervalTime now) const { 79 return TimeoutIn(now) == 0; 80 } 81 82 void nsSocketTransportService::SocketContext::EnsureTimeout( 83 PRIntervalTime now) { 84 SOCKET_LOG(("SocketContext::EnsureTimeout socket=%p", mHandler.get())); 85 if (!mPollStartEpoch) { 86 SOCKET_LOG((" engaging")); 87 mPollStartEpoch = now; 88 } 89 } 90 91 void nsSocketTransportService::SocketContext::DisengageTimeout() { 92 SOCKET_LOG(("SocketContext::DisengageTimeout socket=%p", mHandler.get())); 93 mPollStartEpoch = 0; 94 } 95 96 PRIntervalTime nsSocketTransportService::SocketContext::TimeoutIn( 97 PRIntervalTime now) const { 98 SOCKET_LOG(("SocketContext::TimeoutIn socket=%p, timeout=%us", mHandler.get(), 99 mHandler->mPollTimeout)); 100 101 if (mHandler->mPollTimeout == UINT16_MAX || !mPollStartEpoch) { 102 SOCKET_LOG((" not engaged")); 103 return NS_SOCKET_POLL_TIMEOUT; 104 } 105 106 PRIntervalTime elapsed = (now - mPollStartEpoch); 107 PRIntervalTime timeout = PR_SecondsToInterval(mHandler->mPollTimeout); 108 109 if (elapsed >= timeout) { 110 SOCKET_LOG((" timed out!")); 111 return 0; 112 } 113 SOCKET_LOG((" remains %us", PR_IntervalToSeconds(timeout - elapsed))); 114 return timeout - elapsed; 115 } 116 117 void nsSocketTransportService::SocketContext::MaybeResetEpoch() { 118 if (mPollStartEpoch && mHandler->mPollTimeout == UINT16_MAX) { 119 mPollStartEpoch = 0; 120 } 121 } 122 123 //----------------------------------------------------------------------------- 124 // ctor/dtor (called on the main/UI thread by the service manager) 125 126 nsSocketTransportService::nsSocketTransportService() 127 : mPollableEventTimeout(TimeDuration::FromSeconds(6)), 128 mMaxTimeForPrClosePref(PR_SecondsToInterval(5)), 129 mNetworkLinkChangeBusyWaitPeriod(PR_SecondsToInterval(50)), 130 mNetworkLinkChangeBusyWaitTimeout(PR_SecondsToInterval(7)) { 131 NS_ASSERTION(NS_IsMainThread(), "wrong thread"); 132 133 PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount); 134 135 NS_ASSERTION(!gSocketTransportService, "must not instantiate twice"); 136 gSocketTransportService = this; 137 138 // The Poll list always has an entry at [0]. The rest of the 139 // list is a duplicate of the Active list's PRFileDesc file descriptors. 140 PRPollDesc entry = {nullptr, PR_POLL_READ | PR_POLL_EXCEPT, 0}; 141 mPollList.InsertElementAt(0, entry); 142 } 143 144 void nsSocketTransportService::ApplyPortRemap(uint16_t* aPort) { 145 MOZ_ASSERT(IsOnCurrentThreadInfallible()); 146 147 if (!mPortRemapping) { 148 return; 149 } 150 151 // Reverse the array to make later rules override earlier rules. 152 for (auto const& portMapping : Reversed(*mPortRemapping)) { 153 if (*aPort < std::get<0>(portMapping)) { 154 continue; 155 } 156 if (*aPort > std::get<1>(portMapping)) { 157 continue; 158 } 159 160 *aPort = std::get<2>(portMapping); 161 return; 162 } 163 } 164 165 bool nsSocketTransportService::UpdatePortRemapPreference( 166 nsACString const& aPortMappingPref) { 167 TPortRemapping portRemapping; 168 169 auto consumePreference = [&]() -> bool { 170 Tokenizer tokenizer(aPortMappingPref); 171 172 tokenizer.SkipWhites(); 173 if (tokenizer.CheckEOF()) { 174 return true; 175 } 176 177 nsTArray<std::tuple<uint16_t, uint16_t>> ranges(2); 178 while (true) { 179 uint16_t loPort; 180 tokenizer.SkipWhites(); 181 if (!tokenizer.ReadInteger(&loPort)) { 182 break; 183 } 184 185 uint16_t hiPort; 186 tokenizer.SkipWhites(); 187 if (tokenizer.CheckChar('-')) { 188 tokenizer.SkipWhites(); 189 if (!tokenizer.ReadInteger(&hiPort)) { 190 break; 191 } 192 } else { 193 hiPort = loPort; 194 } 195 196 ranges.AppendElement(std::make_tuple(loPort, hiPort)); 197 198 tokenizer.SkipWhites(); 199 if (tokenizer.CheckChar(',')) { 200 continue; // another port or port range is expected 201 } 202 203 if (tokenizer.CheckChar('=')) { 204 uint16_t targetPort; 205 tokenizer.SkipWhites(); 206 if (!tokenizer.ReadInteger(&targetPort)) { 207 break; 208 } 209 210 // Storing reversed, because the most common cases (like 443) will very 211 // likely be listed as first, less common cases will be added to the end 212 // of the list mapping to the same port. As we iterate the whole 213 // remapping array from the end, this may have a small perf win by 214 // hitting the most common cases earlier. 215 for (auto const& range : Reversed(ranges)) { 216 portRemapping.AppendElement(std::make_tuple( 217 std::get<0>(range), std::get<1>(range), targetPort)); 218 } 219 ranges.Clear(); 220 221 tokenizer.SkipWhites(); 222 if (tokenizer.CheckChar(';')) { 223 continue; // more mappings (or EOF) expected 224 } 225 if (tokenizer.CheckEOF()) { 226 return true; 227 } 228 } 229 230 // Anything else is unexpected. 231 break; 232 } 233 234 // 'break' from the parsing loop means ill-formed preference 235 portRemapping.Clear(); 236 return false; 237 }; 238 239 bool rv = consumePreference(); 240 241 if (!IsOnCurrentThread()) { 242 nsCOMPtr<nsIThread> thread = GetThreadSafely(); 243 if (!thread) { 244 // Init hasn't been called yet. Could probably just assert. 245 // If shutdown, the dispatch below will just silently fail. 246 NS_ASSERTION(false, "ApplyPortRemapPreference before STS::Init"); 247 return false; 248 } 249 thread->Dispatch(NewRunnableMethod<TPortRemapping>( 250 "net::ApplyPortRemapping", this, 251 &nsSocketTransportService::ApplyPortRemapPreference, portRemapping)); 252 } else { 253 ApplyPortRemapPreference(portRemapping); 254 } 255 256 return rv; 257 } 258 259 nsSocketTransportService::~nsSocketTransportService() { 260 NS_ASSERTION(NS_IsMainThread(), "wrong thread"); 261 NS_ASSERTION(!mInitialized, "not shutdown properly"); 262 263 gSocketTransportService = nullptr; 264 } 265 266 //----------------------------------------------------------------------------- 267 // event queue (any thread) 268 269 already_AddRefed<nsIThread> nsSocketTransportService::GetThreadSafely() { 270 MutexAutoLock lock(mLock); 271 nsCOMPtr<nsIThread> result = mThread; 272 return result.forget(); 273 } 274 275 NS_IMETHODIMP 276 nsSocketTransportService::DispatchFromScript(nsIRunnable* event, 277 DispatchFlags flags) { 278 return Dispatch(do_AddRef(event), flags); 279 } 280 281 NS_IMETHODIMP 282 nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event, 283 DispatchFlags flags) { 284 // NOTE: We don't leak runnables on dispatch failure here, even if 285 // NS_DISPATCH_FALLIBLE is not specified. 286 nsCOMPtr<nsIRunnable> event_ref(std::move(event)); 287 SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get())); 288 289 nsCOMPtr<nsIThread> thread = GetThreadSafely(); 290 nsresult rv = thread ? thread->Dispatch(event_ref.forget(), 291 flags | NS_DISPATCH_FALLIBLE) 292 : NS_ERROR_NOT_INITIALIZED; 293 if (rv == NS_ERROR_UNEXPECTED) { 294 // Thread is no longer accepting events. We must have just shut it 295 // down on the main thread. Pretend we never saw it. 296 rv = NS_ERROR_NOT_INITIALIZED; 297 } 298 return rv; 299 } 300 301 NS_IMETHODIMP 302 nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>, 303 uint32_t) { 304 return NS_ERROR_NOT_IMPLEMENTED; 305 } 306 307 NS_IMETHODIMP 308 nsSocketTransportService::RegisterShutdownTask(nsITargetShutdownTask* task) { 309 nsCOMPtr<nsIThread> thread = GetThreadSafely(); 310 return thread ? thread->RegisterShutdownTask(task) : NS_ERROR_UNEXPECTED; 311 } 312 313 NS_IMETHODIMP 314 nsSocketTransportService::UnregisterShutdownTask(nsITargetShutdownTask* task) { 315 nsCOMPtr<nsIThread> thread = GetThreadSafely(); 316 return thread ? thread->UnregisterShutdownTask(task) : NS_ERROR_UNEXPECTED; 317 } 318 319 NS_IMETHODIMP 320 nsSocketTransportService::IsOnCurrentThread(bool* result) { 321 *result = OnSocketThread(); 322 return NS_OK; 323 } 324 325 NS_IMETHODIMP_(bool) 326 nsSocketTransportService::IsOnCurrentThreadInfallible() { 327 return OnSocketThread(); 328 } 329 330 //----------------------------------------------------------------------------- 331 // nsIDirectTaskDispatcher 332 333 already_AddRefed<nsIDirectTaskDispatcher> 334 nsSocketTransportService::GetDirectTaskDispatcherSafely() { 335 MutexAutoLock lock(mLock); 336 nsCOMPtr<nsIDirectTaskDispatcher> result = mDirectTaskDispatcher; 337 return result.forget(); 338 } 339 340 NS_IMETHODIMP 341 nsSocketTransportService::DispatchDirectTask( 342 already_AddRefed<nsIRunnable> aEvent) { 343 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher = 344 GetDirectTaskDispatcherSafely(); 345 NS_ENSURE_TRUE(dispatcher, NS_ERROR_NOT_INITIALIZED); 346 return dispatcher->DispatchDirectTask(std::move(aEvent)); 347 } 348 349 NS_IMETHODIMP nsSocketTransportService::DrainDirectTasks() { 350 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher = 351 GetDirectTaskDispatcherSafely(); 352 if (!dispatcher) { 353 // nothing to drain. 354 return NS_OK; 355 } 356 return dispatcher->DrainDirectTasks(); 357 } 358 359 NS_IMETHODIMP nsSocketTransportService::HaveDirectTasks(bool* aValue) { 360 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher = 361 GetDirectTaskDispatcherSafely(); 362 if (!dispatcher) { 363 *aValue = false; 364 return NS_OK; 365 } 366 return dispatcher->HaveDirectTasks(aValue); 367 } 368 369 //----------------------------------------------------------------------------- 370 // socket api (socket thread only) 371 372 NS_IMETHODIMP 373 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable* event) { 374 SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n")); 375 376 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 377 378 if (CanAttachSocket()) { 379 return Dispatch(event, NS_DISPATCH_NORMAL); 380 } 381 382 auto* runnable = new LinkedRunnableEvent(event); 383 mPendingSocketQueue.insertBack(runnable); 384 return NS_OK; 385 } 386 387 NS_IMETHODIMP 388 nsSocketTransportService::AttachSocket(PRFileDesc* fd, 389 nsASocketHandler* handler) { 390 SOCKET_LOG( 391 ("nsSocketTransportService::AttachSocket [handler=%p]\n", handler)); 392 393 #ifdef XP_UNIX 394 # ifdef XP_DARWIN 395 // See the Darwin case in config/external/nspr/pr/moz.build 396 static constexpr PROsfd kFDs = 4096; 397 # else 398 static constexpr PROsfd kFDs = 65536; 399 # endif 400 PROsfd osfd = PR_FileDesc2NativeHandle(fd); 401 // If the native fd exceeds what PR_Poll can handle, PR_Poll will treat it as 402 // invalid (POLLNVAL) and networking degrades into hard-to-debug failures. 403 // Crash early with a clear reason instead. See bug 1980171 for context. 404 MOZ_RELEASE_ASSERT(osfd < kFDs); 405 #endif 406 407 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 408 409 if (!CanAttachSocket()) { 410 return NS_ERROR_NOT_AVAILABLE; 411 } 412 413 SocketContext sock{fd, handler, 0}; 414 415 AddToIdleList(&sock); 416 return NS_OK; 417 } 418 419 // the number of sockets that can be attached at any given time is 420 // limited. this is done because some operating systems (e.g., Win9x) 421 // limit the number of sockets that can be created by an application. 422 // AttachSocket will fail if the limit is exceeded. consumers should 423 // call CanAttachSocket and check the result before creating a socket. 424 425 bool nsSocketTransportService::CanAttachSocket() { 426 MOZ_ASSERT(!mShuttingDown); 427 uint32_t total = mActiveList.Length() + mIdleList.Length(); 428 bool rv = total < gMaxCount; 429 430 if (!rv) { 431 static bool reported_socket_limit_reached = false; 432 if (!reported_socket_limit_reached) { 433 mozilla::glean::networking::os_socket_limit_reached.Add(1); 434 reported_socket_limit_reached = true; 435 } 436 SOCKET_LOG( 437 ("nsSocketTransportService::CanAttachSocket failed - total: %d, " 438 "maxCount: %d\n", 439 total, gMaxCount)); 440 } 441 442 MOZ_ASSERT(mInitialized); 443 return rv; 444 } 445 446 nsresult nsSocketTransportService::DetachSocket(SocketContextList& listHead, 447 SocketContext* sock) { 448 SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n", 449 sock->mHandler.get())); 450 MOZ_ASSERT((&listHead == &mActiveList) || (&listHead == &mIdleList), 451 "DetachSocket invalid head"); 452 453 { 454 // inform the handler that this socket is going away 455 sock->mHandler->OnSocketDetached(sock->mFD); 456 } 457 mSentBytesCount += sock->mHandler->ByteCountSent(); 458 mReceivedBytesCount += sock->mHandler->ByteCountReceived(); 459 460 // cleanup 461 sock->mFD = nullptr; 462 463 if (&listHead == &mActiveList) { 464 RemoveFromPollList(sock); 465 } else { 466 RemoveFromIdleList(sock); 467 } 468 469 // NOTE: sock is now an invalid pointer 470 471 // 472 // notify the first element on the pending socket queue... 473 // 474 nsCOMPtr<nsIRunnable> event; 475 LinkedRunnableEvent* runnable = mPendingSocketQueue.getFirst(); 476 if (runnable) { 477 event = runnable->TakeEvent(); 478 runnable->remove(); 479 delete runnable; 480 } 481 if (event) { 482 // move event from pending queue to dispatch queue 483 return Dispatch(event, NS_DISPATCH_NORMAL); 484 } 485 return NS_OK; 486 } 487 488 // Returns the index of a SocketContext within a list, or -1 if it's 489 // not a pointer to a list element 490 // NOTE: this could be supplied by nsTArray<> 491 int64_t nsSocketTransportService::SockIndex(SocketContextList& aList, 492 SocketContext* aSock) { 493 ptrdiff_t index = -1; 494 if (!aList.IsEmpty()) { 495 index = aSock - &aList[0]; 496 if (index < 0 || (size_t)index + 1 > aList.Length()) { 497 index = -1; 498 } 499 } 500 return (int64_t)index; 501 } 502 503 void nsSocketTransportService::AddToPollList(SocketContext* sock) { 504 MOZ_ASSERT(SockIndex(mActiveList, sock) == -1, 505 "AddToPollList Socket Already Active"); 506 507 SOCKET_LOG(("nsSocketTransportService::AddToPollList %p [handler=%p]\n", sock, 508 sock->mHandler.get())); 509 510 sock->EnsureTimeout(PR_IntervalNow()); 511 PRPollDesc poll; 512 poll.fd = sock->mFD; 513 poll.in_flags = sock->mHandler->mPollFlags; 514 poll.out_flags = 0; 515 if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) { 516 auto newSocketIndex = mActiveList.Length(); 517 newSocketIndex = ChaosMode::randomUint32LessThan(newSocketIndex + 1); 518 mActiveList.InsertElementAt(newSocketIndex, *sock); 519 // mPollList is offset by 1 520 mPollList.InsertElementAt(newSocketIndex + 1, poll); 521 } else { 522 // Avoid refcount bump/decrease 523 mActiveList.EmplaceBack(sock->mFD, sock->mHandler.forget(), 524 sock->mPollStartEpoch); 525 mPollList.AppendElement(poll); 526 } 527 528 SOCKET_LOG( 529 (" active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length())); 530 } 531 532 void nsSocketTransportService::RemoveFromPollList(SocketContext* sock) { 533 SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList %p [handler=%p]\n", 534 sock, sock->mHandler.get())); 535 536 auto index = SockIndex(mActiveList, sock); 537 MOZ_RELEASE_ASSERT(index != -1, "invalid index"); 538 539 SOCKET_LOG((" index=%" PRId64 " mActiveList.Length()=%zu\n", index, 540 mActiveList.Length())); 541 mActiveList.UnorderedRemoveElementAt(index); 542 // mPollList is offset by 1 543 mPollList.UnorderedRemoveElementAt(index + 1); 544 545 SOCKET_LOG( 546 (" active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length())); 547 } 548 549 void nsSocketTransportService::AddToIdleList(SocketContext* sock) { 550 MOZ_ASSERT(SockIndex(mIdleList, sock) == -1, 551 "AddToIdleList Socket Already Idle"); 552 553 SOCKET_LOG(("nsSocketTransportService::AddToIdleList %p [handler=%p]\n", sock, 554 sock->mHandler.get())); 555 556 // Avoid refcount bump/decrease 557 mIdleList.EmplaceBack(sock->mFD, sock->mHandler.forget(), 558 sock->mPollStartEpoch); 559 560 SOCKET_LOG( 561 (" active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length())); 562 } 563 564 void nsSocketTransportService::RemoveFromIdleList(SocketContext* sock) { 565 SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n", 566 sock->mHandler.get())); 567 auto index = SockIndex(mIdleList, sock); 568 MOZ_RELEASE_ASSERT(index != -1); 569 mIdleList.UnorderedRemoveElementAt(index); 570 571 SOCKET_LOG( 572 (" active=%zu idle=%zu\n", mActiveList.Length(), mIdleList.Length())); 573 } 574 575 void nsSocketTransportService::MoveToIdleList(SocketContext* sock) { 576 SOCKET_LOG(("nsSocketTransportService::MoveToIdleList %p [handler=%p]\n", 577 sock, sock->mHandler.get())); 578 MOZ_ASSERT(SockIndex(mIdleList, sock) == -1); 579 MOZ_ASSERT(SockIndex(mActiveList, sock) != -1); 580 AddToIdleList(sock); 581 RemoveFromPollList(sock); 582 } 583 584 void nsSocketTransportService::MoveToPollList(SocketContext* sock) { 585 SOCKET_LOG(("nsSocketTransportService::MoveToPollList %p [handler=%p]\n", 586 sock, sock->mHandler.get())); 587 MOZ_ASSERT(SockIndex(mIdleList, sock) != -1); 588 MOZ_ASSERT(SockIndex(mActiveList, sock) == -1); 589 AddToPollList(sock); 590 RemoveFromIdleList(sock); 591 } 592 593 void nsSocketTransportService::ApplyPortRemapPreference( 594 TPortRemapping const& portRemapping) { 595 MOZ_ASSERT(IsOnCurrentThreadInfallible()); 596 597 mPortRemapping.reset(); 598 if (!portRemapping.IsEmpty()) { 599 mPortRemapping.emplace(portRemapping); 600 } 601 } 602 603 PRIntervalTime nsSocketTransportService::PollTimeout(PRIntervalTime now) { 604 if (mActiveList.IsEmpty()) { 605 return NS_SOCKET_POLL_TIMEOUT; 606 } 607 608 // compute minimum time before any socket timeout expires. 609 PRIntervalTime minR = NS_SOCKET_POLL_TIMEOUT; 610 for (uint32_t i = 0; i < mActiveList.Length(); ++i) { 611 const SocketContext& s = mActiveList[i]; 612 PRIntervalTime r = s.TimeoutIn(now); 613 if (r < minR) { 614 minR = r; 615 } 616 } 617 if (minR == NS_SOCKET_POLL_TIMEOUT) { 618 SOCKET_LOG(("poll timeout: none\n")); 619 return NS_SOCKET_POLL_TIMEOUT; 620 } 621 SOCKET_LOG(("poll timeout: %" PRIu32 "\n", PR_IntervalToSeconds(minR))); 622 return minR; 623 } 624 625 int32_t nsSocketTransportService::Poll(PRIntervalTime ts) { 626 MOZ_ASSERT(IsOnCurrentThread()); 627 PRPollDesc* firstPollEntry; 628 uint32_t pollCount; 629 PRIntervalTime pollTimeout; 630 631 // If there are pending events for this thread then 632 // DoPollIteration() should service the network without blocking. 633 bool pendingEvents = false; 634 mRawThread->HasPendingEvents(&pendingEvents); 635 636 if (mPollList[0].fd) { 637 mPollList[0].out_flags = 0; 638 firstPollEntry = &mPollList[0]; 639 pollCount = mPollList.Length(); 640 pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(ts); 641 } else { 642 // no pollable event, so busy wait... 643 pollCount = mActiveList.Length(); 644 if (pollCount) { 645 firstPollEntry = &mPollList[1]; 646 } else { 647 firstPollEntry = nullptr; 648 } 649 pollTimeout = 650 pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25); 651 } 652 653 if ((ts - mLastNetworkLinkChangeTime) < mNetworkLinkChangeBusyWaitPeriod) { 654 // Being here means we are few seconds after a network change has 655 // been detected. 656 PRIntervalTime to = mNetworkLinkChangeBusyWaitTimeout; 657 if (to) { 658 pollTimeout = std::min(to, pollTimeout); 659 SOCKET_LOG((" timeout shorthened after network change event")); 660 } 661 } 662 663 TimeStamp pollStart; 664 if (Telemetry::CanRecordPrereleaseData()) { 665 pollStart = TimeStamp::NowLoRes(); 666 } 667 668 SOCKET_LOG((" timeout = %i milliseconds\n", 669 PR_IntervalToMilliseconds(pollTimeout))); 670 671 int32_t n; 672 { 673 #ifdef MOZ_GECKO_PROFILER 674 TimeStamp startTime = TimeStamp::Now(); 675 if (pollTimeout != PR_INTERVAL_NO_WAIT) { 676 // There will be an actual non-zero wait, let the profiler know about it 677 // by marking thread as sleeping around the polling call. 678 profiler_thread_sleep(); 679 } 680 #endif 681 682 n = PR_Poll(firstPollEntry, pollCount, pollTimeout); 683 684 #ifdef MOZ_GECKO_PROFILER 685 if (pollTimeout != PR_INTERVAL_NO_WAIT) { 686 profiler_thread_wake(); 687 } 688 if (profiler_thread_is_being_profiled_for_markers()) { 689 PROFILER_MARKER_TEXT( 690 "SocketTransportService::Poll", NETWORK, 691 MarkerTiming::IntervalUntilNowFrom(startTime), 692 pollTimeout == PR_INTERVAL_NO_TIMEOUT 693 ? nsPrintfCString("Poll count: %u, Poll timeout: NO_TIMEOUT", 694 pollCount) 695 : pollTimeout == PR_INTERVAL_NO_WAIT 696 ? nsPrintfCString("Poll count: %u, Poll timeout: NO_WAIT", 697 pollCount) 698 : nsPrintfCString("Poll count: %u, Poll timeout: %ums", pollCount, 699 PR_IntervalToMilliseconds(pollTimeout))); 700 } 701 #endif 702 } 703 704 SOCKET_LOG((" ...returned after %i milliseconds\n", 705 PR_IntervalToMilliseconds(PR_IntervalNow() - ts))); 706 707 return n; 708 } 709 710 //----------------------------------------------------------------------------- 711 // xpcom api 712 713 NS_IMPL_ISUPPORTS(nsSocketTransportService, nsISocketTransportService, 714 nsIRoutedSocketTransportService, nsIEventTarget, 715 nsISerialEventTarget, nsIThreadObserver, nsIRunnable, 716 nsPISocketTransportService, nsIObserver, nsINamed, 717 nsIDirectTaskDispatcher) 718 719 static const char* gCallbackPrefs[] = { 720 SEND_BUFFER_PREF, 721 KEEPALIVE_ENABLED_PREF, 722 KEEPALIVE_IDLE_TIME_PREF, 723 KEEPALIVE_RETRY_INTERVAL_PREF, 724 KEEPALIVE_PROBE_COUNT_PREF, 725 MAX_TIME_BETWEEN_TWO_POLLS, 726 MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, 727 POLLABLE_EVENT_TIMEOUT, 728 "network.socket.forcePort", 729 nullptr, 730 }; 731 732 /* static */ 733 void nsSocketTransportService::UpdatePrefs(const char* aPref, void* aSelf) { 734 static_cast<nsSocketTransportService*>(aSelf)->UpdatePrefs(); 735 } 736 737 static uint32_t GetThreadStackSize() { 738 #ifdef XP_WIN 739 if (!StaticPrefs::network_allow_large_stack_size_for_socket_thread()) { 740 return nsIThreadManager::DEFAULT_STACK_SIZE; 741 } 742 743 const uint32_t kWindowsThreadStackSize = 512 * 1024; 744 // We can remove this custom stack size when DEFAULT_STACK_SIZE is increased. 745 static_assert(kWindowsThreadStackSize > nsIThreadManager::DEFAULT_STACK_SIZE); 746 return kWindowsThreadStackSize; 747 #else 748 return nsIThreadManager::DEFAULT_STACK_SIZE; 749 #endif 750 } 751 752 // called from main thread only 753 NS_IMETHODIMP 754 nsSocketTransportService::Init() { 755 if (!NS_IsMainThread()) { 756 NS_ERROR("wrong thread"); 757 return NS_ERROR_UNEXPECTED; 758 } 759 760 if (mInitialized) { 761 return NS_OK; 762 } 763 764 if (mShuttingDown) { 765 return NS_ERROR_UNEXPECTED; 766 } 767 768 nsCOMPtr<nsIThread> thread; 769 770 if (!XRE_IsContentProcess() || 771 StaticPrefs::network_allow_raw_sockets_in_content_processes_AtStartup()) { 772 // Since we Poll, we can't use normal LongTask support in Main Process 773 nsresult rv = NS_NewNamedThread( 774 "Socket Thread", getter_AddRefs(thread), this, 775 {GetThreadStackSize(), false, false, Some(SOCKET_THREAD_LONGTASK_MS)}); 776 NS_ENSURE_SUCCESS(rv, rv); 777 } else { 778 // In the child process, we just want a regular nsThread with no socket 779 // polling. So we don't want to run the nsSocketTransportService runnable on 780 // it. 781 nsresult rv = 782 NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), nullptr, 783 {nsIThreadManager::DEFAULT_STACK_SIZE, false, false, 784 Some(SOCKET_THREAD_LONGTASK_MS)}); 785 NS_ENSURE_SUCCESS(rv, rv); 786 787 // Set up some of the state that nsSocketTransportService::Run would set. 788 PRThread* prthread = nullptr; 789 thread->GetPRThread(&prthread); 790 gSocketThread = prthread; 791 mRawThread = thread; 792 } 793 794 { 795 MutexAutoLock lock(mLock); 796 // Install our mThread, protecting against concurrent readers 797 thread.swap(mThread); 798 mDirectTaskDispatcher = do_QueryInterface(mThread); 799 MOZ_DIAGNOSTIC_ASSERT( 800 mDirectTaskDispatcher, 801 "Underlying thread must support direct task dispatching"); 802 } 803 804 Preferences::RegisterCallbacks(UpdatePrefs, gCallbackPrefs, this); 805 UpdatePrefs(); 806 807 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 808 // Note that the observr notifications are forwarded from parent process to 809 // socket process. We have to make sure the topics registered below are also 810 // registered in nsIOService::Init(). 811 if (obsSvc) { 812 MOZ_ALWAYS_SUCCEEDS( 813 obsSvc->AddObserver(this, "last-pb-context-exited", false)); 814 MOZ_ALWAYS_SUCCEEDS( 815 obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, false)); 816 MOZ_ALWAYS_SUCCEEDS( 817 obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false)); 818 MOZ_ALWAYS_SUCCEEDS( 819 obsSvc->AddObserver(this, "xpcom-shutdown-threads", false)); 820 MOZ_ALWAYS_SUCCEEDS( 821 obsSvc->AddObserver(this, NS_NETWORK_LINK_TOPIC, false)); 822 } 823 824 // We can now dispatch tasks to the socket thread. 825 mInitialized = true; 826 return NS_OK; 827 } 828 829 // called from main thread only 830 NS_IMETHODIMP 831 nsSocketTransportService::Shutdown(bool aXpcomShutdown) { 832 SOCKET_LOG(("nsSocketTransportService::Shutdown\n")); 833 834 NS_ENSURE_STATE(NS_IsMainThread()); 835 836 if (!mInitialized || mShuttingDown) { 837 // We never inited, or shutdown has already started 838 return NS_OK; 839 } 840 841 { 842 auto observersCopy = mShutdownObservers; 843 for (auto& observer : observersCopy) { 844 observer->Observe(); 845 } 846 } 847 848 mShuttingDown = true; 849 850 { 851 MutexAutoLock lock(mLock); 852 853 if (mPollableEvent) { 854 mPollableEvent->Signal(); 855 } 856 } 857 858 // If we're shutting down due to going offline (rather than due to XPCOM 859 // shutdown), also tear down the thread. The thread will be shutdown during 860 // xpcom-shutdown-threads if during xpcom-shutdown proper. 861 if (!aXpcomShutdown) { 862 ShutdownThread(); 863 } 864 865 return NS_OK; 866 } 867 868 nsresult nsSocketTransportService::ShutdownThread() { 869 SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n")); 870 871 NS_ENSURE_STATE(NS_IsMainThread()); 872 873 if (!mInitialized) { 874 return NS_OK; 875 } 876 877 // join with thread 878 nsCOMPtr<nsIThread> thread = GetThreadSafely(); 879 thread->Shutdown(); 880 { 881 MutexAutoLock lock(mLock); 882 // Drop our reference to mThread and make sure that any concurrent readers 883 // are excluded 884 mThread = nullptr; 885 mDirectTaskDispatcher = nullptr; 886 } 887 888 Preferences::UnregisterCallbacks(UpdatePrefs, gCallbackPrefs, this); 889 890 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 891 if (obsSvc) { 892 obsSvc->RemoveObserver(this, "last-pb-context-exited"); 893 obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC); 894 obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC); 895 obsSvc->RemoveObserver(this, "xpcom-shutdown-threads"); 896 obsSvc->RemoveObserver(this, NS_NETWORK_LINK_TOPIC); 897 } 898 899 if (mAfterWakeUpTimer) { 900 mAfterWakeUpTimer->Cancel(); 901 mAfterWakeUpTimer = nullptr; 902 } 903 904 mInitialized = false; 905 mShuttingDown = false; 906 907 return NS_OK; 908 } 909 910 NS_IMETHODIMP 911 nsSocketTransportService::GetOffline(bool* offline) { 912 MutexAutoLock lock(mLock); 913 *offline = mOffline; 914 return NS_OK; 915 } 916 917 NS_IMETHODIMP 918 nsSocketTransportService::SetOffline(bool offline) { 919 MutexAutoLock lock(mLock); 920 if (!mOffline && offline) { 921 // signal the socket thread to go offline, so it will detach sockets 922 mGoingOffline = true; 923 mOffline = true; 924 } else if (mOffline && !offline) { 925 mOffline = false; 926 } 927 if (mPollableEvent) { 928 mPollableEvent->Signal(); 929 } 930 931 return NS_OK; 932 } 933 934 NS_IMETHODIMP 935 nsSocketTransportService::GetKeepaliveIdleTime(int32_t* aKeepaliveIdleTimeS) { 936 MOZ_ASSERT(aKeepaliveIdleTimeS); 937 if (NS_WARN_IF(!aKeepaliveIdleTimeS)) { 938 return NS_ERROR_NULL_POINTER; 939 } 940 *aKeepaliveIdleTimeS = mKeepaliveIdleTimeS; 941 return NS_OK; 942 } 943 944 NS_IMETHODIMP 945 nsSocketTransportService::GetKeepaliveRetryInterval( 946 int32_t* aKeepaliveRetryIntervalS) { 947 MOZ_ASSERT(aKeepaliveRetryIntervalS); 948 if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) { 949 return NS_ERROR_NULL_POINTER; 950 } 951 *aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS; 952 return NS_OK; 953 } 954 955 NS_IMETHODIMP 956 nsSocketTransportService::GetKeepaliveProbeCount( 957 int32_t* aKeepaliveProbeCount) { 958 MOZ_ASSERT(aKeepaliveProbeCount); 959 if (NS_WARN_IF(!aKeepaliveProbeCount)) { 960 return NS_ERROR_NULL_POINTER; 961 } 962 *aKeepaliveProbeCount = mKeepaliveProbeCount; 963 return NS_OK; 964 } 965 966 NS_IMETHODIMP 967 nsSocketTransportService::CreateTransport(const nsTArray<nsCString>& types, 968 const nsACString& host, int32_t port, 969 nsIProxyInfo* proxyInfo, 970 nsIDNSRecord* dnsRecord, 971 nsISocketTransport** result) { 972 return CreateRoutedTransport(types, host, port, ""_ns, 0, proxyInfo, 973 dnsRecord, result); 974 } 975 976 NS_IMETHODIMP 977 nsSocketTransportService::CreateRoutedTransport( 978 const nsTArray<nsCString>& types, const nsACString& host, int32_t port, 979 const nsACString& hostRoute, int32_t portRoute, nsIProxyInfo* proxyInfo, 980 nsIDNSRecord* dnsRecord, nsISocketTransport** result) { 981 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); 982 NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE); 983 984 RefPtr<nsSocketTransport> trans = new nsSocketTransport(); 985 nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo, 986 dnsRecord); 987 if (NS_FAILED(rv)) { 988 return rv; 989 } 990 991 trans.forget(result); 992 return NS_OK; 993 } 994 995 NS_IMETHODIMP 996 nsSocketTransportService::CreateUnixDomainTransport( 997 nsIFile* aPath, nsISocketTransport** result) { 998 #ifdef XP_UNIX 999 nsresult rv; 1000 1001 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); 1002 1003 nsAutoCString path; 1004 rv = aPath->GetNativePath(path); 1005 NS_ENSURE_SUCCESS(rv, rv); 1006 1007 RefPtr<nsSocketTransport> trans = new nsSocketTransport(); 1008 1009 rv = trans->InitWithFilename(path.get()); 1010 NS_ENSURE_SUCCESS(rv, rv); 1011 1012 trans.forget(result); 1013 return NS_OK; 1014 #else 1015 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; 1016 #endif 1017 } 1018 1019 NS_IMETHODIMP 1020 nsSocketTransportService::CreateUnixDomainAbstractAddressTransport( 1021 const nsACString& aName, nsISocketTransport** result) { 1022 // Abstract socket address is supported on Linux only 1023 #ifdef XP_LINUX 1024 RefPtr<nsSocketTransport> trans = new nsSocketTransport(); 1025 // First character of Abstract socket address is null 1026 UniquePtr<char[]> name(new char[aName.Length() + 1]); 1027 *(name.get()) = 0; 1028 memcpy(name.get() + 1, aName.BeginReading(), aName.Length()); 1029 nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1); 1030 if (NS_FAILED(rv)) { 1031 return rv; 1032 } 1033 1034 trans.forget(result); 1035 return NS_OK; 1036 #else 1037 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; 1038 #endif 1039 } 1040 1041 NS_IMETHODIMP 1042 nsSocketTransportService::OnDispatchedEvent() { 1043 #ifndef XP_WIN 1044 // On windows poll can hang and this became worse when we introduced the 1045 // patch for bug 698882 (see also bug 1292181), therefore we reverted the 1046 // behavior on windows to be as before bug 698882, e.g. write to the socket 1047 // also if an event dispatch is on the socket thread and writing to the 1048 // socket for each event. 1049 if (OnSocketThread()) { 1050 // this check is redundant to one done inside ::Signal(), but 1051 // we can do it here and skip obtaining the lock - given that 1052 // this is a relatively common occurance its worth the 1053 // redundant code 1054 SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n")); 1055 return NS_OK; 1056 } 1057 #else 1058 if (gIOService->IsNetTearingDown()) { 1059 // Poll can hang sometimes. If we are in shutdown, we are going to 1060 // start a watchdog. If we do not exit poll within 1061 // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again. 1062 StartPollWatchdog(); 1063 } 1064 #endif 1065 1066 MutexAutoLock lock(mLock); 1067 if (mPollableEvent) { 1068 mPollableEvent->Signal(); 1069 } 1070 return NS_OK; 1071 } 1072 1073 NS_IMETHODIMP 1074 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal* thread, 1075 bool mayWait) { 1076 return NS_OK; 1077 } 1078 1079 NS_IMETHODIMP 1080 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread, 1081 bool eventWasProcessed) { 1082 return NS_OK; 1083 } 1084 1085 void nsSocketTransportService::MarkTheLastElementOfPendingQueue() { 1086 mServingPendingQueue = false; 1087 } 1088 1089 NS_IMETHODIMP 1090 nsSocketTransportService::Run() { 1091 SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount)); 1092 1093 #if defined(XP_WIN) 1094 // see bug 1361495, gethostname() triggers winsock initialization. 1095 // so do it here (on parent and child) to protect against it being done first 1096 // accidentally on the main thread.. especially via PR_GetSystemInfo(). This 1097 // will also improve latency of first real winsock operation 1098 // .. 1099 // If STS-thread is no longer needed this should still be run before exiting 1100 1101 char ignoredStackBuffer[255]; 1102 (void)gethostname(ignoredStackBuffer, 255); 1103 #endif 1104 1105 psm::InitializeSSLServerCertVerificationThreads(); 1106 1107 gSocketThread = PR_GetCurrentThread(); 1108 1109 { 1110 // See bug 1843384: 1111 // Avoid blocking the main thread by allocating the PollableEvent outside 1112 // the mutex. Still has the potential to hang the socket thread, but the 1113 // main thread remains responsive. 1114 PollableEvent* pollable = new PollableEvent(); 1115 MutexAutoLock lock(mLock); 1116 mPollableEvent.reset(pollable); 1117 1118 // 1119 // NOTE: per bug 190000, this failure could be caused by Zone-Alarm 1120 // or similar software. 1121 // 1122 // NOTE: per bug 191739, this failure could also be caused by lack 1123 // of a loopback device on Windows and OS/2 platforms (it creates 1124 // a loopback socket pair on these platforms to implement a pollable 1125 // event object). if we can't create a pollable event, then we'll 1126 // have to "busy wait" to implement the socket event queue :-( 1127 // 1128 if (!mPollableEvent->Valid()) { 1129 mPollableEvent = nullptr; 1130 NS_WARNING("running socket transport thread without a pollable event"); 1131 SOCKET_LOG(("running socket transport thread without a pollable event")); 1132 } 1133 1134 PRPollDesc entry = {mPollableEvent ? mPollableEvent->PollableFD() : nullptr, 1135 PR_POLL_READ | PR_POLL_EXCEPT, 0}; 1136 SOCKET_LOG(("Setting entry 0")); 1137 mPollList[0] = entry; 1138 } 1139 1140 mRawThread = NS_GetCurrentThread(); 1141 1142 // Ensure a call to GetCurrentSerialEventTarget() returns this event target. 1143 SerialEventTargetGuard guard(this); 1144 1145 // hook ourselves up to observe event processing for this thread 1146 nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread); 1147 threadInt->SetObserver(this); 1148 1149 // make sure the pseudo random number generator is seeded on this thread 1150 srand(static_cast<unsigned>(PR_Now())); 1151 1152 // For the calculation of the duration of the last cycle (i.e. the last 1153 // for-loop iteration before shutdown). 1154 TimeStamp startOfCycleForLastCycleCalc; 1155 1156 // For measuring of the poll iteration duration without time spent blocked 1157 // in poll(). 1158 TimeStamp pollCycleStart; 1159 1160 // For calculating the time needed for a new element to run. 1161 TimeStamp startOfIteration; 1162 TimeStamp startOfNextIteration; 1163 1164 for (;;) { 1165 bool pendingEvents = false; 1166 if (Telemetry::CanRecordPrereleaseData()) { 1167 startOfCycleForLastCycleCalc = TimeStamp::NowLoRes(); 1168 startOfNextIteration = TimeStamp::NowLoRes(); 1169 } 1170 // We pop out to this loop when there are no pending events. 1171 // If we don't reset these, we may not re-enter ProcessNextEvent() 1172 // until we have events to process, and it may seem like we have 1173 // an event running for a very long time. 1174 mRawThread->SetRunningEventDelay(TimeDuration(), TimeStamp()); 1175 1176 do { 1177 if (Telemetry::CanRecordPrereleaseData()) { 1178 pollCycleStart = TimeStamp::NowLoRes(); 1179 } 1180 1181 DoPollIteration(); 1182 1183 mRawThread->HasPendingEvents(&pendingEvents); 1184 if (pendingEvents) { 1185 if (!mServingPendingQueue) { 1186 nsresult rv = Dispatch( 1187 NewRunnableMethod( 1188 "net::nsSocketTransportService::" 1189 "MarkTheLastElementOfPendingQueue", 1190 this, 1191 &nsSocketTransportService::MarkTheLastElementOfPendingQueue), 1192 nsIEventTarget::DISPATCH_NORMAL); 1193 if (NS_FAILED(rv)) { 1194 NS_WARNING( 1195 "Could not dispatch a new event on the " 1196 "socket thread."); 1197 } else { 1198 mServingPendingQueue = true; 1199 } 1200 1201 if (Telemetry::CanRecordPrereleaseData()) { 1202 startOfIteration = startOfNextIteration; 1203 // Everything that comes after this point will 1204 // be served in the next iteration. If no even 1205 // arrives, startOfNextIteration will be reset at the 1206 // beginning of each for-loop. 1207 startOfNextIteration = TimeStamp::NowLoRes(); 1208 } 1209 } 1210 TimeStamp eventQueueStart = TimeStamp::NowLoRes(); 1211 do { 1212 NS_ProcessNextEvent(mRawThread); 1213 pendingEvents = false; 1214 mRawThread->HasPendingEvents(&pendingEvents); 1215 } while (pendingEvents && mServingPendingQueue && 1216 ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() < 1217 mMaxTimePerPollIter)); 1218 } 1219 } while (pendingEvents); 1220 1221 bool goingOffline = false; 1222 // now that our event queue is empty, check to see if we should exit 1223 if (mShuttingDown) { 1224 break; 1225 } 1226 { 1227 MutexAutoLock lock(mLock); 1228 if (mGoingOffline) { 1229 mGoingOffline = false; 1230 goingOffline = true; 1231 } 1232 } 1233 // Avoid potential deadlock 1234 if (goingOffline) { 1235 Reset(true); 1236 } 1237 } 1238 1239 SOCKET_LOG(("STS shutting down thread\n")); 1240 1241 // detach all sockets, including locals 1242 Reset(false); 1243 1244 // We don't clear gSocketThread so that OnSocketThread() won't be a false 1245 // alarm for events generated by stopping the SSL threads during shutdown. 1246 psm::StopSSLServerCertVerificationThreads(); 1247 1248 // Final pass over the event queue. This makes sure that events posted by 1249 // socket detach handlers get processed. 1250 NS_ProcessPendingEvents(mRawThread); 1251 1252 SOCKET_LOG(("STS thread exit\n")); 1253 MOZ_ASSERT(mPollList.Length() == 1); 1254 MOZ_ASSERT(mActiveList.IsEmpty()); 1255 MOZ_ASSERT(mIdleList.IsEmpty()); 1256 1257 return NS_OK; 1258 } 1259 1260 void nsSocketTransportService::DetachSocketWithGuard( 1261 bool aGuardLocals, SocketContextList& socketList, int32_t index) { 1262 bool isGuarded = false; 1263 if (aGuardLocals) { 1264 socketList[index].mHandler->IsLocal(&isGuarded); 1265 if (!isGuarded) { 1266 socketList[index].mHandler->KeepWhenOffline(&isGuarded); 1267 } 1268 } 1269 if (!isGuarded) { 1270 DetachSocket(socketList, &socketList[index]); 1271 } 1272 } 1273 1274 void nsSocketTransportService::Reset(bool aGuardLocals) { 1275 // detach any sockets 1276 int32_t i; 1277 for (i = mActiveList.Length() - 1; i >= 0; --i) { 1278 DetachSocketWithGuard(aGuardLocals, mActiveList, i); 1279 } 1280 for (i = mIdleList.Length() - 1; i >= 0; --i) { 1281 DetachSocketWithGuard(aGuardLocals, mIdleList, i); 1282 } 1283 } 1284 1285 nsresult nsSocketTransportService::DoPollIteration() { 1286 SOCKET_LOG(("STS poll iter\n")); 1287 1288 PRIntervalTime now = PR_IntervalNow(); 1289 1290 // We can't have more than int32_max sockets in use 1291 int32_t i, count; 1292 // 1293 // poll loop 1294 // 1295 // walk active list backwards to see if any sockets should actually be 1296 // idle, then walk the idle list backwards to see if any idle sockets 1297 // should become active. take care to check only idle sockets that 1298 // were idle to begin with ;-) 1299 // 1300 count = mIdleList.Length(); 1301 for (i = mActiveList.Length() - 1; i >= 0; --i) { 1302 //--- 1303 SOCKET_LOG((" active [%u] { handler=%p condition=%" PRIx32 1304 " pollflags=%hu }\n", 1305 i, mActiveList[i].mHandler.get(), 1306 static_cast<uint32_t>(mActiveList[i].mHandler->mCondition), 1307 mActiveList[i].mHandler->mPollFlags)); 1308 //--- 1309 if (NS_FAILED(mActiveList[i].mHandler->mCondition)) { 1310 DetachSocket(mActiveList, &mActiveList[i]); 1311 } else { 1312 uint16_t in_flags = mActiveList[i].mHandler->mPollFlags; 1313 if (in_flags == 0) { 1314 MoveToIdleList(&mActiveList[i]); 1315 } else { 1316 // update poll flags 1317 mPollList[i + 1].in_flags = in_flags; 1318 mPollList[i + 1].out_flags = 0; 1319 mActiveList[i].EnsureTimeout(now); 1320 } 1321 } 1322 } 1323 for (i = count - 1; i >= 0; --i) { 1324 //--- 1325 SOCKET_LOG((" idle [%u] { handler=%p condition=%" PRIx32 1326 " pollflags=%hu }\n", 1327 i, mIdleList[i].mHandler.get(), 1328 static_cast<uint32_t>(mIdleList[i].mHandler->mCondition), 1329 mIdleList[i].mHandler->mPollFlags)); 1330 //--- 1331 if (NS_FAILED(mIdleList[i].mHandler->mCondition)) { 1332 DetachSocket(mIdleList, &mIdleList[i]); 1333 } else if (mIdleList[i].mHandler->mPollFlags != 0) { 1334 MoveToPollList(&mIdleList[i]); 1335 } 1336 } 1337 1338 { 1339 MutexAutoLock lock(mLock); 1340 if (mPollableEvent) { 1341 // we want to make sure the timeout is measured from the time 1342 // we enter poll(). This method resets the timestamp to 'now', 1343 // if we were first signalled between leaving poll() and here. 1344 // If we didn't do this and processing events took longer than 1345 // the allowed signal timeout, we would detect it as a 1346 // false-positive. AdjustFirstSignalTimestamp is then a no-op 1347 // until mPollableEvent->Clear() is called. 1348 mPollableEvent->AdjustFirstSignalTimestamp(); 1349 } 1350 } 1351 1352 SOCKET_LOG((" calling PR_Poll [active=%zu idle=%zu]\n", mActiveList.Length(), 1353 mIdleList.Length())); 1354 1355 // Measures seconds spent while blocked on PR_Poll 1356 int32_t n = 0; 1357 1358 if (!gIOService->IsNetTearingDown()) { 1359 // Let's not do polling during shutdown. 1360 #if defined(XP_WIN) 1361 StartPolling(); 1362 #endif 1363 n = Poll(now); 1364 #if defined(XP_WIN) 1365 EndPolling(); 1366 #endif 1367 } 1368 1369 now = PR_IntervalNow(); 1370 #ifdef MOZ_GECKO_PROFILER 1371 TimeStamp startTime; 1372 bool profiling = profiler_thread_is_being_profiled_for_markers(); 1373 if (profiling) { 1374 startTime = TimeStamp::Now(); 1375 } 1376 #endif 1377 1378 if (n < 0) { 1379 SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(), 1380 PR_GetOSError())); 1381 } else { 1382 // 1383 // service "active" sockets... 1384 // 1385 for (i = 0; i < int32_t(mActiveList.Length()); ++i) { 1386 PRPollDesc& desc = mPollList[i + 1]; 1387 SocketContext& s = mActiveList[i]; 1388 if (n > 0 && desc.out_flags != 0) { 1389 s.DisengageTimeout(); 1390 s.mHandler->OnSocketReady(desc.fd, desc.out_flags); 1391 } else if (s.IsTimedOut(now)) { 1392 SOCKET_LOG(("socket %p timed out", s.mHandler.get())); 1393 s.DisengageTimeout(); 1394 s.mHandler->OnSocketReady(desc.fd, -1); 1395 } else { 1396 s.MaybeResetEpoch(); 1397 } 1398 } 1399 // 1400 // check for "dead" sockets and remove them (need to do this in 1401 // reverse order obviously). 1402 // 1403 for (i = mActiveList.Length() - 1; i >= 0; --i) { 1404 if (NS_FAILED(mActiveList[i].mHandler->mCondition)) { 1405 DetachSocket(mActiveList, &mActiveList[i]); 1406 } 1407 } 1408 1409 { 1410 MutexAutoLock lock(mLock); 1411 // acknowledge pollable event (should not block) 1412 if (n != 0 && 1413 (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT)) && 1414 mPollableEvent && 1415 ((mPollList[0].out_flags & PR_POLL_EXCEPT) || 1416 !mPollableEvent->Clear())) { 1417 // On Windows, the TCP loopback connection in the 1418 // pollable event may become broken when a laptop 1419 // switches between wired and wireless networks or 1420 // wakes up from hibernation. We try to create a 1421 // new pollable event. If that fails, we fall back 1422 // on "busy wait". 1423 TryRepairPollableEvent(); 1424 } 1425 1426 if (mPollableEvent && 1427 !mPollableEvent->IsSignallingAlive(mPollableEventTimeout)) { 1428 SOCKET_LOG(("Pollable event signalling failed/timed out")); 1429 TryRepairPollableEvent(); 1430 } 1431 } 1432 } 1433 #ifdef MOZ_GECKO_PROFILER 1434 if (profiling) { 1435 TimeStamp endTime = TimeStamp::Now(); 1436 if ((endTime - startTime).ToMilliseconds() >= SOCKET_THREAD_LONGTASK_MS) { 1437 struct LongTaskMarker { 1438 static constexpr Span<const char> MarkerTypeName() { 1439 return MakeStringSpan("SocketThreadLongTask"); 1440 } 1441 static void StreamJSONMarkerData( 1442 baseprofiler::SpliceableJSONWriter& aWriter) { 1443 aWriter.StringProperty("category", "LongTask"); 1444 } 1445 static MarkerSchema MarkerTypeDisplay() { 1446 using MS = MarkerSchema; 1447 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable}; 1448 schema.AddKeyLabelFormat("category", "Type", MS::Format::String, 1449 MS::PayloadFlags::Searchable); 1450 return schema; 1451 } 1452 }; 1453 1454 profiler_add_marker(ProfilerString8View("LongTaskSocketProcessing"), 1455 geckoprofiler::category::OTHER, 1456 MarkerTiming::Interval(startTime, endTime), 1457 LongTaskMarker{}); 1458 } 1459 } 1460 1461 #endif 1462 1463 return NS_OK; 1464 } 1465 1466 void nsSocketTransportService::UpdateSendBufferPref() { 1467 int32_t bufferSize; 1468 1469 // If the pref is set, honor it. 0 means use OS defaults. 1470 nsresult rv = Preferences::GetInt(SEND_BUFFER_PREF, &bufferSize); 1471 if (NS_SUCCEEDED(rv)) { 1472 mSendBufferSize = bufferSize; 1473 return; 1474 } 1475 1476 #if defined(XP_WIN) 1477 mSendBufferSize = 131072 * 4; 1478 #endif 1479 } 1480 1481 nsresult nsSocketTransportService::UpdatePrefs() { 1482 mSendBufferSize = 0; 1483 1484 UpdateSendBufferPref(); 1485 1486 // Default TCP Keepalive Values. 1487 int32_t keepaliveIdleTimeS; 1488 nsresult rv = 1489 Preferences::GetInt(KEEPALIVE_IDLE_TIME_PREF, &keepaliveIdleTimeS); 1490 if (NS_SUCCEEDED(rv)) { 1491 mKeepaliveIdleTimeS = std::clamp(keepaliveIdleTimeS, 1, kMaxTCPKeepIdle); 1492 } 1493 1494 int32_t keepaliveRetryIntervalS; 1495 rv = Preferences::GetInt(KEEPALIVE_RETRY_INTERVAL_PREF, 1496 &keepaliveRetryIntervalS); 1497 if (NS_SUCCEEDED(rv)) { 1498 mKeepaliveRetryIntervalS = 1499 std::clamp(keepaliveRetryIntervalS, 1, kMaxTCPKeepIntvl); 1500 } 1501 1502 int32_t keepaliveProbeCount; 1503 rv = Preferences::GetInt(KEEPALIVE_PROBE_COUNT_PREF, &keepaliveProbeCount); 1504 if (NS_SUCCEEDED(rv)) { 1505 mKeepaliveProbeCount = std::clamp(keepaliveProbeCount, 1, kMaxTCPKeepCount); 1506 } 1507 bool keepaliveEnabled = false; 1508 rv = Preferences::GetBool(KEEPALIVE_ENABLED_PREF, &keepaliveEnabled); 1509 if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) { 1510 mKeepaliveEnabledPref = keepaliveEnabled; 1511 OnKeepaliveEnabledPrefChange(); 1512 } 1513 1514 int32_t maxTimePref; 1515 rv = Preferences::GetInt(MAX_TIME_BETWEEN_TWO_POLLS, &maxTimePref); 1516 if (NS_SUCCEEDED(rv) && maxTimePref >= 0) { 1517 mMaxTimePerPollIter = maxTimePref; 1518 } 1519 1520 int32_t pollBusyWaitPeriod; 1521 rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD, &pollBusyWaitPeriod); 1522 if (NS_SUCCEEDED(rv) && pollBusyWaitPeriod > 0) { 1523 mNetworkLinkChangeBusyWaitPeriod = PR_SecondsToInterval(pollBusyWaitPeriod); 1524 } 1525 1526 int32_t pollBusyWaitPeriodTimeout; 1527 rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD_TIMEOUT, 1528 &pollBusyWaitPeriodTimeout); 1529 if (NS_SUCCEEDED(rv) && pollBusyWaitPeriodTimeout > 0) { 1530 mNetworkLinkChangeBusyWaitTimeout = 1531 PR_SecondsToInterval(pollBusyWaitPeriodTimeout); 1532 } 1533 1534 int32_t maxTimeForPrClosePref; 1535 rv = Preferences::GetInt(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, 1536 &maxTimeForPrClosePref); 1537 if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >= 0) { 1538 mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref); 1539 } 1540 1541 int32_t pollableEventTimeout; 1542 rv = Preferences::GetInt(POLLABLE_EVENT_TIMEOUT, &pollableEventTimeout); 1543 if (NS_SUCCEEDED(rv) && pollableEventTimeout >= 0) { 1544 MutexAutoLock lock(mLock); 1545 mPollableEventTimeout = TimeDuration::FromSeconds(pollableEventTimeout); 1546 } 1547 1548 nsAutoCString portMappingPref; 1549 rv = Preferences::GetCString("network.socket.forcePort", portMappingPref); 1550 if (NS_SUCCEEDED(rv)) { 1551 bool rv = UpdatePortRemapPreference(portMappingPref); 1552 if (!rv) { 1553 NS_ERROR( 1554 "network.socket.forcePort preference is ill-formed, this will likely " 1555 "make everything unexpectedly fail!"); 1556 } 1557 } 1558 1559 return NS_OK; 1560 } 1561 1562 void nsSocketTransportService::OnKeepaliveEnabledPrefChange() { 1563 // Dispatch to socket thread if we're not executing there. 1564 if (!OnSocketThread()) { 1565 gSocketTransportService->Dispatch( 1566 NewRunnableMethod( 1567 "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this, 1568 &nsSocketTransportService::OnKeepaliveEnabledPrefChange), 1569 NS_DISPATCH_NORMAL); 1570 return; 1571 } 1572 1573 SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s", 1574 mKeepaliveEnabledPref ? "enabled" : "disabled")); 1575 1576 // Notify each socket that keepalive has been en/disabled globally. 1577 for (int32_t i = mActiveList.Length() - 1; i >= 0; --i) { 1578 NotifyKeepaliveEnabledPrefChange(&mActiveList[i]); 1579 } 1580 for (int32_t i = mIdleList.Length() - 1; i >= 0; --i) { 1581 NotifyKeepaliveEnabledPrefChange(&mIdleList[i]); 1582 } 1583 } 1584 1585 void nsSocketTransportService::NotifyKeepaliveEnabledPrefChange( 1586 SocketContext* sock) { 1587 MOZ_ASSERT(sock, "SocketContext cannot be null!"); 1588 MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!"); 1589 1590 if (!sock || !sock->mHandler) { 1591 return; 1592 } 1593 1594 sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref); 1595 } 1596 1597 NS_IMETHODIMP 1598 nsSocketTransportService::GetName(nsACString& aName) { 1599 aName.AssignLiteral("nsSocketTransportService"); 1600 return NS_OK; 1601 } 1602 1603 NS_IMETHODIMP 1604 nsSocketTransportService::Observe(nsISupports* subject, const char* topic, 1605 const char16_t* data) { 1606 SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic)); 1607 1608 if (!strcmp(topic, "last-pb-context-exited")) { 1609 nsCOMPtr<nsIRunnable> ev = NewRunnableMethod( 1610 "net::nsSocketTransportService::ClosePrivateConnections", this, 1611 &nsSocketTransportService::ClosePrivateConnections); 1612 nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL); 1613 NS_ENSURE_SUCCESS(rv, rv); 1614 } 1615 1616 if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) { 1617 nsCOMPtr<nsITimer> timer = do_QueryInterface(subject); 1618 if (timer == mAfterWakeUpTimer) { 1619 mAfterWakeUpTimer = nullptr; 1620 mSleepPhase = false; 1621 } 1622 1623 #if defined(XP_WIN) 1624 if (timer == mPollRepairTimer) { 1625 DoPollRepair(); 1626 } 1627 #endif 1628 1629 } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) { 1630 mSleepPhase = true; 1631 if (mAfterWakeUpTimer) { 1632 mAfterWakeUpTimer->Cancel(); 1633 mAfterWakeUpTimer = nullptr; 1634 } 1635 } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) { 1636 if (mSleepPhase && !mAfterWakeUpTimer) { 1637 NS_NewTimerWithObserver(getter_AddRefs(mAfterWakeUpTimer), this, 2000, 1638 nsITimer::TYPE_ONE_SHOT); 1639 } 1640 } else if (!strcmp(topic, "xpcom-shutdown-threads")) { 1641 ShutdownThread(); 1642 } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) { 1643 mLastNetworkLinkChangeTime = PR_IntervalNow(); 1644 } 1645 1646 return NS_OK; 1647 } 1648 1649 void nsSocketTransportService::ClosePrivateConnections() { 1650 MOZ_ASSERT(IsOnCurrentThread(), "Must be called on the socket thread"); 1651 1652 for (int32_t i = mActiveList.Length() - 1; i >= 0; --i) { 1653 if (mActiveList[i].mHandler->mIsPrivate) { 1654 DetachSocket(mActiveList, &mActiveList[i]); 1655 } 1656 } 1657 for (int32_t i = mIdleList.Length() - 1; i >= 0; --i) { 1658 if (mIdleList[i].mHandler->mIsPrivate) { 1659 DetachSocket(mIdleList, &mIdleList[i]); 1660 } 1661 } 1662 } 1663 1664 NS_IMETHODIMP 1665 nsSocketTransportService::GetSendBufferSize(int32_t* value) { 1666 *value = mSendBufferSize; 1667 return NS_OK; 1668 } 1669 1670 /// ugly OS specific includes are placed at the bottom of the src for clarity 1671 1672 #if defined(XP_WIN) 1673 # include <windows.h> 1674 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) 1675 # include <sys/resource.h> 1676 #endif 1677 1678 PRStatus nsSocketTransportService::DiscoverMaxCount() { 1679 gMaxCount = SOCKET_LIMIT_MIN; 1680 1681 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX) 1682 // On unix and os x network sockets and file 1683 // descriptors are the same. OS X comes defaulted at 256, 1684 // most linux at 1000. We can reliably use [sg]rlimit to 1685 // query that and raise it if needed. 1686 1687 struct rlimit rlimitData{}; 1688 if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) { // rlimit broken - use min 1689 return PR_SUCCESS; 1690 } 1691 1692 if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target! 1693 gMaxCount = SOCKET_LIMIT_TARGET; 1694 return PR_SUCCESS; 1695 } 1696 1697 int32_t maxallowed = rlimitData.rlim_max; 1698 if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) { 1699 return PR_SUCCESS; // so small treat as if rlimit is broken 1700 } 1701 1702 if ((maxallowed == -1) || // no hard cap - ok to set target 1703 ((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) { 1704 maxallowed = SOCKET_LIMIT_TARGET; 1705 } 1706 1707 rlimitData.rlim_cur = maxallowed; 1708 setrlimit(RLIMIT_NOFILE, &rlimitData); 1709 if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) && 1710 (rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) { 1711 gMaxCount = rlimitData.rlim_cur; 1712 } 1713 1714 #elif defined(XP_WIN) && !defined(WIN_CE) 1715 // >= XP is confirmed to have at least 1000 1716 static_assert(SOCKET_LIMIT_TARGET <= 1000, 1717 "SOCKET_LIMIT_TARGET max value is 1000"); 1718 gMaxCount = SOCKET_LIMIT_TARGET; 1719 #else 1720 // other platforms are harder to test - so leave at safe legacy value 1721 #endif 1722 1723 return PR_SUCCESS; 1724 } 1725 1726 // Used to return connection info to Dashboard.cpp 1727 void nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo>* data, 1728 SocketContext* context, 1729 bool aActive) { 1730 if (context->mHandler->mIsPrivate) { 1731 return; 1732 } 1733 PRFileDesc* aFD = context->mFD; 1734 1735 PRFileDesc* idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER); 1736 1737 NS_ENSURE_TRUE_VOID(idLayer); 1738 1739 PRDescType type = PR_GetDescType(idLayer); 1740 char host[64] = {0}; 1741 uint16_t port; 1742 const char* type_desc; 1743 1744 if (type == PR_DESC_SOCKET_TCP) { 1745 type_desc = "TCP"; 1746 PRNetAddr peer_addr; 1747 PodZero(&peer_addr); 1748 1749 PRStatus rv = PR_GetPeerName(aFD, &peer_addr); 1750 if (rv != PR_SUCCESS) { 1751 return; 1752 } 1753 1754 rv = PR_NetAddrToString(&peer_addr, host, sizeof(host)); 1755 if (rv != PR_SUCCESS) { 1756 return; 1757 } 1758 1759 if (peer_addr.raw.family == PR_AF_INET) { 1760 port = peer_addr.inet.port; 1761 } else { 1762 port = peer_addr.ipv6.port; 1763 } 1764 port = PR_ntohs(port); 1765 } else { 1766 if (type == PR_DESC_SOCKET_UDP) { 1767 type_desc = "UDP"; 1768 } else { 1769 type_desc = "other"; 1770 } 1771 NetAddr addr; 1772 if (context->mHandler->GetRemoteAddr(&addr) != NS_OK) { 1773 return; 1774 } 1775 if (!addr.ToStringBuffer(host, sizeof(host))) { 1776 return; 1777 } 1778 if (addr.GetPort(&port) != NS_OK) { 1779 return; 1780 } 1781 } 1782 1783 uint64_t sent = context->mHandler->ByteCountSent(); 1784 uint64_t received = context->mHandler->ByteCountReceived(); 1785 SocketInfo info = {nsCString(host), sent, received, port, aActive, 1786 nsCString(type_desc)}; 1787 1788 data->AppendElement(info); 1789 } 1790 1791 void nsSocketTransportService::GetSocketConnections( 1792 nsTArray<SocketInfo>* data) { 1793 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1794 for (uint32_t i = 0; i < mActiveList.Length(); i++) { 1795 AnalyzeConnection(data, &mActiveList[i], true); 1796 } 1797 for (uint32_t i = 0; i < mIdleList.Length(); i++) { 1798 AnalyzeConnection(data, &mIdleList[i], false); 1799 } 1800 } 1801 1802 bool nsSocketTransportService::IsTelemetryEnabledAndNotSleepPhase() { 1803 return Telemetry::CanRecordPrereleaseData() && !mSleepPhase; 1804 } 1805 1806 #if defined(XP_WIN) 1807 void nsSocketTransportService::StartPollWatchdog() { 1808 // Start off the timer from a runnable off of the main thread in order to 1809 // avoid a deadlock, see bug 1370448. 1810 RefPtr<nsSocketTransportService> self(this); 1811 NS_DispatchToMainThread(NS_NewRunnableFunction( 1812 "nsSocketTransportService::StartPollWatchdog", [self] { 1813 MutexAutoLock lock(self->mLock); 1814 1815 // Poll can hang sometimes. If we are in shutdown, we are going to start 1816 // a watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME 1817 // signal a pollable event again. 1818 if (gIOService->IsNetTearingDown() && self->mPolling && 1819 !self->mPollRepairTimer) { 1820 NS_NewTimerWithObserver(getter_AddRefs(self->mPollRepairTimer), self, 1821 REPAIR_POLLABLE_EVENT_TIME, 1822 nsITimer::TYPE_REPEATING_SLACK); 1823 } 1824 })); 1825 } 1826 1827 void nsSocketTransportService::DoPollRepair() { 1828 MutexAutoLock lock(mLock); 1829 if (mPolling && mPollableEvent) { 1830 mPollableEvent->Signal(); 1831 } else if (mPollRepairTimer) { 1832 mPollRepairTimer->Cancel(); 1833 } 1834 } 1835 1836 void nsSocketTransportService::StartPolling() { 1837 MutexAutoLock lock(mLock); 1838 mPolling = true; 1839 } 1840 1841 void nsSocketTransportService::EndPolling() { 1842 MutexAutoLock lock(mLock); 1843 mPolling = false; 1844 if (mPollRepairTimer) { 1845 mPollRepairTimer->Cancel(); 1846 } 1847 } 1848 1849 #endif 1850 1851 void nsSocketTransportService::TryRepairPollableEvent() MOZ_REQUIRES(mLock) { 1852 mLock.AssertCurrentThreadOwns(); 1853 1854 PollableEvent* pollable = nullptr; 1855 { 1856 // Bug 1719046: In certain cases PollableEvent constructor can hang 1857 // when callign PR_NewTCPSocketPair. 1858 // We unlock the mutex to prevent main thread hangs acquiring the lock. 1859 MutexAutoUnlock unlock(mLock); 1860 pollable = new PollableEvent(); 1861 } 1862 1863 NS_WARNING("Trying to repair mPollableEvent"); 1864 mPollableEvent.reset(pollable); 1865 if (!mPollableEvent->Valid()) { 1866 mPollableEvent = nullptr; 1867 } 1868 SOCKET_LOG( 1869 ("running socket transport thread without " 1870 "a pollable event now valid=%d", 1871 !!mPollableEvent)); 1872 mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; 1873 mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; 1874 mPollList[0].out_flags = 0; 1875 } 1876 1877 NS_IMETHODIMP 1878 nsSocketTransportService::AddShutdownObserver( 1879 nsISTSShutdownObserver* aObserver) { 1880 mShutdownObservers.AppendElement(aObserver); 1881 return NS_OK; 1882 } 1883 1884 NS_IMETHODIMP 1885 nsSocketTransportService::RemoveShutdownObserver( 1886 nsISTSShutdownObserver* aObserver) { 1887 mShutdownObservers.RemoveElement(aObserver); 1888 return NS_OK; 1889 } 1890 1891 } // namespace net 1892 } // namespace mozilla