You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
554 lines
12 KiB
554 lines
12 KiB
/*++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ithreadpool.cxx
|
|
|
|
Abstract:
|
|
|
|
Implements the W3Spoof object's IThreadPool interface.
|
|
|
|
Author:
|
|
|
|
Paul M Midgen (pmidge) 08-February-2001
|
|
|
|
|
|
Revision History:
|
|
|
|
08-February-2001 pmidge
|
|
Created
|
|
|
|
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--*/
|
|
|
|
#include "common.h"
|
|
|
|
BOOL
|
|
CW3Spoof::_InitializeThreads(void)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_bool,
|
|
"CW3Spoof::_InitializeThreads",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
BOOL bStatus = FALSE;
|
|
DWORD error = 0L;
|
|
SOCKADDR_IN local = {0};
|
|
|
|
m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, m_dwMaxActiveThreads);
|
|
|
|
if( !m_hIOCP )
|
|
{
|
|
DEBUG_TRACE(
|
|
W3SOBJ,
|
|
("error creating completion port: %s", MapErrorToString(GetLastError()))
|
|
);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
m_sListen = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if( m_sListen == INVALID_SOCKET )
|
|
{
|
|
DEBUG_TRACE(
|
|
W3SOBJ,
|
|
("error creating listen socket: %s", MapErrorToString(WSAGetLastError()))
|
|
);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
local.sin_family = AF_INET;
|
|
local.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
local.sin_port = htons(m_usServerPort);
|
|
|
|
error = bind(m_sListen, (PSOCKADDR) &local, sizeof(local));
|
|
|
|
if( error == SOCKET_ERROR )
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("error binding listen socket: %s", MapErrorToString(WSAGetLastError())));
|
|
goto quit;
|
|
}
|
|
|
|
error = listen(m_sListen, SOMAXCONN);
|
|
|
|
if( error == SOCKET_ERROR )
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("error listening: %s", MapErrorToString(WSAGetLastError())));
|
|
goto quit;
|
|
}
|
|
|
|
CreateIoCompletionPort((HANDLE) m_sListen, m_hIOCP, CK_NEW_CONNECTION, m_dwMaxActiveThreads);
|
|
|
|
m_arThreads = new HANDLE[m_dwPoolSize];
|
|
|
|
if( !m_arThreads )
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("error allocating thread handles: %s", MapErrorToString(GetLastError())));
|
|
goto quit;
|
|
}
|
|
|
|
for(DWORD n=0; n < m_dwPoolSize; n++)
|
|
{
|
|
m_arThreads[n] = CreateThread(
|
|
NULL, 0,
|
|
ThreadFunc, (LPVOID) (static_cast<IThreadPool*>(this)),
|
|
0, NULL
|
|
);
|
|
|
|
if( !m_arThreads[n] )
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("error creating thread %d: %s", n, MapErrorToString(GetLastError())));
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
bStatus = TRUE;
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(bStatus);
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
void
|
|
CW3Spoof::_TerminateThreads(void)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_void,
|
|
"CW3Spoof::_TerminateThreads",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
//
|
|
// BUGBUG: this could generate *_OPERATION_ABORTED results, need to keep this in mind
|
|
// in case there are weird state issues handling aborted io ops. should be no
|
|
// problem... but you never know.
|
|
//
|
|
SAFECLOSESOCKET(m_sListen);
|
|
|
|
for(DWORD n=0; n < m_dwPoolSize; n++)
|
|
{
|
|
PostQueuedCompletionStatus(m_hIOCP, 0L, CK_CANCEL_IO, NULL);
|
|
}
|
|
|
|
WaitForMultipleObjects(m_dwPoolSize, m_arThreads, TRUE, INFINITE);
|
|
|
|
for(DWORD n=0; n < m_dwPoolSize; n++)
|
|
{
|
|
SAFECLOSE(m_arThreads[n]);
|
|
}
|
|
|
|
SAFEDELETEBUF(m_arThreads);
|
|
SAFECLOSE(m_hIOCP);
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
DWORD
|
|
CW3Spoof::_QueueAccept(void)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_dword,
|
|
"CW3Spoof::_QueueAccept",
|
|
"this=%#x",
|
|
this
|
|
));
|
|
|
|
DWORD ret = ERROR_IO_PENDING;
|
|
|
|
if( 0 == InterlockedCompareExchange(&m_PendingAccepts, 0, 0) )
|
|
{
|
|
// available pending accepts has dropped to 0, close
|
|
// the accept queue.
|
|
m_AcceptQueueStatus = 0;
|
|
}
|
|
else
|
|
{
|
|
if( m_AcceptQueueStatus )
|
|
{
|
|
BOOL bAccepted = TRUE;
|
|
PIOCTX pioc = NULL;
|
|
|
|
InterlockedDecrement(&m_PendingAccepts);
|
|
|
|
pioc = new IOCTX(IOCT_CONNECT, socket(AF_INET, SOCK_STREAM, 0));
|
|
|
|
if( pioc && pioc->sockbuf )
|
|
{
|
|
bAccepted = AcceptEx(
|
|
m_sListen,
|
|
pioc->socket,
|
|
(LPVOID) pioc->sockbuf,
|
|
0L,
|
|
sizeof(SOCKADDR_IN)+16,
|
|
sizeof(SOCKADDR_IN)+16,
|
|
NULL,
|
|
&pioc->overlapped
|
|
);
|
|
|
|
if( !bAccepted )
|
|
{
|
|
ret = WSAGetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = ERROR_SUCCESS;
|
|
DEBUG_TRACE(W3SOBJ, ("queue is closed"));
|
|
}
|
|
}
|
|
|
|
DEBUG_LEAVE(ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CW3Spoof::_CompleteAccept(PIOCTX pioc)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_bool,
|
|
"CW3Spoof::_CompleteAccept",
|
|
"this=%#x; pioc=%#x",
|
|
this, pioc
|
|
));
|
|
|
|
BOOL bStatus = FALSE;
|
|
|
|
if( pioc->socket != INVALID_SOCKET )
|
|
{
|
|
DWORD mode = 1L;
|
|
struct linger _linger = {1, 2};
|
|
|
|
setsockopt(
|
|
pioc->socket,
|
|
SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
|
|
(char*) &m_sListen, sizeof(m_sListen)
|
|
);
|
|
|
|
setsockopt(
|
|
pioc->socket,
|
|
SOL_SOCKET, SO_LINGER,
|
|
(char*) &_linger, sizeof(struct linger)
|
|
);
|
|
|
|
setsockopt(
|
|
pioc->socket,
|
|
IPPROTO_TCP, TCP_NODELAY,
|
|
(char*) &mode, sizeof(DWORD)
|
|
);
|
|
|
|
ParseSocketInfo(pioc);
|
|
|
|
bStatus = SUCCEEDED(Register(pioc->socket)) ? TRUE : FALSE;
|
|
}
|
|
|
|
// increment available pending accepts and check to see if the queue
|
|
// can be reopened
|
|
InterlockedIncrement(&m_PendingAccepts);
|
|
|
|
if( m_MaxQueuedAccepts ==
|
|
InterlockedCompareExchange(&m_PendingAccepts, m_MaxQueuedAccepts, m_MaxQueuedAccepts)
|
|
)
|
|
{
|
|
m_AcceptQueueStatus = 1;
|
|
}
|
|
|
|
DEBUG_LEAVE(bStatus);
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CW3Spoof::_DisconnectSocket(PIOCTX pioc, BOOL fNBGC)
|
|
{
|
|
if( pioc->socket != INVALID_SOCKET )
|
|
{
|
|
if( fNBGC )
|
|
{
|
|
shutdown(pioc->socket, SD_SEND);
|
|
}
|
|
else
|
|
{
|
|
struct linger _linger = {1, 0};
|
|
|
|
setsockopt(
|
|
pioc->socket,
|
|
SOL_SOCKET, SO_LINGER,
|
|
(char*) &_linger, sizeof(struct linger)
|
|
);
|
|
}
|
|
|
|
closesocket(pioc->socket);
|
|
pioc->socket = INVALID_SOCKET;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
__stdcall
|
|
CW3Spoof::GetStatus(PIOCTX* ppioc, LPBOOL pbQuit)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_hresult,
|
|
"CW3Spoof::GetStatus",
|
|
"this=%#x; ppioc=%#x; pbQuit=%#x",
|
|
this,
|
|
ppioc,
|
|
pbQuit
|
|
));
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD error = ERROR_SUCCESS;
|
|
DWORD comp = 0L;
|
|
DWORD bytes = 0L;
|
|
LPOVERLAPPED lpo = NULL;
|
|
|
|
_QueueAccept();
|
|
|
|
if( GetQueuedCompletionStatus(m_hIOCP, &bytes, &comp, &lpo, INFINITE) )
|
|
{
|
|
switch( comp )
|
|
{
|
|
case CK_NEW_CONNECTION :
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("status: new connection"));
|
|
|
|
*ppioc = GETIOCTX(lpo);
|
|
*pbQuit = FALSE;
|
|
|
|
if( (*ppioc) && _CompleteAccept(*ppioc) )
|
|
{
|
|
IW3Spoof* pw3s = NULL;
|
|
|
|
hr = QueryInterface(IID_IW3Spoof, (void**) &pw3s);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = SESSIONOBJ::Create(*ppioc, pw3s);
|
|
SAFERELEASE(pw3s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SAFERELEASE((*ppioc));
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
case CK_NORMAL :
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("status: normal"));
|
|
|
|
*ppioc = GETIOCTX(lpo);
|
|
(*ppioc)->bytes = bytes;
|
|
*pbQuit = FALSE;
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case CK_CANCEL_IO :
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("status: cancel io"));
|
|
|
|
m_AcceptQueueStatus = 0;
|
|
CancelIo((HANDLE) m_sListen);
|
|
|
|
//
|
|
// BUGBUG: temporary hack to get all threads to call CancelIo().
|
|
// i need to figure out a better way.
|
|
//
|
|
// P.S. this works because it puts the thread in a non-alertable state,
|
|
// which causes the completion port code to wake up another thread.
|
|
//
|
|
Sleep(100);
|
|
|
|
PostQueuedCompletionStatus(m_hIOCP, 0L, CK_TERMINATE_THREAD, NULL);
|
|
|
|
*ppioc = NULL;
|
|
*pbQuit = FALSE;
|
|
hr = E_FAIL;
|
|
}
|
|
break;
|
|
|
|
case CK_TERMINATE_THREAD :
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("status: terminate thread"));
|
|
|
|
*ppioc = NULL;
|
|
*pbQuit = TRUE;
|
|
hr = E_FAIL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
|
|
switch( error )
|
|
{
|
|
case ERROR_NETNAME_DELETED :
|
|
{
|
|
*ppioc = GETIOCTX(lpo);
|
|
|
|
//
|
|
// happens when a client closes a keep-alive connection on which
|
|
// we have a receive pending. if there's a session associated with
|
|
// this IO, we defer error handling to the session fsm.
|
|
//
|
|
if( (*ppioc)->clientid && SUCCEEDED(m_sessionmap->Get((*ppioc)->clientid, (void**) &(*ppioc)->session)) )
|
|
{
|
|
(*ppioc)->error = error;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
_DisconnectSocket(*ppioc, TRUE);
|
|
SAFERELEASE((*ppioc));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ERROR_OPERATION_ABORTED :
|
|
{
|
|
*ppioc = GETIOCTX(lpo);
|
|
|
|
//
|
|
// happens when CancelIo() or closesocket() are called and there are
|
|
// pending overlapped operations. if there's a session associated with
|
|
// the IOCTX, we defer error handling to the session fsm.
|
|
//
|
|
if( (*ppioc)->clientid && SUCCEEDED(m_sessionmap->Get((*ppioc)->clientid, (void**) &(*ppioc)->session)) )
|
|
{
|
|
(*ppioc)->error = error;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
_DisconnectSocket(*ppioc, FALSE);
|
|
SAFERELEASE((*ppioc));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default :
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("unhandled error - %s", MapErrorToString(error)));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUG_LEAVE(hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
__stdcall
|
|
CW3Spoof::GetSession(LPWSTR clientid, PSESSIONOBJ* ppso)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = m_sessionmap->Get(clientid, (void**) ppso);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
__stdcall
|
|
CW3Spoof::Register(SOCKET s)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_W3SOBJ,
|
|
rt_hresult,
|
|
"CW3Spoof::Register",
|
|
"this=%#x; s=%#x",
|
|
this,
|
|
s
|
|
));
|
|
|
|
HRESULT hr = S_OK;
|
|
HANDLE ret = NULL;
|
|
|
|
ret = CreateIoCompletionPort(
|
|
(HANDLE) s, m_hIOCP,
|
|
CK_NORMAL, m_dwMaxActiveThreads
|
|
);
|
|
|
|
if( !ret )
|
|
{
|
|
DEBUG_TRACE(W3SOBJ, ("failed to associate socket!"));
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
DEBUG_LEAVE(hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
ThreadFunc(LPVOID lpv)
|
|
{
|
|
DEBUG_ENTER((
|
|
DBG_WORKER,
|
|
rt_hresult,
|
|
"worker",
|
|
"lpv=%#x",
|
|
lpv
|
|
));
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL bQuit = FALSE;
|
|
PIOCTX pioc = NULL;
|
|
IThreadPool* ptp = (IThreadPool*) lpv;
|
|
PSESSIONOBJ pso = NULL;
|
|
|
|
while( !bQuit )
|
|
{
|
|
if( SUCCEEDED(ptp->GetStatus(&pioc, &bQuit)) )
|
|
{
|
|
if( (pso = pioc->session) || SUCCEEDED(ptp->GetSession(pioc->clientid, &pso)) )
|
|
{
|
|
pso->Run(pioc);
|
|
}
|
|
|
|
SAFERELEASE(pioc);
|
|
}
|
|
}
|
|
|
|
DEBUG_LEAVE(hr);
|
|
return hr;
|
|
}
|