Bug 1839454 - Don't try and call gcparam in the browser when running JS reftests...
[gecko.git] / netwerk / base / nsSocketTransportService2.cpp
blobd7fbf937a0b39e84c786672c1d2cf5a680a8093a
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"
24 #include "nsError.h"
25 #include "nsIFile.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"
33 #include "prerror.h"
34 #include "prnetdb.h"
36 namespace mozilla {
37 namespace net {
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;
66 // Utility functions
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(
77 PRIntervalTime now) {
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()));
87 mPollStartEpoch = 0;
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!"));
105 return 0;
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) {
113 mPollStartEpoch = 0;
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) {
142 return;
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)) {
148 continue;
150 if (*aPort > std::get<1>(portMapping)) {
151 continue;
154 *aPort = std::get<2>(portMapping);
155 return;
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()) {
168 return true;
171 nsTArray<std::tuple<uint16_t, uint16_t>> ranges(2);
172 while (true) {
173 uint16_t loPort;
174 tokenizer.SkipWhites();
175 if (!tokenizer.ReadInteger(&loPort)) {
176 break;
179 uint16_t hiPort;
180 tokenizer.SkipWhites();
181 if (tokenizer.CheckChar('-')) {
182 tokenizer.SkipWhites();
183 if (!tokenizer.ReadInteger(&hiPort)) {
184 break;
186 } else {
187 hiPort = loPort;
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('=')) {
198 uint16_t targetPort;
199 tokenizer.SkipWhites();
200 if (!tokenizer.ReadInteger(&targetPort)) {
201 break;
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));
213 ranges.Clear();
215 tokenizer.SkipWhites();
216 if (tokenizer.CheckChar(';')) {
217 continue; // more mappings (or EOF) expected
219 if (tokenizer.CheckEOF()) {
220 return true;
224 // Anything else is unexpected.
225 break;
228 // 'break' from the parsing loop means ill-formed preference
229 portRemapping.Clear();
230 return false;
233 bool rv = consumePreference();
235 if (!IsOnCurrentThread()) {
236 nsCOMPtr<nsIThread> thread = GetThreadSafely();
237 if (!thread) {
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");
241 return false;
243 thread->Dispatch(NewRunnableMethod<TPortRemapping>(
244 "net::ApplyPortRemapping", this,
245 &nsSocketTransportService::ApplyPortRemapPreference, portRemapping));
246 } else {
247 ApplyPortRemapPreference(portRemapping);
250 return rv;
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();
269 NS_IMETHODIMP
270 nsSocketTransportService::DispatchFromScript(nsIRunnable* event,
271 uint32_t flags) {
272 nsCOMPtr<nsIRunnable> event_ref(event);
273 return Dispatch(event_ref.forget(), flags);
276 NS_IMETHODIMP
277 nsSocketTransportService::Dispatch(already_AddRefed<nsIRunnable> event,
278 uint32_t flags) {
279 nsCOMPtr<nsIRunnable> event_ref(event);
280 SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get()));
282 nsCOMPtr<nsIThread> thread = GetThreadSafely();
283 nsresult rv;
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;
291 return rv;
294 NS_IMETHODIMP
295 nsSocketTransportService::DelayedDispatch(already_AddRefed<nsIRunnable>,
296 uint32_t) {
297 return NS_ERROR_NOT_IMPLEMENTED;
300 NS_IMETHODIMP
301 nsSocketTransportService::RegisterShutdownTask(nsITargetShutdownTask* task) {
302 nsCOMPtr<nsIThread> thread = GetThreadSafely();
303 return thread ? thread->RegisterShutdownTask(task) : NS_ERROR_UNEXPECTED;
306 NS_IMETHODIMP
307 nsSocketTransportService::UnregisterShutdownTask(nsITargetShutdownTask* task) {
308 nsCOMPtr<nsIThread> thread = GetThreadSafely();
309 return thread ? thread->UnregisterShutdownTask(task) : NS_ERROR_UNEXPECTED;
312 NS_IMETHODIMP
313 nsSocketTransportService::IsOnCurrentThread(bool* result) {
314 *result = OnSocketThread();
315 return NS_OK;
318 NS_IMETHODIMP_(bool)
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();
333 NS_IMETHODIMP
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();
345 if (!dispatcher) {
346 // nothing to drain.
347 return NS_OK;
349 return dispatcher->DrainDirectTasks();
352 NS_IMETHODIMP nsSocketTransportService::HaveDirectTasks(bool* aValue) {
353 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
354 GetDirectTaskDispatcherSafely();
355 if (!dispatcher) {
356 *aValue = false;
357 return NS_OK;
359 return dispatcher->HaveDirectTasks(aValue);
362 //-----------------------------------------------------------------------------
363 // socket api (socket thread only)
365 NS_IMETHODIMP
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);
377 return NS_OK;
380 NS_IMETHODIMP
381 nsSocketTransportService::AttachSocket(PRFileDesc* fd,
382 nsASocketHandler* handler) {
383 SOCKET_LOG(
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);
395 return NS_OK;
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);
417 return rv;
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();
434 // cleanup
435 sock->mFD = nullptr;
437 if (&listHead == &mActiveList) {
438 RemoveFromPollList(sock);
439 } else {
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();
450 if (runnable) {
451 event = runnable->TakeEvent();
452 runnable->remove();
453 delete runnable;
455 if (event) {
456 // move event from pending queue to dispatch queue
457 return Dispatch(event, NS_DISPATCH_NORMAL);
459 return NS_OK;
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()) {
471 index = -1;
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());
485 PRPollDesc poll;
486 poll.fd = sock->mFD;
487 poll.in_flags = sock->mHandler->mPollFlags;
488 poll.out_flags = 0;
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);
495 } else {
496 // Avoid refcount bump/decrease
497 mActiveList.EmplaceBack(sock->mFD, sock->mHandler.forget(),
498 sock->mPollStartEpoch);
499 mPollList.AppendElement(poll);
502 SOCKET_LOG(
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);
519 SOCKET_LOG(
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);
534 SOCKET_LOG(
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);
545 SOCKET_LOG(
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);
554 AddToIdleList(sock);
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);
563 AddToPollList(sock);
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);
587 if (r < minR) {
588 minR = r;
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)));
596 return minR;
599 int32_t nsSocketTransportService::Poll(TimeDuration* pollDuration,
600 PRIntervalTime ts) {
601 MOZ_ASSERT(IsOnCurrentThread());
602 PRPollDesc* firstPollEntry;
603 uint32_t pollCount;
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);
617 } else {
618 // no pollable event, so busy wait...
619 pollCount = mActiveList.Length();
620 if (pollCount) {
621 firstPollEntry = &mPollList[1];
622 } else {
623 firstPollEntry = nullptr;
625 pollTimeout =
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
631 // been detected.
632 PRIntervalTime to = mNetworkLinkChangeBusyWaitTimeout;
633 if (to) {
634 pollTimeout = std::min(to, pollTimeout);
635 SOCKET_LOG((" timeout shorthened after network change event"));
639 TimeStamp pollStart;
640 if (Telemetry::CanRecordPrereleaseData()) {
641 pollStart = TimeStamp::NowLoRes();
644 SOCKET_LOG((" timeout = %i milliseconds\n",
645 PR_IntervalToMilliseconds(pollTimeout)));
647 int32_t rv;
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();
656 #endif
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",
670 pollCount)
671 : pollTimeout == PR_INTERVAL_NO_WAIT
672 ? nsPrintfCString("Poll count: %u, Poll timeout: NO_WAIT",
673 pollCount)
674 : nsPrintfCString("Poll count: %u, Poll timeout: %ums", pollCount,
675 PR_IntervalToMilliseconds(pollTimeout)));
677 #endif
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)));
687 return rv;
690 //-----------------------------------------------------------------------------
691 // xpcom api
693 NS_IMPL_ISUPPORTS(nsSocketTransportService, nsISocketTransportService,
694 nsIRoutedSocketTransportService, nsIEventTarget,
695 nsISerialEventTarget, nsIThreadObserver, nsIRunnable,
696 nsPISocketTransportService, nsIObserver, nsINamed,
697 nsIDirectTaskDispatcher)
699 static const char* gCallbackPrefs[] = {
700 SEND_BUFFER_PREF,
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",
709 nullptr,
712 /* static */
713 void nsSocketTransportService::UpdatePrefs(const char* aPref, void* aSelf) {
714 static_cast<nsSocketTransportService*>(aSelf)->UpdatePrefs();
717 static uint32_t GetThreadStackSize() {
718 #ifdef XP_WIN
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;
727 #else
728 return nsIThreadManager::DEFAULT_STACK_SIZE;
729 #endif
732 // called from main thread only
733 NS_IMETHODIMP
734 nsSocketTransportService::Init() {
735 if (!NS_IsMainThread()) {
736 NS_ERROR("wrong thread");
737 return NS_ERROR_UNEXPECTED;
740 if (mInitialized) {
741 return NS_OK;
744 if (mShuttingDown) {
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);
755 } else {
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
758 // it.
759 nsresult rv =
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;
767 mRawThread = thread;
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);
781 UpdatePrefs();
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().
787 if (obsSvc) {
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.
797 mInitialized = true;
798 return NS_OK;
801 // called from main thread only
802 NS_IMETHODIMP
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
810 return NS_OK;
814 auto observersCopy = mShutdownObservers;
815 for (auto& observer : observersCopy) {
816 observer->Observe();
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) {
834 ShutdownThread();
837 return NS_OK;
840 nsresult nsSocketTransportService::ShutdownThread() {
841 SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
843 NS_ENSURE_STATE(NS_IsMainThread());
845 if (!mInitialized) {
846 return NS_OK;
849 // join with thread
850 nsCOMPtr<nsIThread> thread = GetThreadSafely();
851 thread->Shutdown();
853 MutexAutoLock lock(mLock);
854 // Drop our reference to mThread and make sure that any concurrent readers
855 // are excluded
856 mThread = nullptr;
857 mDirectTaskDispatcher = nullptr;
860 Preferences::UnregisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
862 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
863 if (obsSvc) {
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;
882 return NS_OK;
885 NS_IMETHODIMP
886 nsSocketTransportService::GetOffline(bool* offline) {
887 MutexAutoLock lock(mLock);
888 *offline = mOffline;
889 return NS_OK;
892 NS_IMETHODIMP
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;
898 mOffline = true;
899 } else if (mOffline && !offline) {
900 mOffline = false;
902 if (mPollableEvent) {
903 mPollableEvent->Signal();
906 return NS_OK;
909 NS_IMETHODIMP
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;
916 return NS_OK;
919 NS_IMETHODIMP
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;
927 return NS_OK;
930 NS_IMETHODIMP
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;
938 return NS_OK;
941 NS_IMETHODIMP
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,
948 dnsRecord, result);
951 NS_IMETHODIMP
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,
961 dnsRecord);
962 if (NS_FAILED(rv)) {
963 return rv;
966 trans.forget(result);
967 return NS_OK;
970 NS_IMETHODIMP
971 nsSocketTransportService::CreateUnixDomainTransport(
972 nsIFile* aPath, nsISocketTransport** result) {
973 #ifdef XP_UNIX
974 nsresult rv;
976 NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
978 nsAutoCString path;
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);
988 return NS_OK;
989 #else
990 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
991 #endif
994 NS_IMETHODIMP
995 nsSocketTransportService::CreateUnixDomainAbstractAddressTransport(
996 const nsACString& aName, nsISocketTransport** result) {
997 // Abstract socket address is supported on Linux only
998 #ifdef XP_LINUX
999 RefPtr<nsSocketTransport> trans = new nsSocketTransport();
1000 // First character of Abstract socket address is null
1001 UniquePtr<char[]> name(new char[aName.Length() + 1]);
1002 *(name.get()) = 0;
1003 memcpy(name.get() + 1, aName.BeginReading(), aName.Length());
1004 nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1);
1005 if (NS_FAILED(rv)) {
1006 return rv;
1009 trans.forget(result);
1010 return NS_OK;
1011 #else
1012 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
1013 #endif
1016 NS_IMETHODIMP
1017 nsSocketTransportService::OnDispatchedEvent() {
1018 #ifndef XP_WIN
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
1028 // redundant code
1029 SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
1030 return NS_OK;
1032 #else
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();
1039 #endif
1041 MutexAutoLock lock(mLock);
1042 if (mPollableEvent) {
1043 mPollableEvent->Signal();
1045 return NS_OK;
1048 NS_IMETHODIMP
1049 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal* thread,
1050 bool mayWait) {
1051 return NS_OK;
1054 NS_IMETHODIMP
1055 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
1056 bool eventWasProcessed) {
1057 return NS_OK;
1060 void nsSocketTransportService::MarkTheLastElementOfPendingQueue() {
1061 mServingPendingQueue = false;
1064 NS_IMETHODIMP
1065 nsSocketTransportService::Run() {
1066 SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
1068 #if defined(XP_WIN)
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
1073 // ..
1074 // If STS-thread is no longer needed this should still be run before exiting
1076 char ignoredStackBuffer[255];
1077 Unused << gethostname(ignoredStackBuffer, 255);
1078 #endif
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
1126 // in poll().
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;
1140 for (;;) {
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());
1153 do {
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(
1173 NewRunnableMethod(
1174 "net::nsSocketTransportService::"
1175 "MarkTheLastElementOfPendingQueue",
1176 this,
1177 &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
1178 nsIEventTarget::DISPATCH_NORMAL);
1179 if (NS_FAILED(rv)) {
1180 NS_WARNING(
1181 "Could not dispatch a new event on the "
1182 "socket thread.");
1183 } else {
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();
1197 do {
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());
1224 break;
1227 MutexAutoLock lock(mLock);
1228 if (mGoingOffline) {
1229 mGoingOffline = false;
1230 goingOffline = true;
1233 // Avoid potential deadlock
1234 if (goingOffline) {
1235 Reset(true);
1239 SOCKET_LOG(("STS shutting down thread\n"));
1241 // detach all sockets, including locals
1242 Reset(false);
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());
1257 return NS_OK;
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);
1269 if (!isGuarded) {
1270 DetachSocket(socketList, &socketList[index]);
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);
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
1291 int32_t i, count;
1293 // poll loop
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) {
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);
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]);
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()));
1355 #if defined(XP_WIN)
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();
1360 #endif
1362 // Measures seconds spent while blocked on PR_Poll
1363 int32_t n = 0;
1364 *pollDuration = nullptr;
1366 if (!gIOService->IsNetTearingDown()) {
1367 // Let's not do polling during shutdown.
1368 #if defined(XP_WIN)
1369 StartPolling();
1370 #endif
1371 n = Poll(pollDuration, now);
1372 #if defined(XP_WIN)
1373 EndPolling();
1374 #endif
1377 now = PR_IntervalNow();
1379 if (n < 0) {
1380 SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
1381 PR_GetOSError()));
1382 } else {
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);
1396 } else {
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)
1413 if (n != 0 &&
1414 (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT)) &&
1415 mPollableEvent &&
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
1423 // on "busy wait".
1424 TryRepairPollableEvent();
1427 if (mPollableEvent &&
1428 !mPollableEvent->IsSignallingAlive(mPollableEventTimeout)) {
1429 SOCKET_LOG(("Pollable event signalling failed/timed out"));
1430 TryRepairPollableEvent();
1435 return NS_OK;
1438 void nsSocketTransportService::UpdateSendBufferPref() {
1439 int32_t bufferSize;
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;
1445 return;
1448 #if defined(XP_WIN)
1449 mSendBufferSize = 131072 * 4;
1450 #endif
1453 nsresult nsSocketTransportService::UpdatePrefs() {
1454 mSendBufferSize = 0;
1456 UpdateSendBufferPref();
1458 // Default TCP Keepalive Values.
1459 int32_t keepaliveIdleTimeS;
1460 nsresult rv =
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);
1524 if (!rv) {
1525 NS_ERROR(
1526 "network.socket.forcePort preference is ill-formed, this will likely "
1527 "make everything unexpectedly fail!");
1531 return NS_OK;
1534 void nsSocketTransportService::OnKeepaliveEnabledPrefChange() {
1535 // Dispatch to socket thread if we're not executing there.
1536 if (!OnSocketThread()) {
1537 gSocketTransportService->Dispatch(
1538 NewRunnableMethod(
1539 "net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this,
1540 &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
1541 NS_DISPATCH_NORMAL);
1542 return;
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) {
1563 return;
1566 sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
1569 NS_IMETHODIMP
1570 nsSocketTransportService::GetName(nsACString& aName) {
1571 aName.AssignLiteral("nsSocketTransportService");
1572 return NS_OK;
1575 NS_IMETHODIMP
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)) {
1582 return NS_OK;
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;
1602 #if defined(XP_WIN)
1603 if (timer == mPollRepairTimer) {
1604 DoPollRepair();
1606 #endif
1608 } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
1609 mSleepPhase = true;
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")) {
1620 ShutdownThread();
1621 } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1622 mLastNetworkLinkChangeTime = PR_IntervalNow();
1625 return NS_OK;
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();
1645 NS_IMETHODIMP
1646 nsSocketTransportService::GetSendBufferSize(int32_t* value) {
1647 *value = mSendBufferSize;
1648 return NS_OK;
1651 /// ugly OS specific includes are placed at the bottom of the src for clarity
1653 #if defined(XP_WIN)
1654 # include <windows.h>
1655 #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1656 # include <sys/resource.h>
1657 #endif
1659 // Right now the only need to do this is on windows.
1660 #if defined(XP_WIN)
1661 void nsSocketTransportService::ProbeMaxCount() {
1662 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1664 if (mProbedMaxCount) {
1665 return;
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).
1674 // Allocate
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;
1686 else
1687 gMaxCount = index;
1688 break;
1690 ++numAllocated;
1693 // Test
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));
1700 if (rv >= 0) break;
1702 SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
1703 gMaxCount, rv, PR_GetError()));
1705 gMaxCount -= 32;
1706 if (gMaxCount <= SOCKET_LIMIT_MIN) {
1707 gMaxCount = SOCKET_LIMIT_MIN;
1708 break;
1712 // Free
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));
1719 #endif // windows
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
1732 return PR_SUCCESS;
1735 if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
1736 gMaxCount = SOCKET_LIMIT_TARGET;
1737 return PR_SUCCESS;
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;
1762 #else
1763 // other platforms are harder to test - so leave at safe legacy value
1764 #endif
1766 return PR_SUCCESS;
1769 // Used to return connection info to Dashboard.cpp
1770 void nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo>* data,
1771 SocketContext* context,
1772 bool aActive) {
1773 if (context->mHandler->mIsPrivate) {
1774 return;
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};
1784 uint16_t port;
1785 const char* type_desc;
1787 if (type == PR_DESC_SOCKET_TCP) {
1788 type_desc = "TCP";
1789 PRNetAddr peer_addr;
1790 PodZero(&peer_addr);
1792 PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
1793 if (rv != PR_SUCCESS) {
1794 return;
1797 rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
1798 if (rv != PR_SUCCESS) {
1799 return;
1802 if (peer_addr.raw.family == PR_AF_INET) {
1803 port = peer_addr.inet.port;
1804 } else {
1805 port = peer_addr.ipv6.port;
1807 port = PR_ntohs(port);
1808 } else {
1809 if (type == PR_DESC_SOCKET_UDP) {
1810 type_desc = "UDP";
1811 } else {
1812 type_desc = "other";
1814 NetAddr addr;
1815 if (context->mHandler->GetRemoteAddr(&addr) != NS_OK) {
1816 return;
1818 if (!addr.ToStringBuffer(host, sizeof(host))) {
1819 return;
1821 if (addr.GetPort(&port) != NS_OK) {
1822 return;
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;
1849 #if defined(XP_WIN)
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);
1867 }));
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);
1881 mPolling = true;
1884 void nsSocketTransportService::EndPolling() {
1885 MutexAutoLock lock(mLock);
1886 mPolling = false;
1887 if (mPollRepairTimer) {
1888 mPollRepairTimer->Cancel();
1892 #endif
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;
1902 SOCKET_LOG(
1903 ("running socket transport thread without "
1904 "a pollable event now valid=%d",
1905 !!mPollableEvent));
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;
1911 NS_IMETHODIMP
1912 nsSocketTransportService::AddShutdownObserver(
1913 nsISTSShutdownObserver* aObserver) {
1914 mShutdownObservers.AppendElement(aObserver);
1915 return NS_OK;
1918 NS_IMETHODIMP
1919 nsSocketTransportService::RemoveShutdownObserver(
1920 nsISTSShutdownObserver* aObserver) {
1921 mShutdownObservers.RemoveElement(aObserver);
1922 return NS_OK;
1925 } // namespace net
1926 } // namespace mozilla