1 : // vim:set sw=4 sts=4 et cin:
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifdef MOZ_LOGGING
40 : #define FORCE_PR_LOG
41 : #endif
42 :
43 : #include "nsSocketTransportService2.h"
44 : #include "nsSocketTransport2.h"
45 : #include "nsReadableUtils.h"
46 : #include "nsNetError.h"
47 : #include "prnetdb.h"
48 : #include "prerror.h"
49 : #include "plstr.h"
50 : #include "nsIPrefService.h"
51 : #include "nsIPrefBranch.h"
52 : #include "nsServiceManagerUtils.h"
53 : #include "nsIOService.h"
54 :
55 : #include "mozilla/FunctionTimer.h"
56 :
57 : // XXX: There is no good header file to put these in. :(
58 : namespace mozilla { namespace psm {
59 :
60 : void InitializeSSLServerCertVerificationThreads();
61 : void StopSSLServerCertVerificationThreads();
62 :
63 : } } // namespace mozilla::psm
64 :
65 : using namespace mozilla;
66 :
67 : #if defined(PR_LOGGING)
68 : PRLogModuleInfo *gSocketTransportLog = nsnull;
69 : #endif
70 :
71 : nsSocketTransportService *gSocketTransportService = nsnull;
72 : PRThread *gSocketThread = nsnull;
73 :
74 : #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
75 : #define SOCKET_LIMIT_TARGET 550U
76 : #define SOCKET_LIMIT_MIN 50U
77 :
78 : PRUint32 nsSocketTransportService::gMaxCount;
79 : PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
80 :
81 : //-----------------------------------------------------------------------------
82 : // ctor/dtor (called on the main/UI thread by the service manager)
83 :
84 1419 : nsSocketTransportService::nsSocketTransportService()
85 : : mThread(nsnull)
86 : , mThreadEvent(nsnull)
87 : , mAutodialEnabled(false)
88 : , mLock("nsSocketTransportService::mLock")
89 : , mInitialized(false)
90 : , mShuttingDown(false)
91 : , mActiveListSize(SOCKET_LIMIT_MIN)
92 : , mIdleListSize(SOCKET_LIMIT_MIN)
93 : , mActiveCount(0)
94 : , mIdleCount(0)
95 : , mSendBufferSize(0)
96 1419 : , mProbedMaxCount(false)
97 : {
98 : #if defined(PR_LOGGING)
99 1419 : gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
100 : #endif
101 :
102 1419 : NS_ASSERTION(NS_IsMainThread(), "wrong thread");
103 :
104 1419 : PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
105 : mActiveList = (SocketContext *)
106 1419 : moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
107 : mIdleList = (SocketContext *)
108 1419 : moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
109 : mPollList = (PRPollDesc *)
110 1419 : moz_xmalloc(sizeof(PRPollDesc) * (mActiveListSize + 1));
111 :
112 1419 : NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
113 1419 : gSocketTransportService = this;
114 1419 : }
115 :
116 4245 : nsSocketTransportService::~nsSocketTransportService()
117 : {
118 1415 : NS_ASSERTION(NS_IsMainThread(), "wrong thread");
119 1415 : NS_ASSERTION(!mInitialized, "not shutdown properly");
120 :
121 1415 : if (mThreadEvent)
122 1415 : PR_DestroyPollableEvent(mThreadEvent);
123 :
124 1415 : moz_free(mActiveList);
125 1415 : moz_free(mIdleList);
126 1415 : moz_free(mPollList);
127 1415 : gSocketTransportService = nsnull;
128 5660 : }
129 :
130 : //-----------------------------------------------------------------------------
131 : // event queue (any thread)
132 :
133 : already_AddRefed<nsIThread>
134 49891 : nsSocketTransportService::GetThreadSafely()
135 : {
136 99782 : MutexAutoLock lock(mLock);
137 49891 : nsIThread* result = mThread;
138 49891 : NS_IF_ADDREF(result);
139 49891 : return result;
140 : }
141 :
142 : NS_IMETHODIMP
143 49887 : nsSocketTransportService::Dispatch(nsIRunnable *event, PRUint32 flags)
144 : {
145 49887 : SOCKET_LOG(("STS dispatch [%p]\n", event));
146 :
147 99774 : nsCOMPtr<nsIThread> thread = GetThreadSafely();
148 49887 : NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
149 49887 : nsresult rv = thread->Dispatch(event, flags);
150 49887 : if (rv == NS_ERROR_UNEXPECTED) {
151 : // Thread is no longer accepting events. We must have just shut it
152 : // down on the main thread. Pretend we never saw it.
153 0 : rv = NS_ERROR_NOT_INITIALIZED;
154 : }
155 49887 : return rv;
156 : }
157 :
158 : NS_IMETHODIMP
159 4 : nsSocketTransportService::IsOnCurrentThread(bool *result)
160 : {
161 8 : nsCOMPtr<nsIThread> thread = GetThreadSafely();
162 4 : NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
163 4 : return thread->IsOnCurrentThread(result);
164 : }
165 :
166 : //-----------------------------------------------------------------------------
167 : // socket api (socket thread only)
168 :
169 : NS_IMETHODIMP
170 0 : nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
171 : {
172 0 : SOCKET_LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
173 :
174 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
175 :
176 0 : if (CanAttachSocket()) {
177 0 : return Dispatch(event, NS_DISPATCH_NORMAL);
178 : }
179 :
180 0 : mPendingSocketQ.PutEvent(event);
181 0 : return NS_OK;
182 : }
183 :
184 : NS_IMETHODIMP
185 6296 : nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
186 : {
187 6296 : SOCKET_LOG(("nsSocketTransportService::AttachSocket [handler=%x]\n", handler));
188 :
189 6296 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
190 :
191 6296 : if (!CanAttachSocket()) {
192 0 : return NS_ERROR_NOT_AVAILABLE;
193 : }
194 :
195 : SocketContext sock;
196 6296 : sock.mFD = fd;
197 6296 : sock.mHandler = handler;
198 6296 : sock.mElapsedTime = 0;
199 :
200 6296 : nsresult rv = AddToIdleList(&sock);
201 6296 : if (NS_SUCCEEDED(rv))
202 6296 : NS_ADDREF(handler);
203 6296 : return rv;
204 : }
205 :
206 : nsresult
207 6296 : nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *sock)
208 : {
209 6296 : SOCKET_LOG(("nsSocketTransportService::DetachSocket [handler=%x]\n", sock->mHandler));
210 6296 : NS_ABORT_IF_FALSE((listHead == mActiveList) || (listHead == mIdleList),
211 : "DetachSocket invalid head");
212 :
213 : // inform the handler that this socket is going away
214 6296 : sock->mHandler->OnSocketDetached(sock->mFD);
215 :
216 : // cleanup
217 6296 : sock->mFD = nsnull;
218 6296 : NS_RELEASE(sock->mHandler);
219 :
220 6296 : if (listHead == mActiveList)
221 3413 : RemoveFromPollList(sock);
222 : else
223 2883 : RemoveFromIdleList(sock);
224 :
225 : // NOTE: sock is now an invalid pointer
226 :
227 : //
228 : // notify the first element on the pending socket queue...
229 : //
230 12592 : nsCOMPtr<nsIRunnable> event;
231 6296 : if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
232 : // move event from pending queue to dispatch queue
233 0 : return Dispatch(event, NS_DISPATCH_NORMAL);
234 : }
235 6296 : return NS_OK;
236 : }
237 :
238 : nsresult
239 9154 : nsSocketTransportService::AddToPollList(SocketContext *sock)
240 : {
241 9154 : NS_ABORT_IF_FALSE(!(((PRUint32)(sock - mActiveList)) < mActiveListSize),
242 : "AddToPollList Socket Already Active");
243 :
244 9154 : SOCKET_LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
245 9154 : if (mActiveCount == mActiveListSize) {
246 0 : SOCKET_LOG((" Active List size of %d met\n", mActiveCount));
247 0 : if (!GrowActiveList()) {
248 0 : NS_ERROR("too many active sockets");
249 0 : return NS_ERROR_OUT_OF_MEMORY;
250 : }
251 : }
252 :
253 9154 : mActiveList[mActiveCount] = *sock;
254 9154 : mActiveCount++;
255 :
256 9154 : mPollList[mActiveCount].fd = sock->mFD;
257 9154 : mPollList[mActiveCount].in_flags = sock->mHandler->mPollFlags;
258 9154 : mPollList[mActiveCount].out_flags = 0;
259 :
260 9154 : SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
261 9154 : return NS_OK;
262 : }
263 :
264 : void
265 9154 : nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
266 : {
267 9154 : SOCKET_LOG(("nsSocketTransportService::RemoveFromPollList [handler=%x]\n", sock->mHandler));
268 :
269 9154 : PRUint32 index = sock - mActiveList;
270 9154 : NS_ABORT_IF_FALSE(index < mActiveListSize, "invalid index");
271 :
272 9154 : SOCKET_LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
273 :
274 9154 : if (index != mActiveCount-1) {
275 2919 : mActiveList[index] = mActiveList[mActiveCount-1];
276 2919 : mPollList[index+1] = mPollList[mActiveCount];
277 : }
278 9154 : mActiveCount--;
279 :
280 9154 : SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
281 9154 : }
282 :
283 : nsresult
284 12037 : nsSocketTransportService::AddToIdleList(SocketContext *sock)
285 : {
286 12037 : NS_ABORT_IF_FALSE(!(((PRUint32)(sock - mIdleList)) < mIdleListSize),
287 : "AddToIdlelList Socket Already Idle");
288 :
289 12037 : SOCKET_LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
290 12037 : if (mIdleCount == mIdleListSize) {
291 0 : SOCKET_LOG((" Idle List size of %d met\n", mIdleCount));
292 0 : if (!GrowIdleList()) {
293 0 : NS_ERROR("too many idle sockets");
294 0 : return NS_ERROR_OUT_OF_MEMORY;
295 : }
296 : }
297 :
298 12037 : mIdleList[mIdleCount] = *sock;
299 12037 : mIdleCount++;
300 :
301 12037 : SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
302 12037 : return NS_OK;
303 : }
304 :
305 : void
306 12037 : nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
307 : {
308 12037 : SOCKET_LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%x]\n", sock->mHandler));
309 :
310 12037 : PRUint32 index = sock - mIdleList;
311 12037 : NS_ASSERTION(index < mIdleListSize, "invalid index in idle list");
312 :
313 12037 : if (index != mIdleCount-1)
314 1251 : mIdleList[index] = mIdleList[mIdleCount-1];
315 12037 : mIdleCount--;
316 :
317 12037 : SOCKET_LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
318 12037 : }
319 :
320 : void
321 5741 : nsSocketTransportService::MoveToIdleList(SocketContext *sock)
322 : {
323 5741 : nsresult rv = AddToIdleList(sock);
324 5741 : if (NS_FAILED(rv))
325 0 : DetachSocket(mActiveList, sock);
326 : else
327 5741 : RemoveFromPollList(sock);
328 5741 : }
329 :
330 : void
331 9154 : nsSocketTransportService::MoveToPollList(SocketContext *sock)
332 : {
333 9154 : nsresult rv = AddToPollList(sock);
334 9154 : if (NS_FAILED(rv))
335 0 : DetachSocket(mIdleList, sock);
336 : else
337 9154 : RemoveFromIdleList(sock);
338 9154 : }
339 :
340 : bool
341 0 : nsSocketTransportService::GrowActiveList()
342 : {
343 0 : PRInt32 toAdd = gMaxCount - mActiveListSize;
344 0 : if (toAdd > 100)
345 0 : toAdd = 100;
346 0 : if (toAdd < 1)
347 0 : return false;
348 :
349 0 : mActiveListSize += toAdd;
350 : mActiveList = (SocketContext *)
351 0 : moz_xrealloc(mActiveList, sizeof(SocketContext) * mActiveListSize);
352 : mPollList = (PRPollDesc *)
353 0 : moz_xrealloc(mPollList, sizeof(PRPollDesc) * (mActiveListSize + 1));
354 0 : return true;
355 : }
356 :
357 : bool
358 0 : nsSocketTransportService::GrowIdleList()
359 : {
360 0 : PRInt32 toAdd = gMaxCount - mIdleListSize;
361 0 : if (toAdd > 100)
362 0 : toAdd = 100;
363 0 : if (toAdd < 1)
364 0 : return false;
365 :
366 0 : mIdleListSize += toAdd;
367 : mIdleList = (SocketContext *)
368 0 : moz_xrealloc(mIdleList, sizeof(SocketContext) * mIdleListSize);
369 0 : return true;
370 : }
371 :
372 : PRIntervalTime
373 61371 : nsSocketTransportService::PollTimeout()
374 : {
375 61371 : if (mActiveCount == 0)
376 3667 : return NS_SOCKET_POLL_TIMEOUT;
377 :
378 : // compute minimum time before any socket timeout expires.
379 57704 : PRUint32 minR = PR_UINT16_MAX;
380 199531 : for (PRUint32 i=0; i<mActiveCount; ++i) {
381 141827 : const SocketContext &s = mActiveList[i];
382 : // mPollTimeout could be less than mElapsedTime if setTimeout
383 : // was called with a value smaller than mElapsedTime.
384 : PRUint32 r = (s.mElapsedTime < s.mHandler->mPollTimeout)
385 : ? s.mHandler->mPollTimeout - s.mElapsedTime
386 141827 : : 0;
387 141827 : if (r < minR)
388 0 : minR = r;
389 : }
390 57704 : SOCKET_LOG(("poll timeout: %lu\n", minR));
391 57704 : return PR_SecondsToInterval(minR);
392 : }
393 :
394 : PRInt32
395 61371 : nsSocketTransportService::Poll(bool wait, PRUint32 *interval)
396 : {
397 : PRPollDesc *pollList;
398 : PRUint32 pollCount;
399 : PRIntervalTime pollTimeout;
400 :
401 61371 : if (mPollList[0].fd) {
402 61371 : mPollList[0].out_flags = 0;
403 61371 : pollList = mPollList;
404 61371 : pollCount = mActiveCount + 1;
405 61371 : pollTimeout = PollTimeout();
406 : }
407 : else {
408 : // no pollable event, so busy wait...
409 0 : pollCount = mActiveCount;
410 0 : if (pollCount)
411 0 : pollList = &mPollList[1];
412 : else
413 0 : pollList = nsnull;
414 0 : pollTimeout = PR_MillisecondsToInterval(25);
415 : }
416 :
417 61371 : if (!wait)
418 15049 : pollTimeout = PR_INTERVAL_NO_WAIT;
419 :
420 61371 : PRIntervalTime ts = PR_IntervalNow();
421 :
422 61371 : SOCKET_LOG((" timeout = %i milliseconds\n",
423 : PR_IntervalToMilliseconds(pollTimeout)));
424 61371 : PRInt32 rv = PR_Poll(pollList, pollCount, pollTimeout);
425 :
426 61371 : PRIntervalTime passedInterval = PR_IntervalNow() - ts;
427 :
428 61371 : SOCKET_LOG((" ...returned after %i milliseconds\n",
429 : PR_IntervalToMilliseconds(passedInterval)));
430 :
431 61371 : *interval = PR_IntervalToSeconds(passedInterval);
432 61371 : return rv;
433 : }
434 :
435 : //-----------------------------------------------------------------------------
436 : // xpcom api
437 :
438 514412 : NS_IMPL_THREADSAFE_ISUPPORTS6(nsSocketTransportService,
439 : nsISocketTransportService,
440 : nsIEventTarget,
441 : nsIThreadObserver,
442 : nsIRunnable,
443 : nsPISocketTransportService,
444 : nsIObserver)
445 :
446 : // called from main thread only
447 : NS_IMETHODIMP
448 2845 : nsSocketTransportService::Init()
449 : {
450 : NS_TIME_FUNCTION;
451 :
452 2845 : if (!NS_IsMainThread()) {
453 0 : NS_ERROR("wrong thread");
454 0 : return NS_ERROR_UNEXPECTED;
455 : }
456 :
457 2845 : if (mInitialized)
458 1419 : return NS_OK;
459 :
460 1426 : if (mShuttingDown)
461 0 : return NS_ERROR_UNEXPECTED;
462 :
463 : // Don't initialize inside the offline mode
464 1426 : if (gIOService->IsOffline() && !gIOService->IsComingOnline())
465 0 : return NS_ERROR_OFFLINE;
466 :
467 1426 : if (!mThreadEvent) {
468 1419 : mThreadEvent = PR_NewPollableEvent();
469 : //
470 : // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
471 : // or similar software.
472 : //
473 : // NOTE: per bug 191739, this failure could also be caused by lack
474 : // of a loopback device on Windows and OS/2 platforms (NSPR creates
475 : // a loopback socket pair on these platforms to implement a pollable
476 : // event object). if we can't create a pollable event, then we'll
477 : // have to "busy wait" to implement the socket event queue :-(
478 : //
479 1419 : if (!mThreadEvent) {
480 0 : NS_WARNING("running socket transport thread without a pollable event");
481 0 : SOCKET_LOG(("running socket transport thread without a pollable event"));
482 : }
483 : }
484 :
485 : NS_TIME_FUNCTION_MARK("Created thread");
486 :
487 2852 : nsCOMPtr<nsIThread> thread;
488 1426 : nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
489 1426 : if (NS_FAILED(rv)) return rv;
490 :
491 : {
492 2852 : MutexAutoLock lock(mLock);
493 : // Install our mThread, protecting against concurrent readers
494 1426 : thread.swap(mThread);
495 : }
496 :
497 2852 : nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
498 1426 : if (tmpPrefService)
499 1426 : tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
500 1426 : UpdatePrefs();
501 :
502 : NS_TIME_FUNCTION_MARK("UpdatePrefs");
503 :
504 1426 : mInitialized = true;
505 1426 : return NS_OK;
506 : }
507 :
508 : // called from main thread only
509 : NS_IMETHODIMP
510 1426 : nsSocketTransportService::Shutdown()
511 : {
512 1426 : SOCKET_LOG(("nsSocketTransportService::Shutdown\n"));
513 :
514 1426 : NS_ENSURE_STATE(NS_IsMainThread());
515 :
516 1426 : if (!mInitialized)
517 0 : return NS_OK;
518 :
519 1426 : if (mShuttingDown)
520 0 : return NS_ERROR_UNEXPECTED;
521 :
522 : {
523 2852 : MutexAutoLock lock(mLock);
524 :
525 : // signal the socket thread to shutdown
526 1426 : mShuttingDown = true;
527 :
528 1426 : if (mThreadEvent)
529 1426 : PR_SetPollableEvent(mThreadEvent);
530 : // else wait for Poll timeout
531 : }
532 :
533 : // join with thread
534 1426 : mThread->Shutdown();
535 : {
536 2852 : MutexAutoLock lock(mLock);
537 : // Drop our reference to mThread and make sure that any concurrent
538 : // readers are excluded
539 1426 : mThread = nsnull;
540 : }
541 :
542 2852 : nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
543 1426 : if (tmpPrefService)
544 1426 : tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
545 :
546 1426 : mInitialized = false;
547 1426 : mShuttingDown = false;
548 :
549 1426 : return NS_OK;
550 : }
551 :
552 : NS_IMETHODIMP
553 3014 : nsSocketTransportService::CreateTransport(const char **types,
554 : PRUint32 typeCount,
555 : const nsACString &host,
556 : PRInt32 port,
557 : nsIProxyInfo *proxyInfo,
558 : nsISocketTransport **result)
559 : {
560 3014 : NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
561 3014 : NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
562 :
563 3013 : nsSocketTransport *trans = new nsSocketTransport();
564 3013 : if (!trans)
565 0 : return NS_ERROR_OUT_OF_MEMORY;
566 3013 : NS_ADDREF(trans);
567 :
568 3013 : nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
569 3013 : if (NS_FAILED(rv)) {
570 0 : NS_RELEASE(trans);
571 0 : return rv;
572 : }
573 :
574 3013 : *result = trans;
575 3013 : return NS_OK;
576 : }
577 :
578 : NS_IMETHODIMP
579 0 : nsSocketTransportService::GetAutodialEnabled(bool *value)
580 : {
581 0 : *value = mAutodialEnabled;
582 0 : return NS_OK;
583 : }
584 :
585 : NS_IMETHODIMP
586 1426 : nsSocketTransportService::SetAutodialEnabled(bool value)
587 : {
588 1426 : mAutodialEnabled = value;
589 1426 : return NS_OK;
590 : }
591 :
592 : NS_IMETHODIMP
593 50981 : nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
594 : {
595 101962 : MutexAutoLock lock(mLock);
596 50981 : if (mThreadEvent)
597 50981 : PR_SetPollableEvent(mThreadEvent);
598 50981 : return NS_OK;
599 : }
600 :
601 : NS_IMETHODIMP
602 54371 : nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
603 : bool mayWait, PRUint32 depth)
604 : {
605 54371 : return NS_OK;
606 : }
607 :
608 : NS_IMETHODIMP
609 54371 : nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
610 : PRUint32 depth)
611 : {
612 54371 : return NS_OK;
613 : }
614 :
615 : NS_IMETHODIMP
616 1426 : nsSocketTransportService::Run()
617 : {
618 1426 : SOCKET_LOG(("STS thread init\n"));
619 :
620 1426 : psm::InitializeSSLServerCertVerificationThreads();
621 :
622 1426 : gSocketThread = PR_GetCurrentThread();
623 :
624 : // add thread event to poll list (mThreadEvent may be NULL)
625 1426 : mPollList[0].fd = mThreadEvent;
626 1426 : mPollList[0].in_flags = PR_POLL_READ;
627 1426 : mPollList[0].out_flags = 0;
628 :
629 1426 : nsIThread *thread = NS_GetCurrentThread();
630 :
631 : // hook ourselves up to observe event processing for this thread
632 2852 : nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
633 1426 : threadInt->SetObserver(this);
634 :
635 : // make sure the pseudo random number generator is seeded on this thread
636 1426 : srand(PR_Now());
637 :
638 44905 : for (;;) {
639 46331 : bool pendingEvents = false;
640 46331 : thread->HasPendingEvents(&pendingEvents);
641 :
642 61371 : do {
643 : // If there are pending events for this thread then
644 : // DoPollIteration() should service the network without blocking.
645 61371 : DoPollIteration(!pendingEvents);
646 :
647 : // If nothing was pending before the poll, it might be now
648 61371 : if (!pendingEvents)
649 46322 : thread->HasPendingEvents(&pendingEvents);
650 :
651 61371 : if (pendingEvents) {
652 50888 : NS_ProcessNextEvent(thread);
653 50888 : pendingEvents = false;
654 50888 : thread->HasPendingEvents(&pendingEvents);
655 : }
656 : } while (pendingEvents);
657 :
658 : // now that our event queue is empty, check to see if we should exit
659 : {
660 92662 : MutexAutoLock lock(mLock);
661 46331 : if (mShuttingDown)
662 : break;
663 : }
664 : }
665 :
666 1426 : SOCKET_LOG(("STS shutting down thread\n"));
667 :
668 : // detach any sockets
669 : PRInt32 i;
670 1438 : for (i=mActiveCount-1; i>=0; --i)
671 12 : DetachSocket(mActiveList, &mActiveList[i]);
672 1427 : for (i=mIdleCount-1; i>=0; --i)
673 1 : DetachSocket(mIdleList, &mIdleList[i]);
674 :
675 : // Final pass over the event queue. This makes sure that events posted by
676 : // socket detach handlers get processed.
677 1426 : NS_ProcessPendingEvents(thread);
678 :
679 1426 : gSocketThread = nsnull;
680 :
681 1426 : psm::StopSSLServerCertVerificationThreads();
682 :
683 1426 : SOCKET_LOG(("STS thread exit\n"));
684 1426 : return NS_OK;
685 : }
686 :
687 : nsresult
688 61371 : nsSocketTransportService::DoPollIteration(bool wait)
689 : {
690 61371 : SOCKET_LOG(("STS poll iter [%d]\n", wait));
691 :
692 : PRInt32 i, count;
693 :
694 : //
695 : // poll loop
696 : //
697 : // walk active list backwards to see if any sockets should actually be
698 : // idle, then walk the idle list backwards to see if any idle sockets
699 : // should become active. take care to check only idle sockets that
700 : // were idle to begin with ;-)
701 : //
702 61371 : count = mIdleCount;
703 203039 : for (i=mActiveCount-1; i>=0; --i) {
704 : //---
705 141668 : SOCKET_LOG((" active [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
706 : mActiveList[i].mHandler,
707 : mActiveList[i].mHandler->mCondition,
708 : mActiveList[i].mHandler->mPollFlags));
709 : //---
710 141668 : if (NS_FAILED(mActiveList[i].mHandler->mCondition))
711 3254 : DetachSocket(mActiveList, &mActiveList[i]);
712 : else {
713 138414 : PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags;
714 138414 : if (in_flags == 0)
715 5741 : MoveToIdleList(&mActiveList[i]);
716 : else {
717 : // update poll flags
718 132673 : mPollList[i+1].in_flags = in_flags;
719 132673 : mPollList[i+1].out_flags = 0;
720 : }
721 : }
722 : }
723 91000 : for (i=count-1; i>=0; --i) {
724 : //---
725 29629 : SOCKET_LOG((" idle [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
726 : mIdleList[i].mHandler,
727 : mIdleList[i].mHandler->mCondition,
728 : mIdleList[i].mHandler->mPollFlags));
729 : //---
730 29629 : if (NS_FAILED(mIdleList[i].mHandler->mCondition))
731 2882 : DetachSocket(mIdleList, &mIdleList[i]);
732 26747 : else if (mIdleList[i].mHandler->mPollFlags != 0)
733 9154 : MoveToPollList(&mIdleList[i]);
734 : }
735 :
736 61371 : SOCKET_LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
737 :
738 : #if defined(XP_WIN)
739 : // 30 active connections is the historic limit before firefox 7's 256. A few
740 : // windows systems have troubles with the higher limit, so actively probe a
741 : // limit the first time we exceed 30.
742 : if ((mActiveCount > 30) && !mProbedMaxCount)
743 : ProbeMaxCount();
744 : #endif
745 :
746 : // Measures seconds spent while blocked on PR_Poll
747 : PRUint32 pollInterval;
748 :
749 61371 : PRInt32 n = Poll(wait, &pollInterval);
750 61371 : if (n < 0) {
751 0 : SOCKET_LOG((" PR_Poll error [%d]\n", PR_GetError()));
752 : }
753 : else {
754 : //
755 : // service "active" sockets...
756 : //
757 203198 : for (i=0; i<PRInt32(mActiveCount); ++i) {
758 141827 : PRPollDesc &desc = mPollList[i+1];
759 141827 : SocketContext &s = mActiveList[i];
760 141827 : if (n > 0 && desc.out_flags != 0) {
761 32757 : s.mElapsedTime = 0;
762 32757 : s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
763 : }
764 : // check for timeout errors unless disabled...
765 109070 : else if (s.mHandler->mPollTimeout != PR_UINT16_MAX) {
766 : // update elapsed time counter
767 0 : if (NS_UNLIKELY(pollInterval > (PR_UINT16_MAX - s.mElapsedTime)))
768 0 : s.mElapsedTime = PR_UINT16_MAX;
769 : else
770 0 : s.mElapsedTime += PRUint16(pollInterval);
771 : // check for timeout expiration
772 0 : if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
773 0 : s.mElapsedTime = 0;
774 0 : s.mHandler->OnSocketReady(desc.fd, -1);
775 : }
776 : }
777 : }
778 :
779 : //
780 : // check for "dead" sockets and remove them (need to do this in
781 : // reverse order obviously).
782 : //
783 203198 : for (i=mActiveCount-1; i>=0; --i) {
784 141827 : if (NS_FAILED(mActiveList[i].mHandler->mCondition))
785 147 : DetachSocket(mActiveList, &mActiveList[i]);
786 : }
787 :
788 61371 : if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
789 : // acknowledge pollable event (wait should not block)
790 44755 : if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
791 : // On Windows, the TCP loopback connection in the
792 : // pollable event may become broken when a laptop
793 : // switches between wired and wireless networks or
794 : // wakes up from hibernation. We try to create a
795 : // new pollable event. If that fails, we fall back
796 : // on "busy wait".
797 : {
798 0 : MutexAutoLock lock(mLock);
799 0 : PR_DestroyPollableEvent(mThreadEvent);
800 0 : mThreadEvent = PR_NewPollableEvent();
801 : }
802 0 : if (!mThreadEvent) {
803 : NS_WARNING("running socket transport thread without "
804 0 : "a pollable event");
805 0 : SOCKET_LOG(("running socket transport thread without "
806 : "a pollable event"));
807 : }
808 0 : mPollList[0].fd = mThreadEvent;
809 : // mPollList[0].in_flags was already set to PR_POLL_READ
810 : // in Run().
811 0 : mPollList[0].out_flags = 0;
812 : }
813 : }
814 : }
815 :
816 61371 : return NS_OK;
817 : }
818 :
819 : nsresult
820 1426 : nsSocketTransportService::UpdatePrefs()
821 : {
822 1426 : mSendBufferSize = 0;
823 :
824 2852 : nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
825 1426 : if (tmpPrefService) {
826 : PRInt32 bufferSize;
827 1426 : nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
828 1426 : if (NS_SUCCEEDED(rv) && bufferSize > 0)
829 0 : mSendBufferSize = bufferSize;
830 : }
831 :
832 1426 : return NS_OK;
833 : }
834 :
835 : NS_IMETHODIMP
836 0 : nsSocketTransportService::Observe(nsISupports *subject,
837 : const char *topic,
838 : const PRUnichar *data)
839 : {
840 0 : if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
841 0 : UpdatePrefs();
842 : }
843 0 : return NS_OK;
844 : }
845 :
846 : NS_IMETHODIMP
847 3006 : nsSocketTransportService::GetSendBufferSize(PRInt32 *value)
848 : {
849 3006 : *value = mSendBufferSize;
850 3006 : return NS_OK;
851 : }
852 :
853 :
854 : /// ugly OS specific includes are placed at the bottom of the src for clarity
855 :
856 : #if defined(XP_WIN)
857 : #include <windows.h>
858 : #elif defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
859 : #include <sys/resource.h>
860 : #endif
861 :
862 : // Right now the only need to do this is on windows.
863 : #if defined(XP_WIN)
864 : void
865 : nsSocketTransportService::ProbeMaxCount()
866 : {
867 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
868 :
869 : if (mProbedMaxCount)
870 : return;
871 : mProbedMaxCount = true;
872 :
873 : PRInt32 startedMaxCount = gMaxCount;
874 :
875 : // Allocate and test a PR_Poll up to the gMaxCount number of unconnected
876 : // sockets. See bug 692260 - windows should be able to handle 1000 sockets
877 : // in select() without a problem, but LSPs have been known to balk at lower
878 : // numbers. (64 in the bug).
879 :
880 : // Allocate
881 : struct PRPollDesc pfd[SOCKET_LIMIT_TARGET];
882 : PRUint32 numAllocated = 0;
883 :
884 : for (PRUint32 index = 0 ; index < gMaxCount; ++index) {
885 : pfd[index].in_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
886 : pfd[index].out_flags = 0;
887 : pfd[index].fd = PR_OpenTCPSocket(PR_AF_INET);
888 : if (!pfd[index].fd) {
889 : SOCKET_LOG(("Socket Limit Test index %d failed\n", index));
890 : if (index < SOCKET_LIMIT_MIN)
891 : gMaxCount = SOCKET_LIMIT_MIN;
892 : else
893 : gMaxCount = index;
894 : break;
895 : }
896 : ++numAllocated;
897 : }
898 :
899 : // Test
900 : PR_STATIC_ASSERT(SOCKET_LIMIT_MIN >= 32U);
901 : while (gMaxCount <= numAllocated) {
902 : PRInt32 rv = PR_Poll(pfd, gMaxCount, PR_MillisecondsToInterval(0));
903 :
904 : SOCKET_LOG(("Socket Limit Test poll() size=%d rv=%d\n",
905 : gMaxCount, rv));
906 :
907 : if (rv >= 0)
908 : break;
909 :
910 : SOCKET_LOG(("Socket Limit Test poll confirmationSize=%d rv=%d error=%d\n",
911 : gMaxCount, rv, PR_GetError()));
912 :
913 : gMaxCount -= 32;
914 : if (gMaxCount <= SOCKET_LIMIT_MIN) {
915 : gMaxCount = SOCKET_LIMIT_MIN;
916 : break;
917 : }
918 : }
919 :
920 : // Free
921 : for (PRUint32 index = 0 ; index < numAllocated; ++index)
922 : if (pfd[index].fd)
923 : PR_Close(pfd[index].fd);
924 :
925 : SOCKET_LOG(("Socket Limit Test max was confirmed at %d\n", gMaxCount));
926 : }
927 : #endif // windows
928 :
929 : PRStatus
930 1419 : nsSocketTransportService::DiscoverMaxCount()
931 : {
932 1419 : gMaxCount = SOCKET_LIMIT_MIN;
933 :
934 : #if defined(XP_UNIX) && !defined(AIX) && !defined(NEXTSTEP) && !defined(QNX)
935 : // On unix and os x network sockets and file
936 : // descriptors are the same. OS X comes defaulted at 256,
937 : // most linux at 1000. We can reliably use [sg]rlimit to
938 : // query that and raise it. We will try to raise it 250 past
939 : // our target number of SOCKET_LIMIT_TARGET so that some descriptors
940 : // are still available for other things.
941 :
942 : struct rlimit rlimitData;
943 1419 : if (getrlimit(RLIMIT_NOFILE, &rlimitData) == -1)
944 0 : return PR_SUCCESS;
945 1419 : if (rlimitData.rlim_cur >= SOCKET_LIMIT_TARGET + 250) {
946 1419 : gMaxCount = SOCKET_LIMIT_TARGET;
947 1419 : return PR_SUCCESS;
948 : }
949 :
950 0 : PRInt32 maxallowed = rlimitData.rlim_max;
951 0 : if (maxallowed == -1) { /* no limit */
952 0 : maxallowed = SOCKET_LIMIT_TARGET + 250;
953 0 : } else if ((PRUint32)maxallowed < SOCKET_LIMIT_MIN + 250) {
954 0 : return PR_SUCCESS;
955 0 : } else if ((PRUint32)maxallowed > SOCKET_LIMIT_TARGET + 250) {
956 0 : maxallowed = SOCKET_LIMIT_TARGET + 250;
957 : }
958 :
959 0 : rlimitData.rlim_cur = maxallowed;
960 0 : setrlimit(RLIMIT_NOFILE, &rlimitData);
961 0 : if (getrlimit(RLIMIT_NOFILE, &rlimitData) != -1)
962 0 : if (rlimitData.rlim_cur > SOCKET_LIMIT_MIN + 250)
963 0 : gMaxCount = rlimitData.rlim_cur - 250;
964 :
965 : #elif defined(XP_WIN) && !defined(WIN_CE)
966 : // >= XP is confirmed to have at least 1000
967 : gMaxCount = SOCKET_LIMIT_TARGET;
968 : #else
969 : // other platforms are harder to test - so leave at safe legacy value
970 : #endif
971 :
972 0 : return PR_SUCCESS;
973 : }
|