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::UpdatePrefs(const char* aPref, void* aSelf) {
591
static_cast<nsSocketTransportService*>(aSelf)->UpdatePrefs();
592
}
593
594
// called from main thread only
595
NS_IMETHODIMP
596
nsSocketTransportService::Init() {
597
if (!NS_IsMainThread()) {
598
NS_ERROR("wrong thread");
599
return NS_ERROR_UNEXPECTED;
600
}
601
602
if (mInitialized) return NS_OK;
603
604
if (mShuttingDown) return NS_ERROR_UNEXPECTED;
605
606
nsCOMPtr<nsIThread> thread;
607
nsresult rv =
608
NS_NewNamedThread("Socket Thread", getter_AddRefs(thread), this);
609
if (NS_FAILED(rv)) return rv;
610
611
{
612
MutexAutoLock lock(mLock);
613
// Install our mThread, protecting against concurrent readers
614
thread.swap(mThread);
615
}
616
617
Preferences::RegisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
618
UpdatePrefs();
619
620
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
621
if (obsSvc) {
622
obsSvc->AddObserver(this, "profile-initial-state", false);
623
obsSvc->AddObserver(this, "last-pb-context-exited", false);
624
obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
625
obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
626
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
627
obsSvc->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
628
}
629
630
mInitialized = true;
631
return NS_OK;
632
}
633
634
// called from main thread only
635
NS_IMETHODIMP
636
nsSocketTransportService::Shutdown(bool aXpcomShutdown) {
637
SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
638
639
NS_ENSURE_STATE(NS_IsMainThread());
640
641
if (!mInitialized) return NS_OK;
642
643
if (mShuttingDown) return NS_ERROR_UNEXPECTED;
644
645
{
646
MutexAutoLock lock(mLock);
647
648
// signal the socket thread to shutdown
649
mShuttingDown = true;
650
651
if (mPollableEvent) {
652
mPollableEvent->Signal();
653
}
654
}
655
656
if (!aXpcomShutdown) {
657
return ShutdownThread();
658
}
659
660
return NS_OK;
661
}
662
663
nsresult nsSocketTransportService::ShutdownThread() {
664
SOCKET_LOG(("nsSocketTransportService::ShutdownThread\n"));
665
666
NS_ENSURE_STATE(NS_IsMainThread());
667
668
if (!mInitialized || !mShuttingDown) return NS_OK;
669
670
// join with thread
671
mThread->Shutdown();
672
{
673
MutexAutoLock lock(mLock);
674
// Drop our reference to mThread and make sure that any concurrent
675
// readers are excluded
676
mThread = nullptr;
677
}
678
679
Preferences::UnregisterCallbacks(UpdatePrefs, gCallbackPrefs, this);
680
681
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
682
if (obsSvc) {
683
obsSvc->RemoveObserver(this, "profile-initial-state");
684
obsSvc->RemoveObserver(this, "last-pb-context-exited");
685
obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
686
obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
687
obsSvc->RemoveObserver(this, "xpcom-shutdown-threads");
688
obsSvc->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
689
}
690
691
if (mAfterWakeUpTimer) {
692
mAfterWakeUpTimer->Cancel();
693
mAfterWakeUpTimer = nullptr;
694
}
695
696
IOActivityMonitor::Shutdown();
697
698
mInitialized = false;
699
mShuttingDown = false;
700
701
return NS_OK;
702
}
703
704
NS_IMETHODIMP
705
nsSocketTransportService::GetOffline(bool* offline) {
706
*offline = mOffline;
707
return NS_OK;
708
}
709
710
NS_IMETHODIMP
711
nsSocketTransportService::SetOffline(bool offline) {
712
MutexAutoLock lock(mLock);
713
if (!mOffline && offline) {
714
// signal the socket thread to go offline, so it will detach sockets
715
mGoingOffline = true;
716
mOffline = true;
717
} else if (mOffline && !offline) {
718
mOffline = false;
719
}
720
if (mPollableEvent) {
721
mPollableEvent->Signal();
722
}
723
724
return NS_OK;
725
}
726
727
NS_IMETHODIMP
728
nsSocketTransportService::GetKeepaliveIdleTime(int32_t* aKeepaliveIdleTimeS) {
729
MOZ_ASSERT(aKeepaliveIdleTimeS);
730
if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
731
return NS_ERROR_NULL_POINTER;
732
}
733
*aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
734
return NS_OK;
735
}
736
737
NS_IMETHODIMP
738
nsSocketTransportService::GetKeepaliveRetryInterval(
739
int32_t* aKeepaliveRetryIntervalS) {
740
MOZ_ASSERT(aKeepaliveRetryIntervalS);
741
if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
742
return NS_ERROR_NULL_POINTER;
743
}
744
*aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
745
return NS_OK;
746
}
747
748
NS_IMETHODIMP
749
nsSocketTransportService::GetKeepaliveProbeCount(
750
int32_t* aKeepaliveProbeCount) {
751
MOZ_ASSERT(aKeepaliveProbeCount);
752
if (NS_WARN_IF(!aKeepaliveProbeCount)) {
753
return NS_ERROR_NULL_POINTER;
754
}
755
*aKeepaliveProbeCount = mKeepaliveProbeCount;
756
return NS_OK;
757
}
758
759
NS_IMETHODIMP
760
nsSocketTransportService::CreateTransport(const nsTArray<nsCString>& types,
761
const nsACString& host, int32_t port,
762
nsIProxyInfo* proxyInfo,
763
nsISocketTransport** result) {
764
return CreateRoutedTransport(types, host, port, NS_LITERAL_CSTRING(""), 0,
765
proxyInfo, result);
766
}
767
768
NS_IMETHODIMP
769
nsSocketTransportService::CreateRoutedTransport(
770
const nsTArray<nsCString>& types, const nsACString& host, int32_t port,
771
const nsACString& hostRoute, int32_t portRoute, nsIProxyInfo* proxyInfo,
772
nsISocketTransport** result) {
773
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
774
NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
775
776
RefPtr<nsSocketTransport> trans = new nsSocketTransport();
777
nsresult rv = trans->Init(types, host, port, hostRoute, portRoute, proxyInfo);
778
if (NS_FAILED(rv)) {
779
return rv;
780
}
781
782
trans.forget(result);
783
return NS_OK;
784
}
785
786
NS_IMETHODIMP
787
nsSocketTransportService::CreateUnixDomainTransport(
788
nsIFile* aPath, nsISocketTransport** result) {
789
#ifdef XP_UNIX
790
nsresult rv;
791
792
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
793
794
nsAutoCString path;
795
rv = aPath->GetNativePath(path);
796
if (NS_FAILED(rv)) return rv;
797
798
RefPtr<nsSocketTransport> trans = new nsSocketTransport();
799
800
rv = trans->InitWithFilename(path.get());
801
if (NS_FAILED(rv)) return rv;
802
803
trans.forget(result);
804
return NS_OK;
805
#else
806
return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
807
#endif
808
}
809
810
NS_IMETHODIMP
811
nsSocketTransportService::CreateUnixDomainAbstractAddressTransport(
812
const nsACString& aName, nsISocketTransport** result) {
813
// Abstract socket address is supported on Linux only
814
#ifdef XP_LINUX
815
RefPtr<nsSocketTransport> trans = new nsSocketTransport();
816
// First character of Abstract socket address is null
817
UniquePtr<char[]> name(new char[aName.Length() + 1]);
818
*(name.get()) = 0;
819
memcpy(name.get() + 1, aName.BeginReading(), aName.Length());
820
nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1);
821
if (NS_FAILED(rv)) {
822
return rv;
823
}
824
825
trans.forget(result);
826
return NS_OK;
827
#else
828
return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
829
#endif
830
}
831
832
NS_IMETHODIMP
833
nsSocketTransportService::OnDispatchedEvent() {
834
#ifndef XP_WIN
835
// On windows poll can hang and this became worse when we introduced the
836
// patch for bug 698882 (see also bug 1292181), therefore we reverted the
837
// behavior on windows to be as before bug 698882, e.g. write to the socket
838
// also if an event dispatch is on the socket thread and writing to the
839
// socket for each event.
840
if (OnSocketThread()) {
841
// this check is redundant to one done inside ::Signal(), but
842
// we can do it here and skip obtaining the lock - given that
843
// this is a relatively common occurance its worth the
844
// redundant code
845
SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
846
return NS_OK;
847
}
848
#else
849
if (gIOService->IsNetTearingDown()) {
850
// Poll can hang sometimes. If we are in shutdown, we are going to
851
// start a watchdog. If we do not exit poll within
852
// REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
853
StartPollWatchdog();
854
}
855
#endif
856
857
MutexAutoLock lock(mLock);
858
if (mPollableEvent) {
859
mPollableEvent->Signal();
860
}
861
return NS_OK;
862
}
863
864
NS_IMETHODIMP
865
nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal* thread,
866
bool mayWait) {
867
return NS_OK;
868
}
869
870
NS_IMETHODIMP
871
nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
872
bool eventWasProcessed) {
873
return NS_OK;
874
}
875
876
void nsSocketTransportService::MarkTheLastElementOfPendingQueue() {
877
mServingPendingQueue = false;
878
}
879
880
NS_IMETHODIMP
881
nsSocketTransportService::Run() {
882
SOCKET_LOG(("STS thread init %d sockets\n", gMaxCount));
883
884
#if defined(XP_WIN)
885
// see bug 1361495, gethostname() triggers winsock initialization.
886
// so do it here (on parent and child) to protect against it being done first
887
// accidentally on the main thread.. especially via PR_GetSystemInfo(). This
888
// will also improve latency of first real winsock operation
889
// ..
890
// If STS-thread is no longer needed this should still be run before exiting
891
892
char ignoredStackBuffer[255];
893
Unused << gethostname(ignoredStackBuffer, 255);
894
#endif
895
896
psm::InitializeSSLServerCertVerificationThreads();
897
898
gSocketThread = PR_GetCurrentThread();
899
900
{
901
MutexAutoLock lock(mLock);
902
mPollableEvent.reset(new PollableEvent());
903
//
904
// NOTE: per bug 190000, this failure could be caused by Zone-Alarm
905
// or similar software.
906
//
907
// NOTE: per bug 191739, this failure could also be caused by lack
908
// of a loopback device on Windows and OS/2 platforms (it creates
909
// a loopback socket pair on these platforms to implement a pollable
910
// event object). if we can't create a pollable event, then we'll
911
// have to "busy wait" to implement the socket event queue :-(
912
//
913
if (!mPollableEvent->Valid()) {
914
mPollableEvent = nullptr;
915
NS_WARNING("running socket transport thread without a pollable event");
916
SOCKET_LOG(("running socket transport thread without a pollable event"));
917
}
918
919
mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
920
mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
921
mPollList[0].out_flags = 0;
922
}
923
924
mRawThread = NS_GetCurrentThread();
925
926
// hook ourselves up to observe event processing for this thread
927
nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
928
threadInt->SetObserver(this);
929
930
// make sure the pseudo random number generator is seeded on this thread
931
srand(static_cast<unsigned>(PR_Now()));
932
933
// For the calculation of the duration of the last cycle (i.e. the last
934
// for-loop iteration before shutdown).
935
TimeStamp startOfCycleForLastCycleCalc;
936
int numberOfPendingEventsLastCycle;
937
938
// For measuring of the poll iteration duration without time spent blocked
939
// in poll().
940
TimeStamp pollCycleStart;
941
// Time blocked in poll().
942
TimeDuration singlePollDuration;
943
944
// For calculating the time needed for a new element to run.
945
TimeStamp startOfIteration;
946
TimeStamp startOfNextIteration;
947
int numberOfPendingEvents;
948
949
// If there is too many pending events queued, we will run some poll()
950
// between them and the following variable is cumulative time spent
951
// blocking in poll().
952
TimeDuration pollDuration;
953
954
for (;;) {
955
bool pendingEvents = false;
956
957
numberOfPendingEvents = 0;
958
numberOfPendingEventsLastCycle = 0;
959
if (Telemetry::CanRecordPrereleaseData()) {
960
startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
961
startOfNextIteration = TimeStamp::NowLoRes();
962
}
963
pollDuration = nullptr;
964
965
do {
966
if (Telemetry::CanRecordPrereleaseData()) {
967
pollCycleStart = TimeStamp::NowLoRes();
968
}
969
970
DoPollIteration(&singlePollDuration);
971
972
if (Telemetry::CanRecordPrereleaseData() && !pollCycleStart.IsNull()) {
973
Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME,
974
singlePollDuration.ToMilliseconds());
975
Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_CYCLE,
976
pollCycleStart + singlePollDuration,
977
TimeStamp::NowLoRes());
978
pollDuration += singlePollDuration;
979
}
980
981
mRawThread->HasPendingEvents(&pendingEvents);
982
if (pendingEvents) {
983
if (!mServingPendingQueue) {
984
nsresult rv = Dispatch(
985
NewRunnableMethod(
986
"net::nsSocketTransportService::"
987
"MarkTheLastElementOfPendingQueue",
988
this,
989
&nsSocketTransportService::MarkTheLastElementOfPendingQueue),
990
nsIEventTarget::DISPATCH_NORMAL);
991
if (NS_FAILED(rv)) {
992
NS_WARNING(
993
"Could not dispatch a new event on the "
994
"socket thread.");
995
} else {
996
mServingPendingQueue = true;
997
}
998
999
if (Telemetry::CanRecordPrereleaseData()) {
1000
startOfIteration = startOfNextIteration;
1001
// Everything that comes after this point will
1002
// be served in the next iteration. If no even
1003
// arrives, startOfNextIteration will be reset at the
1004
// beginning of each for-loop.
1005
startOfNextIteration = TimeStamp::NowLoRes();
1006
}
1007
}
1008
TimeStamp eventQueueStart = TimeStamp::NowLoRes();
1009
do {
1010
NS_ProcessNextEvent(mRawThread);
1011
numberOfPendingEvents++;
1012
pendingEvents = false;
1013
mRawThread->HasPendingEvents(&pendingEvents);
1014
} while (pendingEvents && mServingPendingQueue &&
1015
((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() <
1016
mMaxTimePerPollIter));
1017
1018
if (Telemetry::CanRecordPrereleaseData() && !mServingPendingQueue &&
1019
!startOfIteration.IsNull()) {
1020
Telemetry::AccumulateTimeDelta(Telemetry::STS_POLL_AND_EVENTS_CYCLE,
1021
startOfIteration + pollDuration,
1022
TimeStamp::NowLoRes());
1023
1024
Telemetry::Accumulate(Telemetry::STS_NUMBER_OF_PENDING_EVENTS,
1025
numberOfPendingEvents);
1026
1027
numberOfPendingEventsLastCycle += numberOfPendingEvents;
1028
numberOfPendingEvents = 0;
1029
pollDuration = nullptr;
1030
}
1031
}
1032
} while (pendingEvents);
1033
1034
bool goingOffline = false;
1035
// now that our event queue is empty, check to see if we should exit
1036
{
1037
MutexAutoLock lock(mLock);
1038
if (mShuttingDown) {
1039
if (Telemetry::CanRecordPrereleaseData() &&
1040
!startOfCycleForLastCycleCalc.IsNull()) {
1041
Telemetry::Accumulate(
1042
Telemetry::STS_NUMBER_OF_PENDING_EVENTS_IN_THE_LAST_CYCLE,
1043
numberOfPendingEventsLastCycle);
1044
Telemetry::AccumulateTimeDelta(
1045
Telemetry::STS_POLL_AND_EVENT_THE_LAST_CYCLE,
1046
startOfCycleForLastCycleCalc, TimeStamp::NowLoRes());
1047
}
1048
break;
1049
}
1050
if (mGoingOffline) {
1051
mGoingOffline = false;
1052
goingOffline = true;
1053
}
1054
}
1055
// Avoid potential deadlock
1056
if (goingOffline) Reset(true);
1057
}
1058
1059
SOCKET_LOG(("STS shutting down thread\n"));
1060
1061
// detach all sockets, including locals
1062
Reset(false);
1063
1064
// We don't clear gSocketThread so that OnSocketThread() won't be a false
1065
// alarm for events generated by stopping the SLL threads during shutdown.
1066
psm::StopSSLServerCertVerificationThreads();
1067
1068
// Final pass over the event queue. This makes sure that events posted by
1069
// socket detach handlers get processed.
1070
NS_ProcessPendingEvents(mRawThread);
1071
1072
SOCKET_LOG(("STS thread exit\n"));
1073
1074
return NS_OK;
1075
}
1076
1077
void nsSocketTransportService::DetachSocketWithGuard(bool aGuardLocals,
1078
SocketContext* socketList,
1079
int32_t index) {
1080
bool isGuarded = false;
1081
if (aGuardLocals) {
1082
socketList[index].mHandler->IsLocal(&isGuarded);
1083
if (!isGuarded) socketList[index].mHandler->KeepWhenOffline(&isGuarded);
1084
}
1085
if (!isGuarded) DetachSocket(socketList, &socketList[index]);
1086
}
1087
1088
void nsSocketTransportService::Reset(bool aGuardLocals) {
1089
// detach any sockets
1090
int32_t i;
1091
for (i = mActiveCount - 1; i >= 0; --i) {
1092
DetachSocketWithGuard(aGuardLocals, mActiveList, i);
1093
}
1094
for (i = mIdleCount - 1; i >= 0; --i) {
1095
DetachSocketWithGuard(aGuardLocals, mIdleList, i);
1096
}
1097
}
1098
1099
nsresult nsSocketTransportService::DoPollIteration(TimeDuration* pollDuration) {
1100
SOCKET_LOG(("STS poll iter\n"));
1101
1102
PRIntervalTime now = PR_IntervalNow();
1103
1104
int32_t i, count;
1105
//
1106
// poll loop
1107
//
1108
// walk active list backwards to see if any sockets should actually be
1109
// idle, then walk the idle list backwards to see if any idle sockets
1110
// should become active. take care to check only idle sockets that
1111
// were idle to begin with ;-)
1112
//
1113
count = mIdleCount;
1114
for (i = mActiveCount - 1; i >= 0; --i) {
1115
//---
1116
SOCKET_LOG((" active [%u] { handler=%p condition=%" PRIx32
1117
" pollflags=%hu }\n",
1118
i, mActiveList[i].mHandler,
1119
static_cast<uint32_t>(mActiveList[i].mHandler->mCondition),
1120
mActiveList[i].mHandler->mPollFlags));
1121
//---
1122
if (NS_FAILED(mActiveList[i].mHandler->mCondition)) {
1123
DetachSocket(mActiveList, &mActiveList[i]);
1124
} else {
1125
uint16_t in_flags = mActiveList[i].mHandler->mPollFlags;
1126
if (in_flags == 0) {
1127
MoveToIdleList(&mActiveList[i]);
1128
} else {
1129
// update poll flags
1130
mPollList[i + 1].in_flags = in_flags;
1131
mPollList[i + 1].out_flags = 0;
1132
mActiveList[i].EnsureTimeout(now);
1133
}
1134
}
1135
}
1136
for (i = count - 1; i >= 0; --i) {
1137
//---
1138
SOCKET_LOG((" idle [%u] { handler=%p condition=%" PRIx32
1139
" pollflags=%hu }\n",
1140
i, mIdleList[i].mHandler,
1141
static_cast<uint32_t>(mIdleList[i].mHandler->mCondition),
1142
mIdleList[i].mHandler->mPollFlags));
1143
//---
1144
if (NS_FAILED(mIdleList[i].mHandler->mCondition))
1145
DetachSocket(mIdleList, &mIdleList[i]);
1146
else if (mIdleList[i].mHandler->mPollFlags != 0)
1147
MoveToPollList(&mIdleList[i]);
1148
}
1149
1150
{
1151
MutexAutoLock lock(mLock);
1152
if (mPollableEvent) {
1153
// we want to make sure the timeout is measured from the time
1154
// we enter poll(). This method resets the timestamp to 'now',
1155
// if we were first signalled between leaving poll() and here.
1156
// If we didn't do this and processing events took longer than
1157
// the allowed signal timeout, we would detect it as a
1158
// false-positive. AdjustFirstSignalTimestamp is then a no-op
1159
// until mPollableEvent->Clear() is called.
1160
mPollableEvent->AdjustFirstSignalTimestamp();
1161
}
1162
}
1163
1164
SOCKET_LOG(
1165
(" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
1166
1167
#if defined(XP_WIN)
1168
// 30 active connections is the historic limit before firefox 7's 256. A few
1169
// windows systems have troubles with the higher limit, so actively probe a
1170
// limit the first time we exceed 30.
1171
if ((mActiveCount > 30) && !mProbedMaxCount) ProbeMaxCount();
1172
#endif
1173
1174
// Measures seconds spent while blocked on PR_Poll
1175
int32_t n = 0;
1176
*pollDuration = nullptr;
1177
1178
if (!gIOService->IsNetTearingDown()) {
1179
// Let's not do polling during shutdown.
1180
#if defined(XP_WIN)
1181
StartPolling();
1182
#endif
1183
n = Poll(pollDuration, now);
1184
#if defined(XP_WIN)
1185
EndPolling();
1186
#endif
1187
}
1188
1189
now = PR_IntervalNow();
1190
1191
if (n < 0) {
1192
SOCKET_LOG((" PR_Poll error [%d] os error [%d]\n", PR_GetError(),
1193
PR_GetOSError()));
1194
} else {
1195
//
1196
// service "active" sockets...
1197
//
1198
uint32_t numberOfOnSocketReadyCalls = 0;
1199
for (i = 0; i < int32_t(mActiveCount); ++i) {
1200
PRPollDesc& desc = mPollList[i + 1];
1201
SocketContext& s = mActiveList[i];
1202
if (n > 0 && desc.out_flags != 0) {
1203
#ifdef MOZ_TASK_TRACER
1204
tasktracer::AutoSourceEvent taskTracerEvent(
1205
tasktracer::SourceEventType::SocketIO);
1206
#endif
1207
s.DisengageTimeout();
1208
s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
1209
numberOfOnSocketReadyCalls++;
1210
} else if (s.IsTimedOut(now)) {
1211
#ifdef MOZ_TASK_TRACER
1212
tasktracer::AutoSourceEvent taskTracerEvent(
1213
tasktracer::SourceEventType::SocketIO);
1214
#endif
1215
SOCKET_LOG(("socket %p timed out", s.mHandler));
1216
s.DisengageTimeout();
1217
s.mHandler->OnSocketReady(desc.fd, -1);
1218
numberOfOnSocketReadyCalls++;
1219
} else {
1220
s.MaybeResetEpoch();
1221
}
1222
}
1223
if (Telemetry::CanRecordPrereleaseData()) {
1224
Telemetry::Accumulate(Telemetry::STS_NUMBER_OF_ONSOCKETREADY_CALLS,
1225
numberOfOnSocketReadyCalls);
1226
}
1227
1228
//
1229
// check for "dead" sockets and remove them (need to do this in
1230
// reverse order obviously).
1231
//
1232
for (i = mActiveCount - 1; i >= 0; --i) {
1233
if (NS_FAILED(mActiveList[i].mHandler->mCondition))
1234
DetachSocket(mActiveList, &mActiveList[i]);
1235
}
1236
1237
{
1238
MutexAutoLock lock(mLock);
1239
// acknowledge pollable event (should not block)
1240
if (n != 0 &&
1241
(mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT)) &&
1242
mPollableEvent &&
1243
((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
1244
!mPollableEvent->Clear())) {
1245
// On Windows, the TCP loopback connection in the
1246
// pollable event may become broken when a laptop
1247
// switches between wired and wireless networks or
1248
// wakes up from hibernation. We try to create a
1249
// new pollable event. If that fails, we fall back
1250
// on "busy wait".
1251
TryRepairPollableEvent();
1252
}
1253
1254
if (mPollableEvent &&
1255
!mPollableEvent->IsSignallingAlive(mPollableEventTimeout)) {
1256
SOCKET_LOG(("Pollable event signalling failed/timed out"));
1257
TryRepairPollableEvent();
1258
}
1259
}
1260
}
1261
1262
return NS_OK;
1263
}
1264
1265
void nsSocketTransportService::UpdateSendBufferPref() {
1266
int32_t bufferSize;
1267
1268
// If the pref is set, honor it. 0 means use OS defaults.
1269
nsresult rv = Preferences::GetInt(SEND_BUFFER_PREF, &bufferSize);
1270
if (NS_SUCCEEDED(rv)) {
1271
mSendBufferSize = bufferSize;
1272
return;
1273
}
1274
1275
#if defined(XP_WIN)
1276
mSendBufferSize = 131072 * 4;
1277
#endif
1278
}
1279
1280
nsresult nsSocketTransportService::UpdatePrefs() {
1281
mSendBufferSize = 0;
1282
1283
UpdateSendBufferPref();
1284
1285
// Default TCP Keepalive Values.
1286
int32_t keepaliveIdleTimeS;
1287
nsresult rv =
1288
Preferences::GetInt(KEEPALIVE_IDLE_TIME_PREF, &keepaliveIdleTimeS);
1289
if (NS_SUCCEEDED(rv))
1290
mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS, 1, kMaxTCPKeepIdle);
1291
1292
int32_t keepaliveRetryIntervalS;
1293
rv = Preferences::GetInt(KEEPALIVE_RETRY_INTERVAL_PREF,
1294
&keepaliveRetryIntervalS);
1295
if (NS_SUCCEEDED(rv))
1296
mKeepaliveRetryIntervalS =
1297
clamped(keepaliveRetryIntervalS, 1, kMaxTCPKeepIntvl);
1298
1299
int32_t keepaliveProbeCount;
1300
rv = Preferences::GetInt(KEEPALIVE_PROBE_COUNT_PREF, &keepaliveProbeCount);
1301
if (NS_SUCCEEDED(rv))
1302
mKeepaliveProbeCount = clamped(keepaliveProbeCount, 1, kMaxTCPKeepCount);
1303
bool keepaliveEnabled = false;
1304
rv = Preferences::GetBool(KEEPALIVE_ENABLED_PREF, &keepaliveEnabled);
1305
if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
1306
mKeepaliveEnabledPref = keepaliveEnabled;
1307
OnKeepaliveEnabledPrefChange();
1308
}
1309
1310
int32_t maxTimePref;
1311
rv = Preferences::GetInt(MAX_TIME_BETWEEN_TWO_POLLS, &maxTimePref);
1312
if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
1313
mMaxTimePerPollIter = maxTimePref;
1314
}
1315
1316
int32_t pollBusyWaitPeriod;
1317
rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD, &pollBusyWaitPeriod);
1318
if (NS_SUCCEEDED(rv) && pollBusyWaitPeriod > 0) {
1319
mNetworkLinkChangeBusyWaitPeriod = PR_SecondsToInterval(pollBusyWaitPeriod);
1320
}
1321
1322
int32_t pollBusyWaitPeriodTimeout;
1323
rv = Preferences::GetInt(POLL_BUSY_WAIT_PERIOD_TIMEOUT,
1324
&pollBusyWaitPeriodTimeout);
1325
if (NS_SUCCEEDED(rv) && pollBusyWaitPeriodTimeout > 0) {
1326
mNetworkLinkChangeBusyWaitTimeout =
1327
PR_SecondsToInterval(pollBusyWaitPeriodTimeout);
1328
}
1329
1330
int32_t maxTimeForPrClosePref;
1331
rv = Preferences::GetInt(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
1332
&maxTimeForPrClosePref);
1333
if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >= 0) {
1334
mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
1335
}
1336
1337
int32_t pollableEventTimeout;
1338
rv = Preferences::GetInt(POLLABLE_EVENT_TIMEOUT, &pollableEventTimeout);
1339
if (NS_SUCCEEDED(rv) && pollableEventTimeout >= 0) {
1340
MutexAutoLock lock(mLock);
1341
mPollableEventTimeout = TimeDuration::FromSeconds(pollableEventTimeout);
1342
}
1343
1344
bool esniPref = false;
1345
rv = Preferences::GetBool(ESNI_ENABLED, &esniPref);
1346
if (NS_SUCCEEDED(rv)) {
1347
mEsniEnabled = esniPref;
1348
}
1349
1350
bool esniMitmPref = false;
1351
rv = Preferences::GetBool(ESNI_DISABLED_MITM, &esniMitmPref);
1352
if (NS_SUCCEEDED(rv)) {
1353
mTrustedMitmDetected = esniMitmPref;
1354
}
1355
1356
return NS_OK;
1357
}
1358
1359
void nsSocketTransportService::OnKeepaliveEnabledPrefChange() {
1360
// Dispatch to socket thread if we're not executing there.
1361
if (!OnSocketThread()) {
1362
gSocketTransportService->Dispatch(
1363
NewRunnableMethod(
1364
"net::nsSocketTransportService::OnKeepaliveEnabledPrefChange", this,
1365
&nsSocketTransportService::OnKeepaliveEnabledPrefChange),
1366
NS_DISPATCH_NORMAL);
1367
return;
1368
}
1369
1370
SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
1371
mKeepaliveEnabledPref ? "enabled" : "disabled"));
1372
1373
// Notify each socket that keepalive has been en/disabled globally.
1374
for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1375
NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
1376
}
1377
for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1378
NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
1379
}
1380
}
1381
1382
void nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(
1383
SocketContext* sock) {
1384
MOZ_ASSERT(sock, "SocketContext cannot be null!");
1385
MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
1386
1387
if (!sock || !sock->mHandler) {
1388
return;
1389
}
1390
1391
#ifdef MOZ_TASK_TRACER
1392
tasktracer::AutoSourceEvent taskTracerEvent(
1393
tasktracer::SourceEventType::SocketIO);
1394
#endif
1395
sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
1396
}
1397
1398
NS_IMETHODIMP
1399
nsSocketTransportService::Observe(nsISupports* subject, const char* topic,
1400
const char16_t* data) {
1401
SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic));
1402
1403
if (!strcmp(topic, "profile-initial-state")) {
1404
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
1405
return NS_OK;
1406
}
1407
return net::IOActivityMonitor::Init();
1408
}
1409
1410
if (!strcmp(topic, "last-pb-context-exited")) {
1411
nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
1412
"net::nsSocketTransportService::ClosePrivateConnections", this,
1413
&nsSocketTransportService::ClosePrivateConnections);
1414
nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
1415
NS_ENSURE_SUCCESS(rv, rv);
1416
}
1417
1418
if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
1419
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
1420
if (timer == mAfterWakeUpTimer) {
1421
mAfterWakeUpTimer = nullptr;
1422
mSleepPhase = false;
1423
}
1424
1425
#if defined(XP_WIN)
1426
if (timer == mPollRepairTimer) {
1427
DoPollRepair();
1428
}
1429
#endif
1430
1431
} else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
1432
mSleepPhase = true;
1433
if (mAfterWakeUpTimer) {
1434
mAfterWakeUpTimer->Cancel();
1435
mAfterWakeUpTimer = nullptr;
1436
}
1437
} else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
1438
if (mSleepPhase && !mAfterWakeUpTimer) {
1439
NS_NewTimerWithObserver(getter_AddRefs(mAfterWakeUpTimer), this, 2000,
1440
nsITimer::TYPE_ONE_SHOT);
1441
}
1442
} else if (!strcmp(topic, "xpcom-shutdown-threads")) {
1443
ShutdownThread();
1444
} else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1445
mLastNetworkLinkChangeTime = PR_IntervalNow();
1446
mNotTrustedMitmDetected = false;
1447
}
1448
1449
return NS_OK;
1450
}
1451
1452
void nsSocketTransportService::ClosePrivateConnections() {
1453
// Must be called on the socket thread.
1454
#ifdef DEBUG
1455
bool onSTSThread;
1456
IsOnCurrentThread(&onSTSThread);
1457
MOZ_ASSERT(onSTSThread);
1458
#endif
1459
1460
for (int32_t i = mActiveCount - 1; i >= 0; --i) {
1461
if (mActiveList[i].mHandler->mIsPrivate) {
1462
DetachSocket(mActiveList, &mActiveList[i]);
1463
}
1464
}
1465
for (int32_t i = mIdleCount - 1; i >= 0; --i) {
1466
if (mIdleList[i].mHandler->mIsPrivate) {
1467
DetachSocket(mIdleList, &mIdleList[i]);
1468
}
1469
}
1470
1471
ClearPrivateSSLState();
1472
}
1473
1474
NS_IMETHODIMP
1475
nsSocketTransportService::GetSendBufferSize(int32_t* value) {
1476
*value = mSendBufferSize;
1477
return NS_OK;
1478
}
1479
1480
/// ugly OS specific includes are placed at the bottom of the src for clarity
1481
1482
#if defined(XP_WIN)
1483
# include <windows.h>
1484
#elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1485
# include <sys/resource.h>
1486
#endif
1487
1488
// Right now the only need to do this is on windows.
1489
#if defined(XP_WIN)
1490
void nsSocketTransportService::ProbeMaxCount() {
1491
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1492
1493
if (mProbedMaxCount) return;
1494
mProbedMaxCount = true;
1495
1496
// Allocate and test a PR_Poll up to the gMaxCount number of unconnected
1497
// sockets. See bug 692260 - windows should be able to handle 1000 sockets
1498
// in select() without a problem, but LSPs have been known to balk at lower
1499
// numbers. (64 in the bug).
1500
1501
// Allocate
1502
struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
1503
uint32_t numAllocated = 0;
1504
1505
for (uint32_t index = 0; index < gMaxCount; ++index) {
1506
pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
1507
pfd[index].out_flags = 0;
1508
pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET);
1509
if (!pfd[index].fd) {
1510
SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
1511
if (index < SOCKET_LIMIT_MIN)
1512
gMaxCount = SOCKET_LIMIT_MIN;
1513
else
1514
gMaxCount = index;
1515
break;
1516
}
1517
++numAllocated;
1518
}
1519
1520
// Test
1521
static_assert(SOCKET_LIMIT_MIN >= 32U, "Minimum Socket Limit is >= 32");
1522
while (gMaxCount <= numAllocated) {
1523
int32_t rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
1524
1525
SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n", gMaxCount, rv));
1526
1527
if (rv >= 0) break;
1528
1529
SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
1530
gMaxCount, rv, PR_GetError()));
1531
1532
gMaxCount -= 32;
1533
if (gMaxCount <= SOCKET_LIMIT_MIN) {
1534
gMaxCount = SOCKET_LIMIT_MIN;
1535
break;
1536
}
1537
}
1538
1539
// Free
1540
for (uint32_t index = 0; index < numAllocated; ++index)
1541
if (pfd[index].fd) PR_Close(pfd[index].fd);
1542
1543
Telemetry::Accumulate(Telemetry::NETWORK_PROBE_MAXCOUNT, gMaxCount);
1544
SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
1545
}
1546
#endif // windows
1547
1548
PRStatus nsSocketTransportService::DiscoverMaxCount() {
1549
gMaxCount = SOCKET_LIMIT_MIN;
1550
1551
#if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
1552
// On unix and os x network sockets and file
1553
// descriptors are the same. OS X comes defaulted at 256,
1554
// most linux at 1000. We can reliably use [sg]rlimit to
1555
// query that and raise it if needed.
1556
1557
struct rlimit rlimitData;
1558
if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1) // rlimit broken - use min
1559
return PR_SUCCESS;
1560
1561
if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET) { // larger than target!
1562
gMaxCount = SOCKET_LIMIT_TARGET;
1563
return PR_SUCCESS;
1564
}
1565
1566
int32_t maxallowed = rlimitData.rlim_max;
1567
if ((uint32_t)maxallowed <= SOCKET_LIMIT_MIN) {
1568
return PR_SUCCESS; // so small treat as if rlimit is broken
1569
}
1570
1571
if ((maxallowed == -1) || // no hard cap - ok to set target
1572
((uint32_t)maxallowed >= SOCKET_LIMIT_TARGET)) {
1573
maxallowed = SOCKET_LIMIT_TARGET;
1574
}
1575
1576
rlimitData.rlim_cur = maxallowed;
1577
setrlimit(RLIMIT_NOFILE, &rlimitData);
1578
if ((getrlimit(RLIMIT_NOFILE, &rlimitData) != -1) &&
1579
(rlimitData.rlim_cur > SOCKET_LIMIT_MIN)) {
1580
gMaxCount = rlimitData.rlim_cur;
1581
}
1582
1583
#elif defined(XP_WIN) && !defined(WIN_CE)
1584
// >= XP is confirmed to have at least 1000
1585
static_assert(SOCKET_LIMIT_TARGET <= 1000,
1586
"SOCKET_LIMIT_TARGET max value is 1000");
1587
gMaxCount = SOCKET_LIMIT_TARGET;
1588
#else
1589
// other platforms are harder to test - so leave at safe legacy value
1590
#endif
1591
1592
return PR_SUCCESS;
1593
}
1594
1595
// Used to return connection info to Dashboard.cpp
1596
void nsSocketTransportService::AnalyzeConnection(nsTArray<SocketInfo>* data,
1597
struct SocketContext* context,
1598
bool aActive) {
1599
if (context->mHandler->mIsPrivate) return;
1600
PRFileDesc* aFD = context->mFD;
1601
1602
PRFileDesc* idLayer = PR_GetIdentitiesLayer(aFD, PR_NSPR_IO_LAYER);
1603
1604
NS_ENSURE_TRUE_VOID(idLayer);
1605
1606
bool tcp = PR_GetDescType(idLayer) == PR_DESC_SOCKET_TCP;
1607
1608
PRNetAddr peer_addr;
1609
PodZero(&peer_addr);
1610
PRStatus rv = PR_GetPeerName(aFD, &peer_addr);
1611
if (rv != PR_SUCCESS) return;
1612
1613
char host[64] = {0};
1614
rv = PR_NetAddrToString(&peer_addr, host, sizeof(host));
1615
if (rv != PR_SUCCESS) return;
1616
1617
uint16_t port;
1618
if (peer_addr.raw.family == PR_AF_INET)
1619
port = peer_addr.inet.port;
1620
else
1621
port = peer_addr.ipv6.port;
1622
port = PR_ntohs(port);
1623
uint64_t sent = context->mHandler->ByteCountSent();
1624
uint64_t received = context->mHandler->ByteCountReceived();
1625
SocketInfo info = {nsCString(host), sent, received, port, aActive, tcp};
1626
1627
data->AppendElement(info);
1628
}
1629
1630
void nsSocketTransportService::GetSocketConnections(
1631
nsTArray<SocketInfo>* data) {
1632
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1633
for (uint32_t i = 0; i < mActiveCount; i++)
1634
AnalyzeConnection(data, &mActiveList[i], true);
1635
for (uint32_t i = 0; i < mIdleCount; i++)
1636
AnalyzeConnection(data, &mIdleList[i], false);
1637
}
1638
1639
bool nsSocketTransportService::IsTelemetryEnabledAndNotSleepPhase() {
1640
return Telemetry::CanRecordPrereleaseData() && !mSleepPhase;
1641
}
1642
1643
#if defined(XP_WIN)
1644
void nsSocketTransportService::StartPollWatchdog() {
1645
// Start off the timer from a runnable off of the main thread in order to
1646
// avoid a deadlock, see bug 1370448.
1647
RefPtr<nsSocketTransportService> self(this);
1648
NS_DispatchToMainThread(NS_NewRunnableFunction(
1649
"nsSocketTransportService::StartPollWatchdog", [self] {
1650
MutexAutoLock lock(self->mLock);
1651
1652
// Poll can hang sometimes. If we are in shutdown, we are going to start
1653
// a watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
1654
// signal a pollable event again.
1655
MOZ_ASSERT(gIOService->IsNetTearingDown());
1656
if (self->mPolling && !self->mPollRepairTimer) {
1657
NS_NewTimerWithObserver(getter_AddRefs(self->mPollRepairTimer), self,
1658
REPAIR_POLLABLE_EVENT_TIME,
1659
nsITimer::TYPE_REPEATING_SLACK);
1660
}
1661
}));
1662
}
1663
1664
void nsSocketTransportService::DoPollRepair() {
1665
MutexAutoLock lock(mLock);
1666
if (mPolling && mPollableEvent) {
1667
mPollableEvent->Signal();
1668
} else if (mPollRepairTimer) {
1669
mPollRepairTimer->Cancel();
1670
}
1671
}
1672
1673
void nsSocketTransportService::StartPolling() {
1674
MutexAutoLock lock(mLock);
1675
mPolling = true;
1676
}
1677
1678
void nsSocketTransportService::EndPolling() {
1679
MutexAutoLock lock(mLock);
1680
mPolling = false;
1681
if (mPollRepairTimer) {
1682
mPollRepairTimer->Cancel();
1683
}
1684
}
1685
1686
#endif
1687
1688
void nsSocketTransportService::TryRepairPollableEvent() {
1689
mLock.AssertCurrentThreadOwns();
1690
1691
NS_WARNING("Trying to repair mPollableEvent");
1692
mPollableEvent.reset(new PollableEvent());
1693
if (!mPollableEvent->Valid()) {
1694
mPollableEvent = nullptr;
1695
}
1696
SOCKET_LOG(
1697
("running socket transport thread without "
1698
"a pollable event now valid=%d",
1699
!!mPollableEvent));
1700
mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
1701
mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
1702
mPollList[0].out_flags = 0;
1703
}
1704
1705
} // namespace net
1706
} // namespace mozilla