Source code

Revision control

Other Tools

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