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