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/. */
6 #include "nsSocketTransportService2.h"
8 #include "IOActivityMonitor.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/ChaosMode.h"
11 #include "mozilla/IntegerPrintfMacros.h"
12 #include "mozilla/Likely.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"
26 #include "nsINetworkLinkService.h"
27 #include "nsIOService.h"
28 #include "nsIObserverService.h"
29 #include "nsIWidget.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsSocketTransport2.h"
32 #include "nsThreadUtils.h"
39 LazyLogModule
gSocketTransportLog("nsSocketTransport");
40 LazyLogModule
gUDPSocketLog("UDPSocket");
41 LazyLogModule
gTCPSocketLog("TCPSocket");
43 nsSocketTransportService
* gSocketTransportService
= nullptr;
44 static Atomic
<PRThread
*, Relaxed
> gSocketThread(nullptr);
46 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
47 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
48 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
49 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
50 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
51 #define SOCKET_LIMIT_TARGET 1000U
52 #define MAX_TIME_BETWEEN_TWO_POLLS \
53 "network.sts.max_time_for_events_between_two_polls"
54 #define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period"
55 #define POLL_BUSY_WAIT_PERIOD_TIMEOUT \
56 "network.sts.poll_busy_wait_period_timeout"
57 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN \
58 "network.sts.max_time_for_pr_close_during_shutdown"
59 #define POLLABLE_EVENT_TIMEOUT "network.sts.pollable_event_timeout"
61 #define REPAIR_POLLABLE_EVENT_TIME 10
63 uint32_t nsSocketTransportService::gMaxCount
;
64 PRCallOnceType
nsSocketTransportService::gMaxCountInitOnce
;
67 bool OnSocketThread() { return PR_GetCurrentThread() == gSocketThread
; }
69 //-----------------------------------------------------------------------------
71 bool nsSocketTransportService::SocketContext::IsTimedOut(
72 PRIntervalTime now
) const {
73 return TimeoutIn(now
) == 0;
76 void nsSocketTransportService::SocketContext::EnsureTimeout(
78 SOCKET_LOG(("SocketContext::EnsureTimeout socket=%p", mHandler
.get()));
79 if (!mPollStartEpoch
) {
80 SOCKET_LOG((" engaging"));
81 mPollStartEpoch
= now
;
85 void nsSocketTransportService::SocketContext::DisengageTimeout() {
86 SOCKET_LOG(("SocketContext::DisengageTimeout socket=%p", mHandler
.get()));
90 PRIntervalTime
nsSocketTransportService::SocketContext::TimeoutIn(
91 PRIntervalTime now
) const {
92 SOCKET_LOG(("SocketContext::TimeoutIn socket=%p, timeout=%us", mHandler
.get(),
93 mHandler
->mPollTimeout
));
95 if (mHandler
->mPollTimeout
== UINT16_MAX
|| !mPollStartEpoch
) {
96 SOCKET_LOG((" not engaged"));
97 return NS_SOCKET_POLL_TIMEOUT
;
100 PRIntervalTime elapsed
= (now
- mPollStartEpoch
);
101 PRIntervalTime timeout
= PR_SecondsToInterval(mHandler
->mPollTimeout
);
103 if (elapsed
>= timeout
) {
104 SOCKET_LOG((" timed out!"));
107 SOCKET_LOG((" remains %us", PR_IntervalToSeconds(timeout
- elapsed
)));
108 return timeout
- elapsed
;
111 void nsSocketTransportService::SocketContext::MaybeResetEpoch() {
112 if (mPollStartEpoch
&& mHandler
->mPollTimeout
== UINT16_MAX
) {
117 //-----------------------------------------------------------------------------
118 // ctor/dtor (called on the main/UI thread by the service manager)
120 nsSocketTransportService::nsSocketTransportService()
121 : mPollableEventTimeout(TimeDuration::FromSeconds(6)),
122 mMaxTimeForPrClosePref(PR_SecondsToInterval(5)),
123 mNetworkLinkChangeBusyWaitPeriod(PR_SecondsToInterval(50)),
124 mNetworkLinkChangeBusyWaitTimeout(PR_SecondsToInterval(7)) {
125 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
127 PR_CallOnce(&gMaxCountInitOnce
, DiscoverMaxCount
);
129 NS_ASSERTION(!gSocketTransportService
, "must not instantiate twice");
130 gSocketTransportService
= this;
132 // The Poll list always has an entry at [0]. The rest of the
133 // list is a duplicate of the Active list's PRFileDesc file descriptors.
134 PRPollDesc entry
= {nullptr, PR_POLL_READ
| PR_POLL_EXCEPT
, 0};
135 mPollList
.InsertElementAt(0, entry
);
138 void nsSocketTransportService::ApplyPortRemap(uint16_t* aPort
) {
139 MOZ_ASSERT(IsOnCurrentThreadInfallible());
141 if (!mPortRemapping
) {
145 // Reverse the array to make later rules override earlier rules.
146 for (auto const& portMapping
: Reversed(*mPortRemapping
)) {
147 if (*aPort
< std::get
<0>(portMapping
)) {
150 if (*aPort
> std::get
<1>(portMapping
)) {
154 *aPort
= std::get
<2>(portMapping
);
159 bool nsSocketTransportService::UpdatePortRemapPreference(
160 nsACString
const& aPortMappingPref
) {
161 TPortRemapping portRemapping
;
163 auto consumePreference
= [&]() -> bool {
164 Tokenizer
tokenizer(aPortMappingPref
);
166 tokenizer
.SkipWhites();
167 if (tokenizer
.CheckEOF()) {
171 nsTArray
<std::tuple
<uint16_t, uint16_t>> ranges(2);
174 tokenizer
.SkipWhites();
175 if (!tokenizer
.ReadInteger(&loPort
)) {
180 tokenizer
.SkipWhites();
181 if (tokenizer
.CheckChar('-')) {
182 tokenizer
.SkipWhites();
183 if (!tokenizer
.ReadInteger(&hiPort
)) {
190 ranges
.AppendElement(std::make_tuple(loPort
, hiPort
));
192 tokenizer
.SkipWhites();
193 if (tokenizer
.CheckChar(',')) {
194 continue; // another port or port range is expected
197 if (tokenizer
.CheckChar('=')) {
199 tokenizer
.SkipWhites();
200 if (!tokenizer
.ReadInteger(&targetPort
)) {
204 // Storing reversed, because the most common cases (like 443) will very
205 // likely be listed as first, less common cases will be added to the end
206 // of the list mapping to the same port. As we iterate the whole
207 // remapping array from the end, this may have a small perf win by
208 // hitting the most common cases earlier.
209 for (auto const& range
: Reversed(ranges
)) {
210 portRemapping
.AppendElement(std::make_tuple(
211 std::get
<0>(range
), std::get
<1>(range
), targetPort
));
215 tokenizer
.SkipWhites();
216 if (tokenizer
.CheckChar(';')) {
217 continue; // more mappings (or EOF) expected
219 if (tokenizer
.CheckEOF()) {
224 // Anything else is unexpected.
228 // 'break' from the parsing loop means ill-formed preference
229 portRemapping
.Clear();
233 bool rv
= consumePreference();
235 if (!IsOnCurrentThread()) {
236 nsCOMPtr
<nsIThread
> thread
= GetThreadSafely();
238 // Init hasn't been called yet. Could probably just assert.
239 // If shutdown, the dispatch below will just silently fail.
240 NS_ASSERTION(false, "ApplyPortRemapPreference before STS::Init");
243 thread
->Dispatch(NewRunnableMethod
<TPortRemapping
>(
244 "net::ApplyPortRemapping", this,
245 &nsSocketTransportService::ApplyPortRemapPreference
, portRemapping
));
247 ApplyPortRemapPreference(portRemapping
);
253 nsSocketTransportService::~nsSocketTransportService() {
254 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
255 NS_ASSERTION(!mInitialized
, "not shutdown properly");
257 gSocketTransportService
= nullptr;
260 //-----------------------------------------------------------------------------
261 // event queue (any thread)
263 already_AddRefed
<nsIThread
> nsSocketTransportService::GetThreadSafely() {
264 MutexAutoLock
lock(mLock
);
265 nsCOMPtr
<nsIThread
> result
= mThread
;
266 return result
.forget();
270 nsSocketTransportService::DispatchFromScript(nsIRunnable
* event
,
272 nsCOMPtr
<nsIRunnable
> event_ref(event
);
273 return Dispatch(event_ref
.forget(), flags
);
277 nsSocketTransportService::Dispatch(already_AddRefed
<nsIRunnable
> event
,
279 nsCOMPtr
<nsIRunnable
> event_ref(event
);
280 SOCKET_LOG(("STS dispatch [%p]\n", event_ref
.get()));
282 nsCOMPtr
<nsIThread
> thread
= GetThreadSafely();
284 rv
= thread
? thread
->Dispatch(event_ref
.forget(), flags
)
285 : NS_ERROR_NOT_INITIALIZED
;
286 if (rv
== NS_ERROR_UNEXPECTED
) {
287 // Thread is no longer accepting events. We must have just shut it
288 // down on the main thread. Pretend we never saw it.
289 rv
= NS_ERROR_NOT_INITIALIZED
;
295 nsSocketTransportService::DelayedDispatch(already_AddRefed
<nsIRunnable
>,
297 return NS_ERROR_NOT_IMPLEMENTED
;
301 nsSocketTransportService::RegisterShutdownTask(nsITargetShutdownTask
* task
) {
302 nsCOMPtr
<nsIThread
> thread
= GetThreadSafely();
303 return thread
? thread
->RegisterShutdownTask(task
) : NS_ERROR_UNEXPECTED
;
307 nsSocketTransportService::UnregisterShutdownTask(nsITargetShutdownTask
* task
) {
308 nsCOMPtr
<nsIThread
> thread
= GetThreadSafely();
309 return thread
? thread
->UnregisterShutdownTask(task
) : NS_ERROR_UNEXPECTED
;
313 nsSocketTransportService::IsOnCurrentThread(bool* result
) {
314 *result
= OnSocketThread();
319 nsSocketTransportService::IsOnCurrentThreadInfallible() {
320 return OnSocketThread();
323 //-----------------------------------------------------------------------------
324 // nsIDirectTaskDispatcher
326 already_AddRefed
<nsIDirectTaskDispatcher
>
327 nsSocketTransportService::GetDirectTaskDispatcherSafely() {
328 MutexAutoLock
lock(mLock
);
329 nsCOMPtr
<nsIDirectTaskDispatcher
> result
= mDirectTaskDispatcher
;
330 return result
.forget();
334 nsSocketTransportService::DispatchDirectTask(
335 already_AddRefed
<nsIRunnable
> aEvent
) {
336 nsCOMPtr
<nsIDirectTaskDispatcher
> dispatcher
=
337 GetDirectTaskDispatcherSafely();
338 NS_ENSURE_TRUE(dispatcher
, NS_ERROR_NOT_INITIALIZED
);
339 return dispatcher
->DispatchDirectTask(std::move(aEvent
));
342 NS_IMETHODIMP
nsSocketTransportService::DrainDirectTasks() {
343 nsCOMPtr
<nsIDirectTaskDispatcher
> dispatcher
=
344 GetDirectTaskDispatcherSafely();
349 return dispatcher
->DrainDirectTasks();
352 NS_IMETHODIMP
nsSocketTransportService::HaveDirectTasks(bool* aValue
) {
353 nsCOMPtr
<nsIDirectTaskDispatcher
> dispatcher
=
354 GetDirectTaskDispatcherSafely();
359 return dispatcher
->HaveDirectTasks(aValue
);
362 //-----------------------------------------------------------------------------
363 // socket api (socket thread only)
366 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable
* event
) {
367 SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
369 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
371 if (CanAttachSocket()) {
372 return Dispatch(event
, NS_DISPATCH_NORMAL
);
375 auto* runnable
= new LinkedRunnableEvent(event
);
376 mPendingSocketQueue
.insertBack(runnable
);
381 nsSocketTransportService::AttachSocket(PRFileDesc
* fd
,
382 nsASocketHandler
* handler
) {
384 ("nsSocketTransportService::AttachSocket [handler=%p]\n", handler
));
386 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
388 if (!CanAttachSocket()) {
389 return NS_ERROR_NOT_AVAILABLE
;
392 SocketContext sock
{fd
, handler
, 0};
394 AddToIdleList(&sock
);
398 // the number of sockets that can be attached at any given time is
399 // limited. this is done because some operating systems (e.g., Win9x)
400 // limit the number of sockets that can be created by an application.
401 // AttachSocket will fail if the limit is exceeded. consumers should
402 // call CanAttachSocket and check the result before creating a socket.
404 bool nsSocketTransportService::CanAttachSocket() {
405 static bool reported900FDLimit
= false;
407 MOZ_ASSERT(!mShuttingDown
);
408 uint32_t total
= mActiveList
.Length() + mIdleList
.Length();
409 bool rv
= total
< gMaxCount
;
411 if (Telemetry::CanRecordPrereleaseData() &&
412 (((total
>= 900) || !rv
) && !reported900FDLimit
)) {
413 reported900FDLimit
= true;
414 Telemetry::Accumulate(Telemetry::NETWORK_SESSION_AT_900FD
, true);
416 MOZ_ASSERT(mInitialized
);
420 nsresult
nsSocketTransportService::DetachSocket(SocketContextList
& listHead
,
421 SocketContext
* sock
) {
422 SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%p]\n",
423 sock
->mHandler
.get()));
424 MOZ_ASSERT((&listHead
== &mActiveList
) || (&listHead
== &mIdleList
),
425 "DetachSocket invalid head");
428 // inform the handler that this socket is going away
429 sock
->mHandler
->OnSocketDetached(sock
->mFD
);
431 mSentBytesCount
+= sock
->mHandler
->ByteCountSent();
432 mReceivedBytesCount
+= sock
->mHandler
->ByteCountReceived();
437 if (&listHead
== &mActiveList
) {
438 RemoveFromPollList(sock
);
440 RemoveFromIdleList(sock
);
443 // NOTE: sock is now an invalid pointer
446 // notify the first element on the pending socket queue...
448 nsCOMPtr
<nsIRunnable
> event
;
449 LinkedRunnableEvent
* runnable
= mPendingSocketQueue
.getFirst();
451 event
= runnable
->TakeEvent();
456 // move event from pending queue to dispatch queue
457 return Dispatch(event
, NS_DISPATCH_NORMAL
);
462 // Returns the index of a SocketContext within a list, or -1 if it's
463 // not a pointer to a list element
464 // NOTE: this could be supplied by nsTArray<>
465 int64_t nsSocketTransportService::SockIndex(SocketContextList
& aList
,
466 SocketContext
* aSock
) {
467 ptrdiff_t index
= -1;
468 if (!aList
.IsEmpty()) {
469 index
= aSock
- &aList
[0];
470 if (index
< 0 || (size_t)index
+ 1 > aList
.Length()) {
474 return (int64_t)index
;
477 void nsSocketTransportService::AddToPollList(SocketContext
* sock
) {
478 MOZ_ASSERT(SockIndex(mActiveList
, sock
) == -1,
479 "AddToPollList Socket Already Active");
481 SOCKET_LOG(("nsSocketTransportService::AddToPollList %p [handler=%p]\n", sock
,
482 sock
->mHandler
.get()));
484 sock
->EnsureTimeout(PR_IntervalNow());
487 poll
.in_flags
= sock
->mHandler
->mPollFlags
;
489 if (ChaosMode::isActive(ChaosFeature::NetworkScheduling
)) {
490 auto newSocketIndex
= mActiveList
.Length();
491 newSocketIndex
= ChaosMode::randomUint32LessThan(newSocketIndex
+ 1);
492 mActiveList
.InsertElementAt(newSocketIndex
, *sock
);
493 // mPollList is offset by 1
494 mPollList
.InsertElementAt(newSocketIndex
+ 1, poll
);
496 // Avoid refcount bump/decrease
497 mActiveList
.EmplaceBack(sock
->mFD
, sock
->mHandler
.forget(),
498 sock
->mPollStartEpoch
);
499 mPollList
.AppendElement(poll
);
503 (" active=%zu idle=%zu\n", mActiveList
.Length(), mIdleList
.Length()));
506 void nsSocketTransportService::RemoveFromPollList(SocketContext
* sock
) {
507 SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList %p [handler=%p]\n",
508 sock
, sock
->mHandler
.get()));
510 auto index
= SockIndex(mActiveList
, sock
);
511 MOZ_RELEASE_ASSERT(index
!= -1, "invalid index");
513 SOCKET_LOG((" index=%" PRId64
" mActiveList.Length()=%zu\n", index
,
514 mActiveList
.Length()));
515 mActiveList
.UnorderedRemoveElementAt(index
);
516 // mPollList is offset by 1
517 mPollList
.UnorderedRemoveElementAt(index
+ 1);
520 (" active=%zu idle=%zu\n", mActiveList
.Length(), mIdleList
.Length()));
523 void nsSocketTransportService::AddToIdleList(SocketContext
* sock
) {
524 MOZ_ASSERT(SockIndex(mIdleList
, sock
) == -1,
525 "AddToIdleList Socket Already Idle");
527 SOCKET_LOG(("nsSocketTransportService::AddToIdleList %p [handler=%p]\n", sock
,
528 sock
->mHandler
.get()));
530 // Avoid refcount bump/decrease
531 mIdleList
.EmplaceBack(sock
->mFD
, sock
->mHandler
.forget(),
532 sock
->mPollStartEpoch
);
535 (" active=%zu idle=%zu\n", mActiveList
.Length(), mIdleList
.Length()));
538 void nsSocketTransportService::RemoveFromIdleList(SocketContext
* sock
) {
539 SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%p]\n",
540 sock
->mHandler
.get()));
541 auto index
= SockIndex(mIdleList
, sock
);
542 MOZ_RELEASE_ASSERT(index
!= -1);
543 mIdleList
.UnorderedRemoveElementAt(index
);
546 (" active=%zu idle=%zu\n", mActiveList
.Length(), mIdleList
.Length()));
549 void nsSocketTransportService::MoveToIdleList(SocketContext
* sock
) {
550 SOCKET_LOG(("nsSocketTransportService::MoveToIdleList %p [handler=%p]\n",
551 sock
, sock
->mHandler
.get()));
552 MOZ_ASSERT(SockIndex(mIdleList
, sock
) == -1);
553 MOZ_ASSERT(SockIndex(mActiveList
, sock
) != -1);
555 RemoveFromPollList(sock
);
558 void nsSocketTransportService::MoveToPollList(SocketContext
* sock
) {
559 SOCKET_LOG(("nsSocketTransportService::MoveToPollList %p [handler=%p]\n",
560 sock
, sock
->mHandler
.get()));
561 MOZ_ASSERT(SockIndex(mIdleList
, sock
) != -1);
562 MOZ_ASSERT(SockIndex(mActiveList
, sock
) == -1);
564 RemoveFromIdleList(sock
);
567 void nsSocketTransportService::ApplyPortRemapPreference(
568 TPortRemapping
const& portRemapping
) {
569 MOZ_ASSERT(IsOnCurrentThreadInfallible());
571 mPortRemapping
.reset();
572 if (!portRemapping
.IsEmpty()) {
573 mPortRemapping
.emplace(portRemapping
);
577 PRIntervalTime
nsSocketTransportService::PollTimeout(PRIntervalTime now
) {
578 if (mActiveList
.IsEmpty()) {
579 return NS_SOCKET_POLL_TIMEOUT
;
582 // compute minimum time before any socket timeout expires.
583 PRIntervalTime minR
= NS_SOCKET_POLL_TIMEOUT
;
584 for (uint32_t i
= 0; i
< mActiveList
.Length(); ++i
) {
585 const SocketContext
& s
= mActiveList
[i
];
586 PRIntervalTime r
= s
.TimeoutIn(now
);
591 if (minR
== NS_SOCKET_POLL_TIMEOUT
) {
592 SOCKET_LOG(("poll timeout: none\n"));
593 return NS_SOCKET_POLL_TIMEOUT
;
595 SOCKET_LOG(("poll timeout: %" PRIu32
"\n", PR_IntervalToSeconds(minR
)));
599 int32_t nsSocketTransportService::Poll(TimeDuration
* pollDuration
,
601 MOZ_ASSERT(IsOnCurrentThread());
602 PRPollDesc
* firstPollEntry
;
604 PRIntervalTime pollTimeout
;
605 *pollDuration
= nullptr;
607 // If there are pending events for this thread then
608 // DoPollIteration() should service the network without blocking.
609 bool pendingEvents
= false;
610 mRawThread
->HasPendingEvents(&pendingEvents
);
612 if (mPollList
[0].fd
) {
613 mPollList
[0].out_flags
= 0;
614 firstPollEntry
= &mPollList
[0];
615 pollCount
= mPollList
.Length();
616 pollTimeout
= pendingEvents
? PR_INTERVAL_NO_WAIT
: PollTimeout(ts
);
618 // no pollable event, so busy wait...
619 pollCount
= mActiveList
.Length();
621 firstPollEntry
= &mPollList
[1];
623 firstPollEntry
= nullptr;
626 pendingEvents
? PR_INTERVAL_NO_WAIT
: PR_MillisecondsToInterval(25);
629 if ((ts
- mLastNetworkLinkChangeTime
) < mNetworkLinkChangeBusyWaitPeriod
) {
630 // Being here means we are few seconds after a network change has
632 PRIntervalTime to
= mNetworkLinkChangeBusyWaitTimeout
;
634 pollTimeout
= std::min(to
, pollTimeout
);
635 SOCKET_LOG((" timeout shorthened after network change event"));
640 if (Telemetry::CanRecordPrereleaseData()) {
641 pollStart
= TimeStamp::NowLoRes();
644 SOCKET_LOG((" timeout = %i milliseconds\n",
645 PR_IntervalToMilliseconds(pollTimeout
)));
649 #ifdef MOZ_GECKO_PROFILER
650 TimeStamp startTime
= TimeStamp::Now();
651 if (pollTimeout
!= PR_INTERVAL_NO_WAIT
) {
652 // There will be an actual non-zero wait, let the profiler know about it
653 // by marking thread as sleeping around the polling call.
654 profiler_thread_sleep();
658 rv
= PR_Poll(firstPollEntry
, pollCount
, pollTimeout
);
660 #ifdef MOZ_GECKO_PROFILER
661 if (pollTimeout
!= PR_INTERVAL_NO_WAIT
) {
662 profiler_thread_wake();
664 if (profiler_thread_is_being_profiled_for_markers()) {
665 PROFILER_MARKER_TEXT(
666 "SocketTransportService::Poll", NETWORK
,
667 MarkerTiming::IntervalUntilNowFrom(startTime
),
668 pollTimeout
== PR_INTERVAL_NO_TIMEOUT
669 ? nsPrintfCString("Poll count: %u, Poll timeout: NO_TIMEOUT",
671 : pollTimeout
== PR_INTERVAL_NO_WAIT
672 ? nsPrintfCString("Poll count: %u, Poll timeout: NO_WAIT",
674 : nsPrintfCString("Poll count: %u, Poll timeout: %ums", pollCount
,
675 PR_IntervalToMilliseconds(pollTimeout
)));
680 if (Telemetry::CanRecordPrereleaseData() && !pollStart
.IsNull()) {
681 *pollDuration
= TimeStamp::NowLoRes() - pollStart
;
684 SOCKET_LOG((" ...returned after %i milliseconds\n",
685 PR_IntervalToMilliseconds(PR_IntervalNow() - ts
)));
690 //-----------------------------------------------------------------------------
693 NS_IMPL_ISUPPORTS(nsSocketTransportService
, nsISocketTransportService
,
694 nsIRoutedSocketTransportService
, nsIEventTarget
,
695 nsISerialEventTarget
, nsIThreadObserver
, nsIRunnable
,
696 nsPISocketTransportService
, nsIObserver
, nsINamed
,
697 nsIDirectTaskDispatcher
)
699 static const char* gCallbackPrefs
[] = {
701 KEEPALIVE_ENABLED_PREF
,
702 KEEPALIVE_IDLE_TIME_PREF
,
703 KEEPALIVE_RETRY_INTERVAL_PREF
,
704 KEEPALIVE_PROBE_COUNT_PREF
,
705 MAX_TIME_BETWEEN_TWO_POLLS
,
706 MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN
,
707 POLLABLE_EVENT_TIMEOUT
,
708 "network.socket.forcePort",
713 void nsSocketTransportService::UpdatePrefs(const char* aPref
, void* aSelf
) {
714 static_cast<nsSocketTransportService
*>(aSelf
)->UpdatePrefs();
717 static uint32_t GetThreadStackSize() {
719 if (!StaticPrefs::network_allow_large_stack_size_for_socket_thread()) {
720 return nsIThreadManager::DEFAULT_STACK_SIZE
;
723 const uint32_t kWindowsThreadStackSize
= 512 * 1024;
724 // We can remove this custom stack size when DEFAULT_STACK_SIZE is increased.
725 static_assert(kWindowsThreadStackSize
> nsIThreadManager::DEFAULT_STACK_SIZE
);
726 return kWindowsThreadStackSize
;
728 return nsIThreadManager::DEFAULT_STACK_SIZE
;
732 // called from main thread only
734 nsSocketTransportService::Init() {
735 if (!NS_IsMainThread()) {
736 NS_ERROR("wrong thread");
737 return NS_ERROR_UNEXPECTED
;
745 return NS_ERROR_UNEXPECTED
;
748 nsCOMPtr
<nsIThread
> thread
;
750 if (!XRE_IsContentProcess() ||
751 StaticPrefs::network_allow_raw_sockets_in_content_processes_AtStartup()) {
752 nsresult rv
= NS_NewNamedThread("Socket Thread", getter_AddRefs(thread
),
753 this, {.stackSize
= GetThreadStackSize()});
754 NS_ENSURE_SUCCESS(rv
, rv
);
756 // In the child process, we just want a regular nsThread with no socket
757 // polling. So we don't want to run the nsSocketTransportService runnable on
760 NS_NewNamedThread("Socket Thread", getter_AddRefs(thread
), nullptr);
761 NS_ENSURE_SUCCESS(rv
, rv
);
763 // Set up some of the state that nsSocketTransportService::Run would set.
764 PRThread
* prthread
= nullptr;
765 thread
->GetPRThread(&prthread
);
766 gSocketThread
= prthread
;
771 MutexAutoLock
lock(mLock
);
772 // Install our mThread, protecting against concurrent readers
773 thread
.swap(mThread
);
774 mDirectTaskDispatcher
= do_QueryInterface(mThread
);
775 MOZ_DIAGNOSTIC_ASSERT(
776 mDirectTaskDispatcher
,
777 "Underlying thread must support direct task dispatching");
780 Preferences::RegisterCallbacks(UpdatePrefs
, gCallbackPrefs
, this);
783 nsCOMPtr
<nsIObserverService
> obsSvc
= services::GetObserverService();
784 // Note that the observr notifications are forwarded from parent process to
785 // socket process. We have to make sure the topics registered below are also
786 // registered in nsIObserver::Init().
788 obsSvc
->AddObserver(this, "profile-initial-state", false);
789 obsSvc
->AddObserver(this, "last-pb-context-exited", false);
790 obsSvc
->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC
, true);
791 obsSvc
->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC
, true);
792 obsSvc
->AddObserver(this, "xpcom-shutdown-threads", false);
793 obsSvc
->AddObserver(this, NS_NETWORK_LINK_TOPIC
, false);
796 // We can now dispatch tasks to the socket thread.
801 // called from main thread only
803 nsSocketTransportService::Shutdown(bool aXpcomShutdown
) {
804 SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
806 NS_ENSURE_STATE(NS_IsMainThread());
808 if (!mInitialized
|| mShuttingDown
) {
809 // We never inited, or shutdown has already started
814 auto observersCopy
= mShutdownObservers
;
815 for (auto& observer
: observersCopy
) {
820 mShuttingDown
= true;
823 MutexAutoLock
lock(mLock
);
825 if (mPollableEvent
) {
826 mPollableEvent
->Signal();
830 // If we're shutting down due to going offline (rather than due to XPCOM
831 // shutdown), also tear down the thread. The thread will be shutdown during
832 // xpcom-shutdown-threads if during xpcom-shutdown proper.
833 if (!aXpcomShutdown
) {
840 nsresult
nsSocketTransportService::ShutdownThread() {
841 SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
843 NS_ENSURE_STATE(NS_IsMainThread());
850 nsCOMPtr
<nsIThread
> thread
= GetThreadSafely();
853 MutexAutoLock
lock(mLock
);
854 // Drop our reference to mThread and make sure that any concurrent readers
857 mDirectTaskDispatcher
= nullptr;
860 Preferences::UnregisterCallbacks(UpdatePrefs
, gCallbackPrefs
, this);
862 nsCOMPtr
<nsIObserverService
> obsSvc
= services::GetObserverService();
864 obsSvc
->RemoveObserver(this, "profile-initial-state");
865 obsSvc
->RemoveObserver(this, "last-pb-context-exited");
866 obsSvc
->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC
);
867 obsSvc
->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC
);
868 obsSvc
->RemoveObserver(this, "xpcom-shutdown-threads");
869 obsSvc
->RemoveObserver(this, NS_NETWORK_LINK_TOPIC
);
872 if (mAfterWakeUpTimer
) {
873 mAfterWakeUpTimer
->Cancel();
874 mAfterWakeUpTimer
= nullptr;
877 IOActivityMonitor::Shutdown();
879 mInitialized
= false;
880 mShuttingDown
= false;
886 nsSocketTransportService::GetOffline(bool* offline
) {
887 MutexAutoLock
lock(mLock
);
893 nsSocketTransportService::SetOffline(bool offline
) {
894 MutexAutoLock
lock(mLock
);
895 if (!mOffline
&& offline
) {
896 // signal the socket thread to go offline, so it will detach sockets
897 mGoingOffline
= true;
899 } else if (mOffline
&& !offline
) {
902 if (mPollableEvent
) {
903 mPollableEvent
->Signal();
910 nsSocketTransportService::GetKeepaliveIdleTime(int32_t* aKeepaliveIdleTimeS
) {
911 MOZ_ASSERT(aKeepaliveIdleTimeS
);
912 if (NS_WARN_IF(!aKeepaliveIdleTimeS
)) {
913 return NS_ERROR_NULL_POINTER
;
915 *aKeepaliveIdleTimeS
= mKeepaliveIdleTimeS
;
920 nsSocketTransportService::GetKeepaliveRetryInterval(
921 int32_t* aKeepaliveRetryIntervalS
) {
922 MOZ_ASSERT(aKeepaliveRetryIntervalS
);
923 if (NS_WARN_IF(!aKeepaliveRetryIntervalS
)) {
924 return NS_ERROR_NULL_POINTER
;
926 *aKeepaliveRetryIntervalS
= mKeepaliveRetryIntervalS
;
931 nsSocketTransportService::GetKeepaliveProbeCount(
932 int32_t* aKeepaliveProbeCount
) {
933 MOZ_ASSERT(aKeepaliveProbeCount
);
934 if (NS_WARN_IF(!aKeepaliveProbeCount
)) {
935 return NS_ERROR_NULL_POINTER
;
937 *aKeepaliveProbeCount
= mKeepaliveProbeCount
;
942 nsSocketTransportService::CreateTransport(const nsTArray
<nsCString
>& types
,
943 const nsACString
& host
, int32_t port
,
944 nsIProxyInfo
* proxyInfo
,
945 nsIDNSRecord
* dnsRecord
,
946 nsISocketTransport
** result
) {
947 return CreateRoutedTransport(types
, host
, port
, ""_ns
, 0, proxyInfo
,
952 nsSocketTransportService::CreateRoutedTransport(
953 const nsTArray
<nsCString
>& types
, const nsACString
& host
, int32_t port
,
954 const nsACString
& hostRoute
, int32_t portRoute
, nsIProxyInfo
* proxyInfo
,
955 nsIDNSRecord
* dnsRecord
, nsISocketTransport
** result
) {
956 NS_ENSURE_TRUE(mInitialized
, NS_ERROR_NOT_INITIALIZED
);
957 NS_ENSURE_TRUE(port
>= 0 && port
<= 0xFFFF, NS_ERROR_ILLEGAL_VALUE
);
959 RefPtr
<nsSocketTransport
> trans
= new nsSocketTransport();
960 nsresult rv
= trans
->Init(types
, host
, port
, hostRoute
, portRoute
, proxyInfo
,
966 trans
.forget(result
);
971 nsSocketTransportService::CreateUnixDomainTransport(
972 nsIFile
* aPath
, nsISocketTransport
** result
) {
976 NS_ENSURE_TRUE(mInitialized
, NS_ERROR_NOT_INITIALIZED
);
979 rv
= aPath
->GetNativePath(path
);
980 NS_ENSURE_SUCCESS(rv
, rv
);
982 RefPtr
<nsSocketTransport
> trans
= new nsSocketTransport();
984 rv
= trans
->InitWithFilename(path
.get());
985 NS_ENSURE_SUCCESS(rv
, rv
);
987 trans
.forget(result
);
990 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
995 nsSocketTransportService::CreateUnixDomainAbstractAddressTransport(
996 const nsACString
& aName
, nsISocketTransport
** result
) {
997 // Abstract socket address is supported on Linux only
999 RefPtr
<nsSocketTransport
> trans
= new nsSocketTransport();
1000 // First character of Abstract socket address is null
1001 UniquePtr
<char[]> name(new char[aName
.Length() + 1]);
1003 memcpy(name
.get() + 1, aName
.BeginReading(), aName
.Length());
1004 nsresult rv
= trans
->InitWithName(name
.get(), aName
.Length() + 1);
1005 if (NS_FAILED(rv
)) {
1009 trans
.forget(result
);
1012 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
1017 nsSocketTransportService::OnDispatchedEvent() {
1019 // On windows poll can hang and this became worse when we introduced the
1020 // patch for bug 698882 (see also bug 1292181), therefore we reverted the
1021 // behavior on windows to be as before bug 698882, e.g. write to the socket
1022 // also if an event dispatch is on the socket thread and writing to the
1023 // socket for each event.
1024 if (OnSocketThread()) {
1025 // this check is redundant to one done inside ::Signal(), but
1026 // we can do it here and skip obtaining the lock - given that
1027 // this is a relatively common occurance its worth the
1029 SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
1033 if (gIOService
->IsNetTearingDown()) {
1034 // Poll can hang sometimes. If we are in shutdown, we are going to
1035 // start a watchdog. If we do not exit poll within
1036 // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
1037 StartPollWatchdog();
1041 MutexAutoLock
lock(mLock
);
1042 if (mPollableEvent
) {
1043 mPollableEvent
->Signal();
1049 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal
* thread
,
1055 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal
* thread
,
1056 bool eventWasProcessed
) {
1060 void nsSocketTransportService::MarkTheLastElementOfPendingQueue() {
1061 mServingPendingQueue
= false;
1065 nsSocketTransportService::Run() {
1066 SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount
));
1069 // see bug 1361495, gethostname() triggers winsock initialization.
1070 // so do it here (on parent and child) to protect against it being done first
1071 // accidentally on the main thread.. especially via PR_GetSystemInfo(). This
1072 // will also improve latency of first real winsock operation
1074 // If STS-thread is no longer needed this should still be run before exiting
1076 char ignoredStackBuffer
[255];
1077 Unused
<< gethostname(ignoredStackBuffer
, 255);
1080 psm::InitializeSSLServerCertVerificationThreads();
1082 gSocketThread
= PR_GetCurrentThread();
1085 MutexAutoLock
lock(mLock
);
1086 mPollableEvent
.reset(new PollableEvent());
1088 // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
1089 // or similar software.
1091 // NOTE: per bug 191739, this failure could also be caused by lack
1092 // of a loopback device on Windows and OS/2 platforms (it creates
1093 // a loopback socket pair on these platforms to implement a pollable
1094 // event object). if we can't create a pollable event, then we'll
1095 // have to "busy wait" to implement the socket event queue :-(
1097 if (!mPollableEvent
->Valid()) {
1098 mPollableEvent
= nullptr;
1099 NS_WARNING("running socket transport thread without a pollable event");
1100 SOCKET_LOG(("running socket transport thread without a pollable event"));
1103 PRPollDesc entry
= {mPollableEvent
? mPollableEvent
->PollableFD() : nullptr,
1104 PR_POLL_READ
| PR_POLL_EXCEPT
, 0};
1105 SOCKET_LOG(("Setting entry 0"));
1106 mPollList
[0] = entry
;
1109 mRawThread
= NS_GetCurrentThread();
1111 // Ensure a call to GetCurrentSerialEventTarget() returns this event target.
1112 SerialEventTargetGuard
guard(this);
1114 // hook ourselves up to observe event processing for this thread
1115 nsCOMPtr
<nsIThreadInternal
> threadInt
= do_QueryInterface(mRawThread
);
1116 threadInt
->SetObserver(this);
1118 // make sure the pseudo random number generator is seeded on this thread
1119 srand(static_cast<unsigned>(PR_Now()));
1121 // For the calculation of the duration of the last cycle (i.e. the last
1122 // for-loop iteration before shutdown).
1123 TimeStamp startOfCycleForLastCycleCalc
;
1125 // For measuring of the poll iteration duration without time spent blocked
1127 TimeStamp pollCycleStart
;
1128 // Time blocked in poll().
1129 TimeDuration singlePollDuration
;
1131 // For calculating the time needed for a new element to run.
1132 TimeStamp startOfIteration
;
1133 TimeStamp startOfNextIteration
;
1135 // If there is too many pending events queued, we will run some poll()
1136 // between them and the following variable is cumulative time spent
1137 // blocking in poll().
1138 TimeDuration pollDuration
;
1141 bool pendingEvents
= false;
1142 if (Telemetry::CanRecordPrereleaseData()) {
1143 startOfCycleForLastCycleCalc
= TimeStamp::NowLoRes();
1144 startOfNextIteration
= TimeStamp::NowLoRes();
1146 pollDuration
= nullptr;
1147 // We pop out to this loop when there are no pending events.
1148 // If we don't reset these, we may not re-enter ProcessNextEvent()
1149 // until we have events to process, and it may seem like we have
1150 // an event running for a very long time.
1151 mRawThread
->SetRunningEventDelay(TimeDuration(), TimeStamp());
1154 if (Telemetry::CanRecordPrereleaseData()) {
1155 pollCycleStart
= TimeStamp::NowLoRes();
1158 DoPollIteration(&singlePollDuration
);
1160 if (Telemetry::CanRecordPrereleaseData() && !pollCycleStart
.IsNull()) {
1161 Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME
,
1162 singlePollDuration
.ToMilliseconds());
1163 Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_CYCLE
,
1164 pollCycleStart
+ singlePollDuration
,
1165 TimeStamp::NowLoRes());
1166 pollDuration
+= singlePollDuration
;
1169 mRawThread
->HasPendingEvents(&pendingEvents
);
1170 if (pendingEvents
) {
1171 if (!mServingPendingQueue
) {
1172 nsresult rv
= Dispatch(
1174 "net::nsSocketTransportService::"
1175 "MarkTheLastElementOfPendingQueue",
1177 &nsSocketTransportService::MarkTheLastElementOfPendingQueue
),
1178 nsIEventTarget::DISPATCH_NORMAL
);
1179 if (NS_FAILED(rv
)) {
1181 "Could not dispatch a new event on the "
1184 mServingPendingQueue
= true;
1187 if (Telemetry::CanRecordPrereleaseData()) {
1188 startOfIteration
= startOfNextIteration
;
1189 // Everything that comes after this point will
1190 // be served in the next iteration. If no even
1191 // arrives, startOfNextIteration will be reset at the
1192 // beginning of each for-loop.
1193 startOfNextIteration
= TimeStamp::NowLoRes();
1196 TimeStamp eventQueueStart
= TimeStamp::NowLoRes();
1198 NS_ProcessNextEvent(mRawThread
);
1199 pendingEvents
= false;
1200 mRawThread
->HasPendingEvents(&pendingEvents
);
1201 } while (pendingEvents
&& mServingPendingQueue
&&
1202 ((TimeStamp::NowLoRes() - eventQueueStart
).ToMilliseconds() <
1203 mMaxTimePerPollIter
));
1205 if (Telemetry::CanRecordPrereleaseData() && !mServingPendingQueue
&&
1206 !startOfIteration
.IsNull()) {
1207 Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_AND_EVENTS_CYCLE
,
1208 startOfIteration
+ pollDuration
,
1209 TimeStamp::NowLoRes());
1210 pollDuration
= nullptr;
1213 } while (pendingEvents
);
1215 bool goingOffline
= false;
1216 // now that our event queue is empty, check to see if we should exit
1217 if (mShuttingDown
) {
1218 if (Telemetry::CanRecordPrereleaseData() &&
1219 !startOfCycleForLastCycleCalc
.IsNull()) {
1220 Telemetry::AccumulateTimeDelta(
1221 Telemetry::STS_POLL_AND_EVENT_THE_LAST_CYCLE
,
1222 startOfCycleForLastCycleCalc
, TimeStamp::NowLoRes());
1227 MutexAutoLock
lock(mLock
);
1228 if (mGoingOffline
) {
1229 mGoingOffline
= false;
1230 goingOffline
= true;
1233 // Avoid potential deadlock
1239 SOCKET_LOG(("STS shutting down thread\n"));
1241 // detach all sockets, including locals
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();
1248 // Final pass over the event queue. This makes sure that events posted by
1249 // socket detach handlers get processed.
1250 NS_ProcessPendingEvents(mRawThread
);
1252 SOCKET_LOG(("STS thread exit\n"));
1253 MOZ_ASSERT(mPollList
.Length() == 1);
1254 MOZ_ASSERT(mActiveList
.IsEmpty());
1255 MOZ_ASSERT(mIdleList
.IsEmpty());
1260 void nsSocketTransportService::DetachSocketWithGuard(
1261 bool aGuardLocals
, SocketContextList
& socketList
, int32_t index
) {
1262 bool isGuarded
= false;
1264 socketList
[index
].mHandler
->IsLocal(&isGuarded
);
1266 socketList
[index
].mHandler
->KeepWhenOffline(&isGuarded
);
1270 DetachSocket(socketList
, &socketList
[index
]);
1274 void nsSocketTransportService::Reset(bool aGuardLocals
) {
1275 // detach any sockets
1277 for (i
= mActiveList
.Length() - 1; i
>= 0; --i
) {
1278 DetachSocketWithGuard(aGuardLocals
, mActiveList
, i
);
1280 for (i
= mIdleList
.Length() - 1; i
>= 0; --i
) {
1281 DetachSocketWithGuard(aGuardLocals
, mIdleList
, i
);
1285 nsresult
nsSocketTransportService::DoPollIteration(TimeDuration
* pollDuration
) {
1286 SOCKET_LOG(("STS poll iter\n"));
1288 PRIntervalTime now
= PR_IntervalNow();
1290 // We can't have more than int32_max sockets in use
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 ;-)
1300 count
= mIdleList
.Length();
1301 for (i
= mActiveList
.Length() - 1; i
>= 0; --i
) {
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
));
1309 if (NS_FAILED(mActiveList
[i
].mHandler
->mCondition
)) {
1310 DetachSocket(mActiveList
, &mActiveList
[i
]);
1312 uint16_t in_flags
= mActiveList
[i
].mHandler
->mPollFlags
;
1313 if (in_flags
== 0) {
1314 MoveToIdleList(&mActiveList
[i
]);
1316 // update poll flags
1317 mPollList
[i
+ 1].in_flags
= in_flags
;
1318 mPollList
[i
+ 1].out_flags
= 0;
1319 mActiveList
[i
].EnsureTimeout(now
);
1323 for (i
= count
- 1; i
>= 0; --i
) {
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
));
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
]);
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();
1352 SOCKET_LOG((" calling PR_Poll [active=%zu idle=%zu]\n", mActiveList
.Length(),
1353 mIdleList
.Length()));
1356 // 30 active connections is the historic limit before firefox 7's 256. A few
1357 // windows systems have troubles with the higher limit, so actively probe a
1358 // limit the first time we exceed 30.
1359 if ((mActiveList
.Length() > 30) && !mProbedMaxCount
) ProbeMaxCount();
1362 // Measures seconds spent while blocked on PR_Poll
1364 *pollDuration
= nullptr;
1366 if (!gIOService
->IsNetTearingDown()) {
1367 // Let's not do polling during shutdown.
1371 n
= Poll(pollDuration
, now
);
1377 now
= PR_IntervalNow();
1380 SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
1384 // service "active" sockets...
1386 for (i
= 0; i
< int32_t(mActiveList
.Length()); ++i
) {
1387 PRPollDesc
& desc
= mPollList
[i
+ 1];
1388 SocketContext
& s
= mActiveList
[i
];
1389 if (n
> 0 && desc
.out_flags
!= 0) {
1390 s
.DisengageTimeout();
1391 s
.mHandler
->OnSocketReady(desc
.fd
, desc
.out_flags
);
1392 } else if (s
.IsTimedOut(now
)) {
1393 SOCKET_LOG(("socket %p timed out", s
.mHandler
.get()));
1394 s
.DisengageTimeout();
1395 s
.mHandler
->OnSocketReady(desc
.fd
, -1);
1397 s
.MaybeResetEpoch();
1401 // check for "dead" sockets and remove them (need to do this in
1402 // reverse order obviously).
1404 for (i
= mActiveList
.Length() - 1; i
>= 0; --i
) {
1405 if (NS_FAILED(mActiveList
[i
].mHandler
->mCondition
)) {
1406 DetachSocket(mActiveList
, &mActiveList
[i
]);
1411 MutexAutoLock
lock(mLock
);
1412 // acknowledge pollable event (should not block)
1414 (mPollList
[0].out_flags
& (PR_POLL_READ
| PR_POLL_EXCEPT
)) &&
1416 ((mPollList
[0].out_flags
& PR_POLL_EXCEPT
) ||
1417 !mPollableEvent
->Clear())) {
1418 // On Windows, the TCP loopback connection in the
1419 // pollable event may become broken when a laptop
1420 // switches between wired and wireless networks or
1421 // wakes up from hibernation. We try to create a
1422 // new pollable event. If that fails, we fall back
1424 TryRepairPollableEvent();
1427 if (mPollableEvent
&&
1428 !mPollableEvent
->IsSignallingAlive(mPollableEventTimeout
)) {
1429 SOCKET_LOG(("Pollable event signalling failed/timed out"));
1430 TryRepairPollableEvent();
1438 void nsSocketTransportService::UpdateSendBufferPref() {
1441 // If the pref is set, honor it. 0 means use OS defaults.
1442 nsresult rv
= Preferences::GetInt(SEND_BUFFER_PREF
, &bufferSize
);
1443 if (NS_SUCCEEDED(rv
)) {
1444 mSendBufferSize
= bufferSize
;
1449 mSendBufferSize
= 131072 * 4;
1453 nsresult
nsSocketTransportService::UpdatePrefs() {
1454 mSendBufferSize
= 0;
1456 UpdateSendBufferPref();
1458 // Default TCP Keepalive Values.
1459 int32_t keepaliveIdleTimeS
;
1461 Preferences::GetInt(KEEPALIVE_IDLE_TIME_PREF
, &keepaliveIdleTimeS
);
1462 if (NS_SUCCEEDED(rv
)) {
1463 mKeepaliveIdleTimeS
= clamped(keepaliveIdleTimeS
, 1, kMaxTCPKeepIdle
);
1466 int32_t keepaliveRetryIntervalS
;
1467 rv
= Preferences::GetInt(KEEPALIVE_RETRY_INTERVAL_PREF
,
1468 &keepaliveRetryIntervalS
);
1469 if (NS_SUCCEEDED(rv
)) {
1470 mKeepaliveRetryIntervalS
=
1471 clamped(keepaliveRetryIntervalS
, 1, kMaxTCPKeepIntvl
);
1474 int32_t keepaliveProbeCount
;
1475 rv
= Preferences::GetInt(KEEPALIVE_PROBE_COUNT_PREF
, &keepaliveProbeCount
);
1476 if (NS_SUCCEEDED(rv
)) {
1477 mKeepaliveProbeCount
= clamped(keepaliveProbeCount
, 1, kMaxTCPKeepCount
);
1479 bool keepaliveEnabled
= false;
1480 rv
= Preferences::GetBool(KEEPALIVE_ENABLED_PREF
, &keepaliveEnabled
);
1481 if (NS_SUCCEEDED(rv
) && keepaliveEnabled
!= mKeepaliveEnabledPref
) {
1482 mKeepaliveEnabledPref
= keepaliveEnabled
;
1483 OnKeepaliveEnabledPrefChange();
1486 int32_t maxTimePref
;
1487 rv
= Preferences::GetInt(MAX_TIME_BETWEEN_TWO_POLLS
, &maxTimePref
);
1488 if (NS_SUCCEEDED(rv
) && maxTimePref
>= 0) {
1489 mMaxTimePerPollIter
= maxTimePref
;
1492 int32_t pollBusyWaitPeriod
;
1493 rv
= Preferences::GetInt(POLL_BUSY_WAIT_PERIOD
, &pollBusyWaitPeriod
);
1494 if (NS_SUCCEEDED(rv
) && pollBusyWaitPeriod
> 0) {
1495 mNetworkLinkChangeBusyWaitPeriod
= PR_SecondsToInterval(pollBusyWaitPeriod
);
1498 int32_t pollBusyWaitPeriodTimeout
;
1499 rv
= Preferences::GetInt(POLL_BUSY_WAIT_PERIOD_TIMEOUT
,
1500 &pollBusyWaitPeriodTimeout
);
1501 if (NS_SUCCEEDED(rv
) && pollBusyWaitPeriodTimeout
> 0) {
1502 mNetworkLinkChangeBusyWaitTimeout
=
1503 PR_SecondsToInterval(pollBusyWaitPeriodTimeout
);
1506 int32_t maxTimeForPrClosePref
;
1507 rv
= Preferences::GetInt(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN
,
1508 &maxTimeForPrClosePref
);
1509 if (NS_SUCCEEDED(rv
) && maxTimeForPrClosePref
>= 0) {
1510 mMaxTimeForPrClosePref
= PR_MillisecondsToInterval(maxTimeForPrClosePref
);
1513 int32_t pollableEventTimeout
;
1514 rv
= Preferences::GetInt(POLLABLE_EVENT_TIMEOUT
, &pollableEventTimeout
);
1515 if (NS_SUCCEEDED(rv
) && pollableEventTimeout
>= 0) {
1516 MutexAutoLock
lock(mLock
);
1517 mPollableEventTimeout
= TimeDuration::FromSeconds(pollableEventTimeout
);
1520 nsAutoCString portMappingPref
;
1521 rv
= Preferences::GetCString("network.socket.forcePort", portMappingPref
);
1522 if (NS_SUCCEEDED(rv
)) {
1523 bool rv
= UpdatePortRemapPreference(portMappingPref
);
1526 "network.socket.forcePort preference is ill-formed, this will likely "
1527 "make everything unexpectedly fail!");
1534 void nsSocketTransportService::OnKeepaliveEnabledPrefChange() {
1535 // Dispatch to socket thread if we're not executing there.
1536 if (!OnSocketThread()) {
1537 gSocketTransportService
->Dispatch(
1539 "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this,
1540 &nsSocketTransportService::OnKeepaliveEnabledPrefChange
),
1541 NS_DISPATCH_NORMAL
);
1545 SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
1546 mKeepaliveEnabledPref
? "enabled" : "disabled"));
1548 // Notify each socket that keepalive has been en/disabled globally.
1549 for (int32_t i
= mActiveList
.Length() - 1; i
>= 0; --i
) {
1550 NotifyKeepaliveEnabledPrefChange(&mActiveList
[i
]);
1552 for (int32_t i
= mIdleList
.Length() - 1; i
>= 0; --i
) {
1553 NotifyKeepaliveEnabledPrefChange(&mIdleList
[i
]);
1557 void nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(
1558 SocketContext
* sock
) {
1559 MOZ_ASSERT(sock
, "SocketContext cannot be null!");
1560 MOZ_ASSERT(sock
->mHandler
, "SocketContext does not have a handler!");
1562 if (!sock
|| !sock
->mHandler
) {
1566 sock
->mHandler
->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref
);
1570 nsSocketTransportService::GetName(nsACString
& aName
) {
1571 aName
.AssignLiteral("nsSocketTransportService");
1576 nsSocketTransportService::Observe(nsISupports
* subject
, const char* topic
,
1577 const char16_t
* data
) {
1578 SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic
));
1580 if (!strcmp(topic
, "profile-initial-state")) {
1581 if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF
, false)) {
1584 return net::IOActivityMonitor::Init();
1587 if (!strcmp(topic
, "last-pb-context-exited")) {
1588 nsCOMPtr
<nsIRunnable
> ev
= NewRunnableMethod(
1589 "net::nsSocketTransportService::ClosePrivateConnections", this,
1590 &nsSocketTransportService::ClosePrivateConnections
);
1591 nsresult rv
= Dispatch(ev
, nsIEventTarget::DISPATCH_NORMAL
);
1592 NS_ENSURE_SUCCESS(rv
, rv
);
1595 if (!strcmp(topic
, NS_TIMER_CALLBACK_TOPIC
)) {
1596 nsCOMPtr
<nsITimer
> timer
= do_QueryInterface(subject
);
1597 if (timer
== mAfterWakeUpTimer
) {
1598 mAfterWakeUpTimer
= nullptr;
1599 mSleepPhase
= false;
1603 if (timer
== mPollRepairTimer
) {
1608 } else if (!strcmp(topic
, NS_WIDGET_SLEEP_OBSERVER_TOPIC
)) {
1610 if (mAfterWakeUpTimer
) {
1611 mAfterWakeUpTimer
->Cancel();
1612 mAfterWakeUpTimer
= nullptr;
1614 } else if (!strcmp(topic
, NS_WIDGET_WAKE_OBSERVER_TOPIC
)) {
1615 if (mSleepPhase
&& !mAfterWakeUpTimer
) {
1616 NS_NewTimerWithObserver(getter_AddRefs(mAfterWakeUpTimer
), this, 2000,
1617 nsITimer::TYPE_ONE_SHOT
);
1619 } else if (!strcmp(topic
, "xpcom-shutdown-threads")) {
1621 } else if (!strcmp(topic
, NS_NETWORK_LINK_TOPIC
)) {
1622 mLastNetworkLinkChangeTime
= PR_IntervalNow();
1628 void nsSocketTransportService::ClosePrivateConnections() {
1629 MOZ_ASSERT(IsOnCurrentThread(), "Must be called on the socket thread");
1631 for (int32_t i
= mActiveList
.Length() - 1; i
>= 0; --i
) {
1632 if (mActiveList
[i
].mHandler
->mIsPrivate
) {
1633 DetachSocket(mActiveList
, &mActiveList
[i
]);
1636 for (int32_t i
= mIdleList
.Length() - 1; i
>= 0; --i
) {
1637 if (mIdleList
[i
].mHandler
->mIsPrivate
) {
1638 DetachSocket(mIdleList
, &mIdleList
[i
]);
1642 ClearPrivateSSLState();
1646 nsSocketTransportService::GetSendBufferSize(int32_t* value
) {
1647 *value
= mSendBufferSize
;
1651 /// ugly OS specific includes are placed at the bottom of the src for clarity
1654 # include <windows.h>
1655 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1656 # include <sys/resource.h>
1659 // Right now the only need to do this is on windows.
1661 void nsSocketTransportService::ProbeMaxCount() {
1662 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1664 if (mProbedMaxCount
) {
1667 mProbedMaxCount
= true;
1669 // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
1670 // sockets. See bug 692260 - windows should be able to handle 1000 sockets
1671 // in select() without a problem, but LSPs have been known to balk at lower
1672 // numbers. (64 in the bug).
1675 struct PRPollDesc pfd
[SOCKET_LIMIT_TARGET
];
1676 uint32_t numAllocated
= 0;
1678 for (uint32_t index
= 0; index
< gMaxCount
; ++index
) {
1679 pfd
[index
].in_flags
= PR_POLL_READ
| PR_POLL_WRITE
| PR_POLL_EXCEPT
;
1680 pfd
[index
].out_flags
= 0;
1681 pfd
[index
].fd
= PR_OpenTCPSocket(PR_AF_INET
);
1682 if (!pfd
[index
].fd
) {
1683 SOCKET_LOG(("Socket Limit Test index %d failed\n", index
));
1684 if (index
< SOCKET_LIMIT_MIN
)
1685 gMaxCount
= SOCKET_LIMIT_MIN
;
1694 static_assert(SOCKET_LIMIT_MIN
>= 32U, "Minimum Socket Limit is >= 32");
1695 while (gMaxCount
<= numAllocated
) {
1696 int32_t rv
= PR_Poll(pfd
, gMaxCount
, PR_MillisecondsToInterval(0));
1698 SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", gMaxCount
, rv
));
1702 SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
1703 gMaxCount
, rv
, PR_GetError()));
1706 if (gMaxCount
<= SOCKET_LIMIT_MIN
) {
1707 gMaxCount
= SOCKET_LIMIT_MIN
;
1713 for (uint32_t index
= 0; index
< numAllocated
; ++index
)
1714 if (pfd
[index
].fd
) PR_Close(pfd
[index
].fd
);
1716 Telemetry::Accumulate(Telemetry::NETWORK_PROBE_MAXCOUNT
, gMaxCount
);
1717 SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount
));
1721 PRStatus
nsSocketTransportService::DiscoverMaxCount() {
1722 gMaxCount
= SOCKET_LIMIT_MIN
;
1724 #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1725 // On unix and os x network sockets and file
1726 // descriptors are the same. OS X comes defaulted at 256,
1727 // most linux at 1000. We can reliably use [sg]rlimit to
1728 // query that and raise it if needed.
1730 struct rlimit rlimitData
{};
1731 if (getrlimit(RLIMIT_NOFILE
, &rlimitData
) == -1) { // rlimit broken - use min
1735 if (rlimitData
.rlim_cur
>= SOCKET_LIMIT_TARGET
) { // larger than target!
1736 gMaxCount
= SOCKET_LIMIT_TARGET
;
1740 int32_t maxallowed
= rlimitData
.rlim_max
;
1741 if ((uint32_t)maxallowed
<= SOCKET_LIMIT_MIN
) {
1742 return PR_SUCCESS
; // so small treat as if rlimit is broken
1745 if ((maxallowed
== -1) || // no hard cap - ok to set target
1746 ((uint32_t)maxallowed
>= SOCKET_LIMIT_TARGET
)) {
1747 maxallowed
= SOCKET_LIMIT_TARGET
;
1750 rlimitData
.rlim_cur
= maxallowed
;
1751 setrlimit(RLIMIT_NOFILE
, &rlimitData
);
1752 if ((getrlimit(RLIMIT_NOFILE
, &rlimitData
) != -1) &&
1753 (rlimitData
.rlim_cur
> SOCKET_LIMIT_MIN
)) {
1754 gMaxCount
= rlimitData
.rlim_cur
;
1757 #elif defined(XP_WIN) && !defined(WIN_CE)
1758 // >= XP is confirmed to have at least 1000
1759 static_assert(SOCKET_LIMIT_TARGET
<= 1000,
1760 "SOCKET_LIMIT_TARGET max value is 1000");
1761 gMaxCount
= SOCKET_LIMIT_TARGET
;
1763 // other platforms are harder to test - so leave at safe legacy value
1769 // Used to return connection info to Dashboard.cpp
1770 void nsSocketTransportService::AnalyzeConnection(nsTArray
<SocketInfo
>* data
,
1771 SocketContext
* context
,
1773 if (context
->mHandler
->mIsPrivate
) {
1776 PRFileDesc
* aFD
= context
->mFD
;
1778 PRFileDesc
* idLayer
= PR_GetIdentitiesLayer(aFD
, PR_NSPR_IO_LAYER
);
1780 NS_ENSURE_TRUE_VOID(idLayer
);
1782 PRDescType type
= PR_GetDescType(idLayer
);
1783 char host
[64] = {0};
1785 const char* type_desc
;
1787 if (type
== PR_DESC_SOCKET_TCP
) {
1789 PRNetAddr peer_addr
;
1790 PodZero(&peer_addr
);
1792 PRStatus rv
= PR_GetPeerName(aFD
, &peer_addr
);
1793 if (rv
!= PR_SUCCESS
) {
1797 rv
= PR_NetAddrToString(&peer_addr
, host
, sizeof(host
));
1798 if (rv
!= PR_SUCCESS
) {
1802 if (peer_addr
.raw
.family
== PR_AF_INET
) {
1803 port
= peer_addr
.inet
.port
;
1805 port
= peer_addr
.ipv6
.port
;
1807 port
= PR_ntohs(port
);
1809 if (type
== PR_DESC_SOCKET_UDP
) {
1812 type_desc
= "other";
1815 if (context
->mHandler
->GetRemoteAddr(&addr
) != NS_OK
) {
1818 if (!addr
.ToStringBuffer(host
, sizeof(host
))) {
1821 if (addr
.GetPort(&port
) != NS_OK
) {
1826 uint64_t sent
= context
->mHandler
->ByteCountSent();
1827 uint64_t received
= context
->mHandler
->ByteCountReceived();
1828 SocketInfo info
= {nsCString(host
), sent
, received
, port
, aActive
,
1829 nsCString(type_desc
)};
1831 data
->AppendElement(info
);
1834 void nsSocketTransportService::GetSocketConnections(
1835 nsTArray
<SocketInfo
>* data
) {
1836 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1837 for (uint32_t i
= 0; i
< mActiveList
.Length(); i
++) {
1838 AnalyzeConnection(data
, &mActiveList
[i
], true);
1840 for (uint32_t i
= 0; i
< mIdleList
.Length(); i
++) {
1841 AnalyzeConnection(data
, &mIdleList
[i
], false);
1845 bool nsSocketTransportService::IsTelemetryEnabledAndNotSleepPhase() {
1846 return Telemetry::CanRecordPrereleaseData() && !mSleepPhase
;
1850 void nsSocketTransportService::StartPollWatchdog() {
1851 // Start off the timer from a runnable off of the main thread in order to
1852 // avoid a deadlock, see bug 1370448.
1853 RefPtr
<nsSocketTransportService
> self(this);
1854 NS_DispatchToMainThread(NS_NewRunnableFunction(
1855 "nsSocketTransportService::StartPollWatchdog", [self
] {
1856 MutexAutoLock
lock(self
->mLock
);
1858 // Poll can hang sometimes. If we are in shutdown, we are going to start
1859 // a watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
1860 // signal a pollable event again.
1861 MOZ_ASSERT(gIOService
->IsNetTearingDown());
1862 if (self
->mPolling
&& !self
->mPollRepairTimer
) {
1863 NS_NewTimerWithObserver(getter_AddRefs(self
->mPollRepairTimer
), self
,
1864 REPAIR_POLLABLE_EVENT_TIME
,
1865 nsITimer::TYPE_REPEATING_SLACK
);
1870 void nsSocketTransportService::DoPollRepair() {
1871 MutexAutoLock
lock(mLock
);
1872 if (mPolling
&& mPollableEvent
) {
1873 mPollableEvent
->Signal();
1874 } else if (mPollRepairTimer
) {
1875 mPollRepairTimer
->Cancel();
1879 void nsSocketTransportService::StartPolling() {
1880 MutexAutoLock
lock(mLock
);
1884 void nsSocketTransportService::EndPolling() {
1885 MutexAutoLock
lock(mLock
);
1887 if (mPollRepairTimer
) {
1888 mPollRepairTimer
->Cancel();
1894 void nsSocketTransportService::TryRepairPollableEvent() {
1895 mLock
.AssertCurrentThreadOwns();
1897 NS_WARNING("Trying to repair mPollableEvent");
1898 mPollableEvent
.reset(new PollableEvent());
1899 if (!mPollableEvent
->Valid()) {
1900 mPollableEvent
= nullptr;
1903 ("running socket transport thread without "
1904 "a pollable event now valid=%d",
1906 mPollList
[0].fd
= mPollableEvent
? mPollableEvent
->PollableFD() : nullptr;
1907 mPollList
[0].in_flags
= PR_POLL_READ
| PR_POLL_EXCEPT
;
1908 mPollList
[0].out_flags
= 0;
1912 nsSocketTransportService::AddShutdownObserver(
1913 nsISTSShutdownObserver
* aObserver
) {
1914 mShutdownObservers
.AppendElement(aObserver
);
1919 nsSocketTransportService::RemoveShutdownObserver(
1920 nsISTSShutdownObserver
* aObserver
) {
1921 mShutdownObservers
.RemoveElement(aObserver
);
1926 } // namespace mozilla