mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
567 lines
17 KiB
C++
567 lines
17 KiB
C++
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is Mozilla.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 2002
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Darin Fisher <darin@netscape.com>
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
#include "nsSocketTransportService2.h"
|
||
|
#include "nsSocketTransport2.h"
|
||
|
#include "nsPrintfCString.h"
|
||
|
#include "nsReadableUtils.h"
|
||
|
#include "nsAutoLock.h"
|
||
|
#include "nsNetError.h"
|
||
|
#include "prlock.h"
|
||
|
#include "prlog.h"
|
||
|
#include "prerror.h"
|
||
|
#include "plstr.h"
|
||
|
|
||
|
#if defined(PR_LOGGING)
|
||
|
PRLogModuleInfo *gSocketTransportLog = nsnull;
|
||
|
#endif
|
||
|
|
||
|
nsSocketTransportService *gSocketTransportService = nsnull;
|
||
|
PRThread *gSocketThread = nsnull;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// ctor/dtor (called on the main/UI thread by the service manager)
|
||
|
|
||
|
nsSocketTransportService::nsSocketTransportService()
|
||
|
: mInitialized(PR_FALSE)
|
||
|
, mThread(nsnull)
|
||
|
, mThreadEvent(nsnull)
|
||
|
, mEventQLock(PR_NewLock())
|
||
|
, mActiveCount(0)
|
||
|
, mIdleCount(0)
|
||
|
{
|
||
|
#if defined(PR_LOGGING)
|
||
|
gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
|
||
|
#endif
|
||
|
|
||
|
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||
|
|
||
|
gSocketTransportService = this;
|
||
|
}
|
||
|
|
||
|
nsSocketTransportService::~nsSocketTransportService()
|
||
|
{
|
||
|
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||
|
NS_ASSERTION(!mInitialized, "not shutdown properly");
|
||
|
|
||
|
PR_DestroyLock(mEventQLock);
|
||
|
|
||
|
if (mThreadEvent)
|
||
|
PR_DestroyPollableEvent(mThreadEvent);
|
||
|
|
||
|
gSocketTransportService = nsnull;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// event queue (any thread)
|
||
|
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::PostEvent(nsISocketEventHandler *handler,
|
||
|
PRUint32 type, PRUint32 uparam,
|
||
|
void *vparam)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::PostEvent [handler=%x type=%u u=%x v=%x]\n",
|
||
|
handler, type, uparam, vparam));
|
||
|
|
||
|
NS_ASSERTION(handler, "null handler");
|
||
|
|
||
|
nsAutoLock lock(mEventQLock);
|
||
|
NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
|
||
|
|
||
|
SocketEvent *event = new SocketEvent(handler, type, uparam, vparam);
|
||
|
if (!event)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
|
||
|
if (mEventQ.mTail)
|
||
|
mEventQ.mTail->mNext = event;
|
||
|
mEventQ.mTail = event;
|
||
|
if (!mEventQ.mHead)
|
||
|
mEventQ.mHead = event;
|
||
|
|
||
|
PR_SetPollableEvent(mThreadEvent);
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// socket api (socket thread only)
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::AttachSocket(PRFileDesc *fd, nsASocketHandler *handler)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::AttachSocket [handler=%x]\n", handler));
|
||
|
|
||
|
NS_ENSURE_TRUE(mServicingEventQ, NS_ERROR_UNEXPECTED);
|
||
|
NS_ENSURE_TRUE(mIdleCount < NS_SOCKET_MAX_COUNT, NS_ERROR_UNEXPECTED);
|
||
|
|
||
|
SocketContext *sock = &mIdleList[mIdleCount];
|
||
|
sock->mFD = fd;
|
||
|
sock->mHandler = handler;
|
||
|
NS_ADDREF(handler);
|
||
|
|
||
|
mIdleCount++;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::DetachSocket_Internal(SocketContext *sock)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::DetachSocket_Internal [handler=%x]\n", sock->mHandler));
|
||
|
|
||
|
// inform the handler that this socket is going away
|
||
|
sock->mHandler->OnSocketDetached(sock->mFD);
|
||
|
|
||
|
// cleanup
|
||
|
sock->mFD = nsnull;
|
||
|
NS_RELEASE(sock->mHandler);
|
||
|
|
||
|
// find out what list this is on...
|
||
|
PRInt32 index = sock - mActiveList;
|
||
|
if (index > 0 && index <= NS_SOCKET_MAX_COUNT)
|
||
|
RemoveFromPollList(sock);
|
||
|
else
|
||
|
RemoveFromIdleList(sock);
|
||
|
|
||
|
// NOTE: sock is now an invalid pointer
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::AddToPollList(SocketContext *sock)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::AddToPollList [handler=%x]\n", sock->mHandler));
|
||
|
|
||
|
NS_ASSERTION(mActiveCount < NS_SOCKET_MAX_COUNT, "too many active sockets");
|
||
|
|
||
|
memcpy(&mActiveList[++mActiveCount], sock, sizeof(SocketContext));
|
||
|
mPollList[mActiveCount].fd = sock->mFD;
|
||
|
mPollList[mActiveCount].in_flags = sock->mHandler->mPollFlags;
|
||
|
mPollList[mActiveCount].out_flags = 0;
|
||
|
|
||
|
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSocketTransportService::RemoveFromPollList(SocketContext *sock)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::RemoveFromPollList [handler=%x]\n", sock->mHandler));
|
||
|
|
||
|
PRUint32 index = sock - mActiveList;
|
||
|
NS_ASSERTION(index > 0 && index <= NS_SOCKET_MAX_COUNT, "invalid index");
|
||
|
|
||
|
LOG((" index=%u mActiveCount=%u\n", index, mActiveCount));
|
||
|
|
||
|
if (index != mActiveCount) {
|
||
|
memcpy(&mActiveList[index], &mActiveList[mActiveCount], sizeof(SocketContext));
|
||
|
memcpy(&mPollList[index], &mPollList[mActiveCount], sizeof(PRPollDesc));
|
||
|
}
|
||
|
mActiveCount--;
|
||
|
|
||
|
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::AddToIdleList(SocketContext *sock)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::AddToIdleList [handler=%x]\n", sock->mHandler));
|
||
|
|
||
|
NS_ASSERTION(mIdleCount < NS_SOCKET_MAX_COUNT, "too many idle sockets");
|
||
|
memcpy(&mIdleList[mIdleCount], sock, sizeof(SocketContext));
|
||
|
mIdleCount++;
|
||
|
|
||
|
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsSocketTransportService::RemoveFromIdleList(SocketContext *sock)
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::RemoveFromIdleList [handler=%x]\n", sock->mHandler));
|
||
|
|
||
|
PRUint32 index = sock - &mIdleList[0];
|
||
|
NS_ASSERTION(index >= 0 && index < NS_SOCKET_MAX_COUNT, "invalid index");
|
||
|
|
||
|
if (index != mIdleCount - 1)
|
||
|
memcpy(&mActiveList[index], &mActiveList[mIdleCount - 1], sizeof(SocketContext));
|
||
|
mIdleCount--;
|
||
|
|
||
|
LOG((" active=%u idle=%u\n", mActiveCount, mIdleCount));
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// host:port -> ipaddr cache
|
||
|
|
||
|
PLDHashTableOps nsSocketTransportService::ops =
|
||
|
{
|
||
|
PL_DHashAllocTable,
|
||
|
PL_DHashFreeTable,
|
||
|
PL_DHashGetKeyStub,
|
||
|
PL_DHashStringKey,
|
||
|
nsSocketTransportService::MatchEntry,
|
||
|
PL_DHashMoveEntryStub,
|
||
|
nsSocketTransportService::ClearEntry,
|
||
|
PL_DHashFinalizeStub,
|
||
|
nsnull
|
||
|
};
|
||
|
|
||
|
PRBool PR_CALLBACK
|
||
|
nsSocketTransportService::MatchEntry(PLDHashTable *table,
|
||
|
const PLDHashEntryHdr *entry,
|
||
|
const void *key)
|
||
|
{
|
||
|
const nsSocketTransportService::nsHostEntry *he =
|
||
|
NS_REINTERPRET_CAST(const nsSocketTransportService::nsHostEntry *, entry);
|
||
|
|
||
|
return !strcmp(he->hostport(), (const char *) key);
|
||
|
}
|
||
|
|
||
|
void PR_CALLBACK
|
||
|
nsSocketTransportService::ClearEntry(PLDHashTable *table,
|
||
|
PLDHashEntryHdr *entry)
|
||
|
{
|
||
|
nsSocketTransportService::nsHostEntry *he =
|
||
|
NS_REINTERPRET_CAST(nsSocketTransportService::nsHostEntry *, entry);
|
||
|
|
||
|
PL_strfree((char *) he->key);
|
||
|
he->key = nsnull;
|
||
|
memset(&he->addr, 0, sizeof(he->addr));
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::LookupHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr)
|
||
|
{
|
||
|
NS_ASSERTION(!host.IsEmpty(), "empty host");
|
||
|
NS_ASSERTION(addr, "null addr");
|
||
|
|
||
|
PLDHashEntryHdr *hdr;
|
||
|
nsCAutoString hostport(host + nsPrintfCString(":%d", port));
|
||
|
|
||
|
hdr = PL_DHashTableOperate(&mHostDB, hostport.get(), PL_DHASH_LOOKUP);
|
||
|
if (PL_DHASH_ENTRY_IS_BUSY(hdr)) {
|
||
|
// found match
|
||
|
nsHostEntry *ent = NS_REINTERPRET_CAST(nsHostEntry *, hdr);
|
||
|
memcpy(addr, &ent->addr, sizeof(ent->addr));
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
return NS_ERROR_UNKNOWN_HOST;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsSocketTransportService::RememberHost(const nsACString &host, PRUint16 port, PRIPv6Addr *addr)
|
||
|
{
|
||
|
// remember hostname
|
||
|
|
||
|
PLDHashEntryHdr *hdr;
|
||
|
nsCAutoString hostport(host + nsPrintfCString(":%d", port));
|
||
|
|
||
|
hdr = PL_DHashTableOperate(&mHostDB, hostport.get(), PL_DHASH_ADD);
|
||
|
if (!hdr)
|
||
|
return NS_ERROR_FAILURE;
|
||
|
|
||
|
NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(hdr), "entry not busy");
|
||
|
|
||
|
nsHostEntry *ent = NS_REINTERPRET_CAST(nsHostEntry *, hdr);
|
||
|
if (ent->key == nsnull) {
|
||
|
ent->key = (const void *) ToNewCString(hostport);
|
||
|
memcpy(&ent->addr, addr, sizeof(ent->addr));
|
||
|
}
|
||
|
#ifdef DEBUG
|
||
|
else {
|
||
|
// verify that the existing entry is in fact a perfect match
|
||
|
NS_ASSERTION(PL_strcmp(ent->hostport(), hostport.get()) == 0, "bad match");
|
||
|
NS_ASSERTION(memcmp(&ent->addr, addr, sizeof(ent->addr)) == 0, "bad match");
|
||
|
}
|
||
|
#endif
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// xpcom api
|
||
|
|
||
|
NS_IMPL_THREADSAFE_ISUPPORTS2(nsSocketTransportService,
|
||
|
nsISocketTransportService,
|
||
|
nsIRunnable)
|
||
|
|
||
|
// called from main thread only
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::Init()
|
||
|
{
|
||
|
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||
|
|
||
|
if (mInitialized)
|
||
|
return NS_OK;
|
||
|
|
||
|
if (!mThreadEvent)
|
||
|
mThreadEvent = PR_NewPollableEvent();
|
||
|
|
||
|
nsresult rv = NS_NewThread(&mThread, this, 0, PR_JOINABLE_THREAD);
|
||
|
if (NS_FAILED(rv)) return rv;
|
||
|
|
||
|
mInitialized = PR_TRUE;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
// called from main thread only
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::Shutdown()
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::Shutdown\n"));
|
||
|
|
||
|
NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread");
|
||
|
|
||
|
if (!mInitialized)
|
||
|
return NS_OK;
|
||
|
|
||
|
{
|
||
|
nsAutoLock lock(mEventQLock);
|
||
|
|
||
|
// signal uninitialized to block further events
|
||
|
mInitialized = PR_FALSE;
|
||
|
|
||
|
PR_SetPollableEvent(mThreadEvent);
|
||
|
}
|
||
|
|
||
|
// join with thread
|
||
|
mThread->Join();
|
||
|
NS_RELEASE(mThread);
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::CreateTransport(const char **types,
|
||
|
PRUint32 typeCount,
|
||
|
const nsACString &host,
|
||
|
PRInt32 port,
|
||
|
nsIProxyInfo *proxyInfo,
|
||
|
nsISocketTransport **result)
|
||
|
{
|
||
|
NS_ENSURE_TRUE(mInitialized, NS_ERROR_OFFLINE);
|
||
|
NS_ENSURE_TRUE(port >= 0 && port <= 0xFFFF, NS_ERROR_ILLEGAL_VALUE);
|
||
|
|
||
|
nsSocketTransport *trans = new nsSocketTransport();
|
||
|
if (!trans)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
NS_ADDREF(trans);
|
||
|
|
||
|
nsresult rv = trans->Init(types, typeCount, host, port, proxyInfo);
|
||
|
if (NS_FAILED(rv)) {
|
||
|
NS_RELEASE(trans);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
*result = trans;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::GetAutodialEnabled(PRBool *value)
|
||
|
{
|
||
|
*value = PR_FALSE;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::SetAutodialEnabled(PRBool value)
|
||
|
{
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP
|
||
|
nsSocketTransportService::Run()
|
||
|
{
|
||
|
LOG(("nsSocketTransportService::Run"));
|
||
|
|
||
|
gSocketThread = PR_GetCurrentThread();
|
||
|
|
||
|
//
|
||
|
// Initialize hostname database
|
||
|
//
|
||
|
PL_DHashTableInit(&mHostDB, &ops, nsnull, sizeof(nsHostEntry), 0);
|
||
|
|
||
|
//
|
||
|
// setup thread
|
||
|
//
|
||
|
NS_ASSERTION(mThreadEvent, "no thread event");
|
||
|
|
||
|
mPollList[0].fd = mThreadEvent;
|
||
|
mPollList[0].in_flags = PR_POLL_READ;
|
||
|
|
||
|
PRInt32 i, count;
|
||
|
|
||
|
//
|
||
|
// poll loop
|
||
|
//
|
||
|
PRBool active = PR_TRUE;
|
||
|
while (active) {
|
||
|
// clear out flags for pollable event
|
||
|
mPollList[0].out_flags = 0;
|
||
|
|
||
|
//
|
||
|
// walk active list backwards to see if any sockets should actually be
|
||
|
// idle, then walk the idle list backwards to see if any idle sockets
|
||
|
// should become active. take care to only check idle sockets that
|
||
|
// were idle to begin with ;-)
|
||
|
//
|
||
|
count = mIdleCount;
|
||
|
if (mActiveCount) {
|
||
|
for (i=mActiveCount; i>=1; --i) {
|
||
|
//---
|
||
|
LOG((" active [%d] { handler=%x condition=%x pollflags=%hu }\n", i,
|
||
|
mActiveList[i].mHandler,
|
||
|
mActiveList[i].mHandler->mCondition,
|
||
|
mActiveList[i].mHandler->mPollFlags));
|
||
|
//---
|
||
|
if (NS_FAILED(mActiveList[i].mHandler->mCondition))
|
||
|
DetachSocket_Internal(&mActiveList[i]);
|
||
|
else {
|
||
|
PRUint16 in_flags = mActiveList[i].mHandler->mPollFlags;
|
||
|
if (in_flags == 0)
|
||
|
MoveToIdleList(&mActiveList[i]);
|
||
|
else {
|
||
|
// update poll flags
|
||
|
mPollList[i].in_flags = in_flags;
|
||
|
mPollList[i].out_flags = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (count) {
|
||
|
for (i=count-1; i>=0; --i) {
|
||
|
//---
|
||
|
LOG((" idle [%d] { handler=%x condition=%x pollflags=%hu }\n", i,
|
||
|
mIdleList[i].mHandler,
|
||
|
mIdleList[i].mHandler->mCondition,
|
||
|
mIdleList[i].mHandler->mPollFlags));
|
||
|
//---
|
||
|
if (NS_FAILED(mIdleList[i].mHandler->mCondition))
|
||
|
DetachSocket_Internal(&mIdleList[i]);
|
||
|
else if (mIdleList[i].mHandler->mPollFlags != 0)
|
||
|
MoveToPollList(&mIdleList[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LOG((" calling PR_Poll [active=%u idle=%u]\n", mActiveCount, mIdleCount));
|
||
|
|
||
|
PRInt32 n = PR_Poll(mPollList, PollCount(), NS_SOCKET_POLL_TIMEOUT);
|
||
|
if (n < 0) {
|
||
|
LOG((" PR_Poll error [%d]\n", PR_GetError()));
|
||
|
active = PR_FALSE;
|
||
|
}
|
||
|
else if (n == 0) {
|
||
|
LOG((" PR_Poll timeout expired\n"));
|
||
|
// loop around again
|
||
|
}
|
||
|
else {
|
||
|
count = PollCount();
|
||
|
|
||
|
//
|
||
|
// service "active" sockets...
|
||
|
//
|
||
|
for (i=1; i<count; ++i) {
|
||
|
if (mPollList[i].out_flags != 0) {
|
||
|
nsASocketHandler *handler = mActiveList[i].mHandler;
|
||
|
handler->OnSocketReady(mPollList[i].fd,
|
||
|
mPollList[i].out_flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// check for "dead" sockets and remove them (need to do this in
|
||
|
// reverse order obviously).
|
||
|
//
|
||
|
for (i=count-1; i>=1; --i) {
|
||
|
if (NS_FAILED(mActiveList[i].mHandler->mCondition))
|
||
|
DetachSocket_Internal(&mActiveList[i]);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// service the event queue
|
||
|
//
|
||
|
if (mPollList[0].out_flags == PR_POLL_READ) {
|
||
|
|
||
|
// grab the event queue
|
||
|
SocketEvent *head = nsnull, *event;
|
||
|
{
|
||
|
nsAutoLock lock(mEventQLock);
|
||
|
|
||
|
// wait should not block
|
||
|
PR_WaitForPollableEvent(mPollList[0].fd);
|
||
|
|
||
|
head = mEventQ.mHead;
|
||
|
mEventQ.mHead = nsnull;
|
||
|
mEventQ.mTail = nsnull;
|
||
|
|
||
|
// check to see if we're supposed to shutdown
|
||
|
active = mInitialized;
|
||
|
}
|
||
|
// service the event queue
|
||
|
mServicingEventQ = PR_TRUE;
|
||
|
while (head) {
|
||
|
head->mHandler->OnSocketEvent(head->mType,
|
||
|
head->mUparam,
|
||
|
head->mVparam);
|
||
|
// delete head of queue
|
||
|
event = head->mNext;
|
||
|
delete head;
|
||
|
head = event;
|
||
|
}
|
||
|
mServicingEventQ = PR_FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// shutdown thread
|
||
|
//
|
||
|
|
||
|
LOG(("shutting down socket transport thread...\n"));
|
||
|
|
||
|
// detach any sockets
|
||
|
for (i=mActiveCount; i>=1; --i)
|
||
|
DetachSocket_Internal(&mActiveList[i]);
|
||
|
for (i=mIdleCount-1; i>=0; --i)
|
||
|
DetachSocket_Internal(&mIdleList[i]);
|
||
|
|
||
|
// clear the hostname database
|
||
|
PL_DHashTableFinish(&mHostDB);
|
||
|
|
||
|
gSocketThread = nsnull;
|
||
|
return NS_OK;
|
||
|
}
|