Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsSocketTransportService2.cpp
blob42d14d5d973c5c9ab074d63dfdfb721a8c496f13
1 // vim:set sw=4 sts=4 et cin:
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
15 * The Original Code is Mozilla.
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.
22 * Contributor(s):
23 * Darin Fisher <darin@netscape.com>
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.
37 * ***** END LICENSE BLOCK ***** */
39 #ifdef MOZ_LOGGING
40 #define FORCE_PR_LOG
41 #endif
43 #include "nsSocketTransportService2.h"
44 #include "nsSocketTransport2.h"
45 #include "nsReadableUtils.h"
46 #include "nsAutoLock.h"
47 #include "nsNetError.h"
48 #include "prnetdb.h"
49 #include "prlock.h"
50 #include "prerror.h"
51 #include "plstr.h"
53 #if defined(PR_LOGGING)
54 PRLogModuleInfo *gSocketTransportLog = nsnull;
55 #endif
57 nsSocketTransportService *gSocketTransportService = nsnull;
58 PRThread *gSocketThread = nsnull;
60 //-----------------------------------------------------------------------------
61 // ctor/dtor (called on the main/UI thread by the service manager)
63 nsSocketTransportService::nsSocketTransportService()
64 : mThread(nsnull)
65 , mThreadEvent(nsnull)
66 , mAutodialEnabled(PR_FALSE)
67 , mLock(PR_NewLock())
68 , mInitialized(PR_FALSE)
69 , mShuttingDown(PR_FALSE)
70 , mActiveCount(0)
71 , mIdleCount(0)
73 #if defined(PR_LOGGING)
74 gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
75 #endif
77 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
79 NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
80 gSocketTransportService = this;
83 nsSocketTransportService::~nsSocketTransportService()
85 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
86 NS_ASSERTION(!mInitialized, "not shutdown properly");
88 if (mLock)
89 PR_DestroyLock(mLock);
91 if (mThreadEvent)
92 PR_DestroyPollableEvent(mThreadEvent);
94 gSocketTransportService = nsnull;
97 //-----------------------------------------------------------------------------
98 // event queue (any thread)
100 already_AddRefed<nsIThread>
101 nsSocketTransportService::GetThreadSafely()
103 nsAutoLock lock(mLock);
104 nsIThread* result = mThread;
105 NS_IF_ADDREF(result);
106 return result;
109 NS_IMETHODIMP
110 nsSocketTransportService::Dispatch(nsIRunnable *event, PRUint32 flags)
112 LOG(("STS dispatch [%p]\n", event));
114 nsCOMPtr<nsIThread> thread = GetThreadSafely();
115 NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
116 nsresult rv = thread->Dispatch(event, flags);
117 if (rv == NS_ERROR_UNEXPECTED) {
118 // Thread is no longer accepting events. We must have just shut it
119 // down on the main thread. Pretend we never saw it.
120 rv = NS_ERROR_NOT_INITIALIZED;
122 return rv;
125 NS_IMETHODIMP
126 nsSocketTransportService::IsOnCurrentThread(PRBool *result)
128 nsCOMPtr<nsIThread> thread = GetThreadSafely();
129 NS_ENSURE_TRUE(thread, NS_ERROR_NOT_INITIALIZED);
130 return thread->IsOnCurrentThread(result);
133 //-----------------------------------------------------------------------------
134 // socket api (socket thread only)
136 NS_IMETHODIMP
137 nsSocketTransportService::NotifyWhenCanAttachSocket(nsIRunnable *event)
139 LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n"));
141 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
143 if (CanAttachSocket()) {
144 return Dispatch(event, NS_DISPATCH_NORMAL);
147 mPendingSocketQ.PutEvent(event);
148 return NS_OK;
151 NS_IMETHODIMP
152 nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
154 LOG(("nsSocketTransportService::AttachSocket [handler=%x]\n", handler));
156 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
158 if (!CanAttachSocket()) {
159 return NS_ERROR_NOT_AVAILABLE;
162 SocketContext sock;
163 sock.mFD = fd;
164 sock.mHandler = handler;
165 sock.mElapsedTime = 0;
167 nsresult rv = AddToIdleList(&sock);
168 if (NS_SUCCEEDED(rv))
169 NS_ADDREF(handler);
170 return rv;
173 nsresult
174 nsSocketTransportService::DetachSocket(SocketContext *sock)
176 LOG(("nsSocketTransportService::DetachSocket [handler=%x]\n", sock->mHandler));
178 // inform the handler that this socket is going away
179 sock->mHandler->OnSocketDetached(sock->mFD);
181 // cleanup
182 sock->mFD = nsnull;
183 NS_RELEASE(sock->mHandler);
185 // find out what list this is on.
186 PRUint32 index = sock - mActiveList;
187 if (index < NS_SOCKET_MAX_COUNT)
188 RemoveFromPollList(sock);
189 else
190 RemoveFromIdleList(sock);
192 // NOTE: sock is now an invalid pointer
195 // notify the first element on the pending socket queue...
197 nsCOMPtr<nsIRunnable> event;
198 if (mPendingSocketQ.GetPendingEvent(getter_AddRefs(event))) {
199 // move event from pending queue to dispatch queue
200 return Dispatch(event, NS_DISPATCH_NORMAL);
202 return NS_OK;
205 nsresult
206 nsSocketTransportService::AddToPollList(SocketContext *sock)
208 LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
210 if (mActiveCount == NS_SOCKET_MAX_COUNT) {
211 NS_ERROR("too many active sockets");
212 return NS_ERROR_UNEXPECTED;
215 mActiveList[mActiveCount] = *sock;
216 mActiveCount++;
218 mPollList[mActiveCount].fd = sock->mFD;
219 mPollList[mActiveCount].in_flags = sock->mHandler->mPollFlags;
220 mPollList[mActiveCount].out_flags = 0;
222 LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
223 return NS_OK;
226 void
227 nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
229 LOG(("nsSocketTransportService::RemoveFromPollList [handler=%x]\n", sock->mHandler));
231 PRUint32 index = sock - mActiveList;
232 NS_ASSERTION(index < NS_SOCKET_MAX_COUNT, "invalid index");
234 LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
236 if (index != mActiveCount-1) {
237 mActiveList[index] = mActiveList[mActiveCount-1];
238 mPollList[index+1] = mPollList[mActiveCount];
240 mActiveCount--;
242 LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
245 nsresult
246 nsSocketTransportService::AddToIdleList(SocketContext *sock)
248 LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
250 if (mIdleCount == NS_SOCKET_MAX_COUNT) {
251 NS_ERROR("too many idle sockets");
252 return NS_ERROR_UNEXPECTED;
255 mIdleList[mIdleCount] = *sock;
256 mIdleCount++;
258 LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
259 return NS_OK;
262 void
263 nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
265 LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%x]\n", sock->mHandler));
267 PRUint32 index = sock - &mIdleList[0];
268 NS_ASSERTION(index < NS_SOCKET_MAX_COUNT, "invalid index");
270 if (index != mIdleCount-1)
271 mIdleList[index] = mIdleList[mIdleCount-1];
272 mIdleCount--;
274 LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
277 void
278 nsSocketTransportService::MoveToIdleList(SocketContext *sock)
280 nsresult rv = AddToIdleList(sock);
281 if (NS_FAILED(rv))
282 DetachSocket(sock);
283 else
284 RemoveFromPollList(sock);
287 void
288 nsSocketTransportService::MoveToPollList(SocketContext *sock)
290 nsresult rv = AddToPollList(sock);
291 if (NS_FAILED(rv))
292 DetachSocket(sock);
293 else
294 RemoveFromIdleList(sock);
297 PRIntervalTime
298 nsSocketTransportService::PollTimeout()
300 if (mActiveCount == 0)
301 return NS_SOCKET_POLL_TIMEOUT;
303 // compute minimum time before any socket timeout expires.
304 PRUint32 minR = PR_UINT16_MAX;
305 for (PRUint32 i=0; i<mActiveCount; ++i) {
306 const SocketContext &s = mActiveList[i];
307 // mPollTimeout could be less than mElapsedTime if setTimeout
308 // was called with a value smaller than mElapsedTime.
309 PRUint32 r = (s.mElapsedTime < s.mHandler->mPollTimeout)
310 ? s.mHandler->mPollTimeout - s.mElapsedTime
311 : 0;
312 if (r < minR)
313 minR = r;
315 LOG(("poll timeout: %lu\n", minR));
316 return PR_SecondsToInterval(minR);
319 PRInt32
320 nsSocketTransportService::Poll(PRBool wait, PRUint32 *interval)
322 PRPollDesc *pollList;
323 PRUint32 pollCount;
324 PRIntervalTime pollTimeout;
326 if (mPollList[0].fd) {
327 mPollList[0].out_flags = 0;
328 pollList = mPollList;
329 pollCount = mActiveCount + 1;
330 pollTimeout = PollTimeout();
332 else {
333 // no pollable event, so busy wait...
334 pollCount = mActiveCount;
335 if (pollCount)
336 pollList = &mPollList[1];
337 else
338 pollList = nsnull;
339 pollTimeout = PR_MillisecondsToInterval(25);
342 if (!wait)
343 pollTimeout = PR_INTERVAL_NO_WAIT;
345 PRIntervalTime ts = PR_IntervalNow();
347 LOG((" timeout = %i milliseconds\n",
348 PR_IntervalToMilliseconds(pollTimeout)));
349 PRInt32 rv = PR_Poll(pollList, pollCount, pollTimeout);
351 PRIntervalTime passedInterval = PR_IntervalNow() - ts;
353 LOG((" ...returned after %i milliseconds\n",
354 PR_IntervalToMilliseconds(passedInterval)));
356 *interval = PR_IntervalToSeconds(passedInterval);
357 return rv;
360 //-----------------------------------------------------------------------------
361 // xpcom api
363 NS_IMPL_THREADSAFE_ISUPPORTS5(nsSocketTransportService,
364 nsISocketTransportService,
365 nsIEventTarget,
366 nsIThreadObserver,
367 nsIRunnable,
368 nsPISocketTransportService)
370 // called from main thread only
371 NS_IMETHODIMP
372 nsSocketTransportService::Init()
374 NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
376 if (!NS_IsMainThread()) {
377 NS_ERROR("wrong thread");
378 return NS_ERROR_UNEXPECTED;
381 if (mInitialized)
382 return NS_OK;
384 if (mShuttingDown)
385 return NS_ERROR_UNEXPECTED;
387 if (!mThreadEvent) {
388 mThreadEvent = PR_NewPollableEvent();
390 // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
391 // or similar software.
393 // NOTE: per bug 191739, this failure could also be caused by lack
394 // of a loopback device on Windows and OS/2 platforms (NSPR creates
395 // a loopback socket pair on these platforms to implement a pollable
396 // event object). if we can't create a pollable event, then we'll
397 // have to "busy wait" to implement the socket event queue :-(
399 if (!mThreadEvent) {
400 NS_WARNING("running socket transport thread without a pollable event");
401 LOG(("running socket transport thread without a pollable event"));
405 nsCOMPtr<nsIThread> thread;
406 nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
407 if (NS_FAILED(rv)) return rv;
410 nsAutoLock lock(mLock);
411 // Install our mThread, protecting against concurrent readers
412 thread.swap(mThread);
415 mInitialized = PR_TRUE;
416 return NS_OK;
419 // called from main thread only
420 NS_IMETHODIMP
421 nsSocketTransportService::Shutdown()
423 LOG(("nsSocketTransportService::Shutdown\n"));
425 NS_ENSURE_STATE(NS_IsMainThread());
427 if (!mInitialized)
428 return NS_OK;
430 if (mShuttingDown)
431 return NS_ERROR_UNEXPECTED;
434 nsAutoLock lock(mLock);
436 // signal the socket thread to shutdown
437 mShuttingDown = PR_TRUE;
439 if (mThreadEvent)
440 PR_SetPollableEvent(mThreadEvent);
441 // else wait for Poll timeout
444 // join with thread
445 mThread->Shutdown();
447 nsAutoLock lock(mLock);
448 // Drop our reference to mThread and make sure that any concurrent
449 // readers are excluded
450 mThread = nsnull;
453 mInitialized = PR_FALSE;
454 mShuttingDown = PR_FALSE;
456 return NS_OK;
459 NS_IMETHODIMP
460 nsSocketTransportService::CreateTransport(const char **types,
461 PRUint32 typeCount,
462 const nsACString &host,
463 PRInt32 port,
464 nsIProxyInfo *proxyInfo,
465 nsISocketTransport **result)
467 NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
468 NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
470 nsSocketTransport *trans = new nsSocketTransport();
471 if (!trans)
472 return NS_ERROR_OUT_OF_MEMORY;
473 NS_ADDREF(trans);
475 nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
476 if (NS_FAILED(rv)) {
477 NS_RELEASE(trans);
478 return rv;
481 *result = trans;
482 return NS_OK;
485 NS_IMETHODIMP
486 nsSocketTransportService::GetAutodialEnabled(PRBool *value)
488 *value = mAutodialEnabled;
489 return NS_OK;
492 NS_IMETHODIMP
493 nsSocketTransportService::SetAutodialEnabled(PRBool value)
495 mAutodialEnabled = value;
496 return NS_OK;
499 NS_IMETHODIMP
500 nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
502 nsAutoLock lock(mLock);
503 if (mThreadEvent)
504 PR_SetPollableEvent(mThreadEvent);
505 return NS_OK;
508 NS_IMETHODIMP
509 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
510 PRBool mayWait, PRUint32 depth)
512 // DoPollIteration doesn't support being called recursively. This case
513 // should only happen when someone (e.g., PSM) is issuing a synchronous
514 // proxy call from this thread to the main thread.
515 if (depth > 1)
516 return NS_OK;
518 // Favor processing existing sockets before other events.
519 DoPollIteration(PR_FALSE);
521 PRBool val;
522 while (mayWait && NS_SUCCEEDED(thread->HasPendingEvents(&val)) && !val)
523 DoPollIteration(PR_TRUE);
525 return NS_OK;
528 NS_IMETHODIMP
529 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
530 PRUint32 depth)
532 return NS_OK;
535 NS_IMETHODIMP
536 nsSocketTransportService::Run()
538 LOG(("STS thread init\n"));
540 gSocketThread = PR_GetCurrentThread();
542 // add thread event to poll list (mThreadEvent may be NULL)
543 mPollList[0].fd = mThreadEvent;
544 mPollList[0].in_flags = PR_POLL_READ;
545 mPollList[0].out_flags = 0;
547 nsIThread *thread = NS_GetCurrentThread();
549 // hook ourselves up to observe event processing for this thread
550 nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
551 threadInt->SetObserver(this);
553 for (;;) {
554 // process all pending events
555 NS_ProcessPendingEvents(thread);
557 // now that our event queue is empty, check to see if we should exit
559 nsAutoLock lock(mLock);
560 if (mShuttingDown)
561 break;
564 // wait for and process the next pending event
565 NS_ProcessNextEvent(thread);
568 LOG(("STS shutting down thread\n"));
570 // detach any sockets
571 PRInt32 i;
572 for (i=mActiveCount-1; i>=0; --i)
573 DetachSocket(&mActiveList[i]);
574 for (i=mIdleCount-1; i>=0; --i)
575 DetachSocket(&mIdleList[i]);
577 // Final pass over the event queue. This makes sure that events posted by
578 // socket detach handlers get processed.
579 NS_ProcessPendingEvents(thread);
581 gSocketThread = nsnull;
583 LOG(("STS thread exit\n"));
584 return NS_OK;
587 nsresult
588 nsSocketTransportService::DoPollIteration(PRBool wait)
590 LOG(("STS poll iter [%d]\n", wait));
592 PRInt32 i, count;
595 // poll loop
597 PRBool pollError = PR_FALSE;
600 // walk active list backwards to see if any sockets should actually be
601 // idle, then walk the idle list backwards to see if any idle sockets
602 // should become active. take care to check only idle sockets that
603 // were idle to begin with ;-)
605 count = mIdleCount;
606 for (i=mActiveCount-1; i>=0; --i) {
607 //---
608 LOG((" active [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
609 mActiveList[i].mHandler,
610 mActiveList[i].mHandler->mCondition,
611 mActiveList[i].mHandler->mPollFlags));
612 //---
613 if (NS_FAILED(mActiveList[i].mHandler->mCondition))
614 DetachSocket(&mActiveList[i]);
615 else {
616 PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags;
617 if (in_flags == 0)
618 MoveToIdleList(&mActiveList[i]);
619 else {
620 // update poll flags
621 mPollList[i+1].in_flags = in_flags;
622 mPollList[i+1].out_flags = 0;
626 for (i=count-1; i>=0; --i) {
627 //---
628 LOG((" idle [%u] { handler=%x condition=%x pollflags=%hu }\n", i,
629 mIdleList[i].mHandler,
630 mIdleList[i].mHandler->mCondition,
631 mIdleList[i].mHandler->mPollFlags));
632 //---
633 if (NS_FAILED(mIdleList[i].mHandler->mCondition))
634 DetachSocket(&mIdleList[i]);
635 else if (mIdleList[i].mHandler->mPollFlags != 0)
636 MoveToPollList(&mIdleList[i]);
639 LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
641 // Measures seconds spent while blocked on PR_Poll
642 PRUint32 pollInterval;
644 PRInt32 n = Poll(wait, &pollInterval);
645 if (n < 0) {
646 LOG((" PR_Poll error [%d]\n", PR_GetError()));
647 pollError = PR_TRUE;
649 else {
651 // service "active" sockets...
653 for (i=0; i<PRInt32(mActiveCount); ++i) {
654 PRPollDesc &desc = mPollList[i+1];
655 SocketContext &s = mActiveList[i];
656 if (n > 0 && desc.out_flags != 0) {
657 s.mElapsedTime = 0;
658 s.mHandler->OnSocketReady(desc.fd, desc.out_flags);
660 // check for timeout errors unless disabled...
661 else if (s.mHandler->mPollTimeout != PR_UINT16_MAX) {
662 // update elapsed time counter
663 if (NS_UNLIKELY(pollInterval > (PR_UINT16_MAX - s.mElapsedTime)))
664 s.mElapsedTime = PR_UINT16_MAX;
665 else
666 s.mElapsedTime += PRUint16(pollInterval);
667 // check for timeout expiration
668 if (s.mElapsedTime >= s.mHandler->mPollTimeout) {
669 s.mElapsedTime = 0;
670 s.mHandler->OnSocketReady(desc.fd, -1);
676 // check for "dead" sockets and remove them (need to do this in
677 // reverse order obviously).
679 for (i=mActiveCount-1; i>=0; --i) {
680 if (NS_FAILED(mActiveList[i].mHandler->mCondition))
681 DetachSocket(&mActiveList[i]);
684 if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
685 // acknowledge pollable event (wait should not block)
686 if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
687 // On Windows, the TCP loopback connection in the
688 // pollable event may become broken when a laptop
689 // switches between wired and wireless networks or
690 // wakes up from hibernation. We try to create a
691 // new pollable event. If that fails, we fall back
692 // on "busy wait".
694 nsAutoLock lock(mLock);
695 PR_DestroyPollableEvent(mThreadEvent);
696 mThreadEvent = PR_NewPollableEvent();
698 if (!mThreadEvent) {
699 NS_WARNING("running socket transport thread without "
700 "a pollable event");
701 LOG(("running socket transport thread without "
702 "a pollable event"));
704 mPollList[0].fd = mThreadEvent;
705 // mPollList[0].in_flags was already set to PR_POLL_READ
706 // in Run().
707 mPollList[0].out_flags = 0;
712 return NS_OK;