2025-11-28 00:35:46 +09:00

1637 lines
48 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 2004 Microsoft Corporation. All Rights Reserved.
//
// Module Name: overlap.cpp
//
// Description:
//
// This module is responsible for handling the overlapped IO
// passed to us by the upper layer. If the LSP is on NT, then
// an IO completion port (IOCP) is created and all overlapped
// IO initiated by the upper layer passes through our IOCP.
// If this is Win9x then we create a worker thread and execute
// the overlapped IO via asynchronous procedure calls (APC).
//
#include "lspdef.h"
#include <stdio.h>
#include <stdlib.h>
#pragma warning(disable:4127) // disable "conditional expression is constant" warning
#pragma warning(disable:4702) // disable "unreacheable code" warning
//
// For each overlapped operation given to us from the upper layer, we'll
// assign a WSAOVERLAPPEDPLUS structure so that we can make the overlapped
// call to the lower layer. The following variables are pointers to these
// structures.
//
static LIST_ENTRY gPendingOperations; // list of queued overlapped operations (Win9x)
static LIST_ENTRY gFreeOverlappedPlus; // look aside list of free structures
//
// NOTE: On Win9x a semaphore is used to trigger when an overlapped operation
// is queued for execution. On NT based OSes (including XP), a completion
// port is used. That is, the use of 'gIocp' and 'gWakeupSemaphore' is
// mutually exclusive.
//
HANDLE *gWorkerThread = NULL, // Array of handles to worker threads
gWakeupSemaphore = NULL, // Wakeup semaphore
gIocp = NULL; // Handle to the IOCP
DWORD gThreadCount = (DWORD)-1; // Number of worker threads
////////////////////////////////////////////////////////////////////////////////
//
// Function prototypes
//
////////////////////////////////////////////////////////////////////////////////
// Either allocates a new structure or returns a free one on a lookaside list
LPWSAOVERLAPPEDPLUS
AllocOverlappedStructure(
SOCK_INFO *SocketContext
);
// Returns the overlapped structure to a list of free structures
void
FreeOverlappedStructure(
WSAOVERLAPPEDPLUS *olp
);
// Executes an overlapped operation
int
ExecuteOverlappedOperation(
WSAOVERLAPPEDPLUS *lpOverlapped,
BOOL bSynchronous
);
// On Win9x queues an overlapped operation for later execution. On WinNT, immediately
// execute the call.
int
EnqueueOverlappedOperation(
WSAOVERLAPPEDPLUS *op
);
// Sets the overlapped in progress flag in the given OVERLAPPED structure
void
SetOverlappedInProgress(
OVERLAPPED *ol
);
// Overlapped thread which either handles overlapped completion via completion port
// or executes overlapped operations with APCs.
DWORD WINAPI
OverlappedManagerThread(
LPVOID lpParam
);
// Invokes the APC associated with an overlapped operation to signal completion
VOID CALLBACK
CallUserApcProc(
ULONG_PTR Context
);
// Checks whether a given socket has been closed in which case all associated resrouces
// can be freed
void
CheckForContextCleanup(
WSAOVERLAPPEDPLUS *ol
);
////////////////////////////////////////////////////////////////////////////////
//
// Function Implementation
//
////////////////////////////////////////////////////////////////////////////////
//
// Function: InitOverlappedManager
//
// Description:
// This function is called once and determines whether we're running
// on NT or Win9x. If on NT, create an IO completion port as the
// intermediary between the upper and lower layer. All overlapped IO
// will be posted using this IOCP. When IOCP are available we'll create
// a number of threads (equal to the number of procs) to service the
// completion of the IO. If on Win9x, we'll create a single thread which
// will post overlapped IO operations using APCs.
//
int
InitOverlappedManager(
)
{
DWORD i;
int ret = NO_ERROR;
EnterCriticalSection( &gOverlappedCS );
//
// Make sure we're not already initialized -- we'll always have at least
// one worker thread running
//
if ( NULL != gWorkerThread )
goto cleanup;
InitializeListHead( &gFreeOverlappedPlus );
InitializeListHead( &gPendingOperations );
//
// See if we're on NT by trying to create the completion port. If it
// fails then we're on Win9x.
//
gIocp = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
(ULONG_PTR)0,
0
);
if ( NULL != gIocp )
{
SYSTEM_INFO sinfo;
//
// We're on NT so figure out how many processors we have
//
GetSystemInfo( &sinfo );
gThreadCount = sinfo.dwNumberOfProcessors;
}
else
{
//
// We're on Win9x so create a semaphore instead. This is used to
// wake up the worker thread to service overlapped IO calls.
//
gWakeupSemaphore = CreateSemaphore(
NULL,
0,
MAXLONG,
NULL
);
if ( NULL == gWakeupSemaphore )
{
dbgprint("InitOverlappedManager: CreateSemaphore() failed: %d", GetLastError());
ret = WSAEPROVIDERFAILEDINIT;
goto cleanup;
}
//
// This is Win9x, no multiproc support so create just a single thread
//
gThreadCount = 1;
}
dbgprint("Creating %d threads", gThreadCount);
gWorkerThread = (HANDLE *) LspAlloc(
sizeof( HANDLE ) * gThreadCount,
&ret // if fails, ret will be WSAENOBUFS
);
if ( NULL == gWorkerThread )
{
goto cleanup;
}
//
// Create our worker threads
//
for(i=0; i < gThreadCount ;i++)
{
gWorkerThread[i] = CreateThread(
NULL,
0,
OverlappedManagerThread,
(LPVOID)gIocp,
0,
NULL
);
if ( NULL == gWorkerThread[ i ] )
{
dbgprint("InitOverlappedManager: CreateThread() failed: %d", GetLastError());
ret = WSAEPROVIDERFAILEDINIT;
goto cleanup;
}
}
cleanup:
LeaveCriticalSection( &gOverlappedCS );
return ret;
}
//
// Function: StopOverlappedManager
//
// Description:
// This function is called before the DLL gets unloaded. It tries to
// shutdown the worker threads gracefully before exiting. It also
// frees/closes allocated memory and handles.
//
int
StopOverlappedManager(
)
{
DWORD i;
int ret = NO_ERROR;
EnterCriticalSection( &gOverlappedCS );
//
// Post a completion packet to the IOCP (one for each thread)
//
if ( NULL != gIocp )
{
for(i=0; i < gThreadCount ;i++)
{
ret = PostQueuedCompletionStatus(
gIocp,
(DWORD)-1,
0,
NULL
);
if ( 0 == ret )
{
dbgprint("PostQueuedCompletionStatus() failed: %d", GetLastError());
}
}
//
// Wait a while for the threads to get the signal and exit - if it fails or
// times out then oh well, we need to clean up anyway
//
}
else
{
//
// On Win9x we closed the semaphore so the worker thread should fail
// when waiting for a signal and break out of the loop.
//
LIST_ENTRY *entry = NULL;
WSAOVERLAPPEDPLUS *olp = NULL;
while ( !IsListEmpty( &gPendingOperations ) )
{
entry = RemoveHeadList( &gPendingOperations );
olp = CONTAINING_RECORD( entry, WSAOVERLAPPEDPLUS, Link );
FreeOverlappedStructure( olp );
}
}
if ( NULL != gWorkerThread )
{
ret = WaitForMultipleObjectsEx( gThreadCount, gWorkerThread, TRUE, 5000, TRUE );
if ( WAIT_TIMEOUT == ret )
dbgprint("StopOverlappedManager: Timed out waiting for IOCP threads to exit!");
else if ( WAIT_FAILED == ret )
dbgprint("StopOverlappedManager: WaitForMultipleObjectsEx failed: %d",
GetLastError());
else
dbgprint("StopOverlappedManager: All worker threads stopped!");
for(i=0; i < gThreadCount ;i++)
{
CloseHandle( gWorkerThread[ i ] );
gWorkerThread[ i ] = NULL;
dbgprint("Closing overlapped thread(s)");
}
LspFree( gWorkerThread );
gWorkerThread = NULL;
}
//
// Cleanup remaining handles...
//
if ( NULL != gIocp )
{
CloseHandle( gIocp );
gIocp = NULL;
}
if ( NULL != gWakeupSemaphore )
{
CloseHandle( gWakeupSemaphore );
gWakeupSemaphore = NULL;
}
LeaveCriticalSection( &gOverlappedCS );
return ret;
}
//
// Function: QueueOverlappedOperation
//
// Description:
// Whenever one of the overlapped enabled Winsock SPI functions are
// called (as an overlapped operation), it calls this function which
// determines whether it can execute it immediate (in the case of NT
// and IOCP) or post it to a queue to be executed by the woker thread
// via an APC (on Win9x).
//
int
QueueOverlappedOperation(
WSAOVERLAPPEDPLUS *ol,
SOCK_INFO *SocketContext
)
{
BOOL bSynchronous = FALSE;
int err;
//
// Set the fields of the overlapped to indicate IO is not complete yet
//
__try
{
SetOverlappedInProgress( ol->lpCallerOverlapped );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
return WSAEFAULT;
}
if ( NULL != gIocp )
{
//
// If we haven't already added the provider socket to the IOCP then
// do it now.
//
AcquireSocketLock( SocketContext );
if ( NULL == SocketContext->hIocp )
{
SocketContext->hIocp = CreateIoCompletionPort(
(HANDLE)ol->ProviderSocket,
gIocp,
ol->CallerSocket,
0
);
if ( NULL == SocketContext->hIocp )
{
if ( ERROR_INVALID_PARAMETER == (err = GetLastError() ) )
{
//
// If the socket option SO_SYNCHRONOUS_(NON)ALERT is set then
// no overlapped operation can be performed on that socket and
// tryiing to associate it with a completion port will fail.
// The other bad news is that an LSP cannot trap this setsockopt
// call. In reality we don't have to do anything. If an app sets
// this option and then makes overlapped calls anyway, then they
// shouldn't be expecting any overlapped notifications! This
// statement is put here in case you want to mark the socket
// info structure as synchronous.
//
bSynchronous = TRUE;
}
else
{
dbgprint("QueueOverlappedOperation: CreateIoCompletionPort() "
"failed: %d (Prov %d Iocp 0x%x Caller 0x%x 0)",
err, ol->ProviderSocket,
gIocp, ol->CallerSocket);
}
}
dbgprint("Adding provider handle %X to IOCP", ol->ProviderSocket);
}
ReleaseSocketLock( SocketContext );
//
// Simply execute the operation
//
return ExecuteOverlappedOperation( ol, bSynchronous );
}
else
{
// Queue up the operation for the worker thread to initiate
//
return EnqueueOverlappedOperation( ol );
}
}
//
// Function: EnqueueOverlappedOperation
//
// Description:
// Enqueue an overlapped operation to be executed by the worker
// thread via an APC.
//
int
EnqueueOverlappedOperation(
WSAOVERLAPPEDPLUS *op
)
{
int ret = WSA_IO_PENDING;
EnterCriticalSection( &gOverlappedCS );
if ( NULL == op )
{
dbgprint("EnqueueOverlappedOperation: op == NULL!");
ret = WSAEFAULT;
goto cleanup;
}
InsertTailList( &gPendingOperations, &op->Link );
//
// Increment the semaphore count. This lets the worker thread
// know that there are pending overlapped operations to execute.
//
ReleaseSemaphore( gWakeupSemaphore, 1, NULL );
cleanup:
LeaveCriticalSection( &gOverlappedCS );
return ret;
}
//
// Function: DequeueOverlappedOperation
//
// Description:
// Once the worker thread is notified that there are pending
// operations, it calls this function to get the first operation
// pending in order to execute it.
//
WSAOVERLAPPEDPLUS *
DequeueOverlappedOperation(
)
{
WSAOVERLAPPEDPLUS *op = NULL;
LIST_ENTRY *link = NULL;
EnterCriticalSection( &gOverlappedCS );
link = RemoveHeadList( &gPendingOperations );
op = CONTAINING_RECORD( link, WSAOVERLAPPEDPLUS, Link );
/*
op = gPendingStart;
if ( NULL != gPendingStart )
{
gPendingStart = op->next;
}
if (op == gPendingEnd)
{
gPendingStart = gPendingEnd = NULL;
}
*/
LeaveCriticalSection( &gOverlappedCS );
return op;
}
//
// Function: ExecuteOverlappedOperation
//
// Description:
// This function actually executes an overlapped operation that was queued.
// If on Win9x we substitute our own completion function in order to intercept
// the results. If on NT we post the operation to our completion port.
// This function either returns NO_ERROR if the operation succeeds immediately
// or the Winsock error code upone failure (or overlapped operation).
//
int
ExecuteOverlappedOperation(
WSAOVERLAPPEDPLUS *ol,
BOOL bSynchronous
)
{
LPWSAOVERLAPPED_COMPLETION_ROUTINE routine = NULL;
PROVIDER *Provider;
DWORD *lpdwFlags = NULL,
*lpdwBytes = NULL,
dwBytes;
int ret=SOCKET_ERROR, err=0;
if ( NULL == gIocp)
routine = IntermediateCompletionRoutine;
Provider = ol->Provider;
//
// Reset the event handle if present. The handle is masked with 0xFFFFFFFE in
// order to zero out the last bit. If the last bit is one and the socket is
// associated with a compeltion port then when an overlapped operation is
// called, the operation is not posted to the IO completion port.
//
if ( NULL != ol->lpCallerOverlapped->hEvent )
{
ULONG_PTR ptr = 1;
ResetEvent( (HANDLE) ( (ULONG_PTR) ol->lpCallerOverlapped->hEvent & ~ptr ) );
}
switch ( ol->Operation )
{
case LSP_OP_IOCTL:
lpdwFlags = NULL;
lpdwBytes = &ol->IoctlArgs.cbBytesReturned;
ret = Provider->NextProcTable.lpWSPIoctl(
ol->ProviderSocket,
ol->IoctlArgs.dwIoControlCode,
ol->IoctlArgs.lpvInBuffer,
ol->IoctlArgs.cbInBuffer,
ol->IoctlArgs.lpvOutBuffer,
ol->IoctlArgs.cbOutBuffer,
&ol->IoctlArgs.cbBytesReturned,
&ol->ProviderOverlapped,
routine,
&ol->CallerThreadId,
&err
);
break;
case LSP_OP_RECV:
lpdwFlags = &ol->RecvArgs.dwFlags;
lpdwBytes = &ol->RecvArgs.dwNumberOfBytesRecvd;
ret = Provider->NextProcTable.lpWSPRecv(
ol->ProviderSocket,
ol->RecvArgs.lpBuffers,
ol->RecvArgs.dwBufferCount,
&ol->RecvArgs.dwNumberOfBytesRecvd,
&ol->RecvArgs.dwFlags,
&ol->ProviderOverlapped,
routine,
&ol->CallerThreadId,
&err
);
break;
case LSP_OP_RECVFROM:
lpdwFlags = &ol->RecvFromArgs.dwFlags;
lpdwBytes = &ol->RecvFromArgs.dwNumberOfBytesRecvd;
ret = Provider->NextProcTable.lpWSPRecvFrom(
ol->ProviderSocket,
ol->RecvFromArgs.lpBuffers,
ol->RecvFromArgs.dwBufferCount,
&ol->RecvFromArgs.dwNumberOfBytesRecvd,
&ol->RecvFromArgs.dwFlags,
ol->RecvFromArgs.lpFrom,
ol->RecvFromArgs.lpFromLen,
&ol->ProviderOverlapped,
routine,
&ol->CallerThreadId,
&err
);
break;
case LSP_OP_SEND:
lpdwFlags = &ol->SendArgs.dwFlags;
lpdwBytes = &ol->SendArgs.dwNumberOfBytesSent;
ret = Provider->NextProcTable.lpWSPSend(
ol->ProviderSocket,
ol->SendArgs.lpBuffers,
ol->SendArgs.dwBufferCount,
&ol->SendArgs.dwNumberOfBytesSent,
ol->SendArgs.dwFlags,
&ol->ProviderOverlapped,
routine,
&ol->CallerThreadId,
&err
);
break;
case LSP_OP_SENDTO:
lpdwFlags = &ol->SendToArgs.dwFlags;
lpdwBytes = &ol->SendToArgs.dwNumberOfBytesSent;
ret = Provider->NextProcTable.lpWSPSendTo(
ol->ProviderSocket,
ol->SendToArgs.lpBuffers,
ol->SendToArgs.dwBufferCount,
&ol->SendToArgs.dwNumberOfBytesSent,
ol->SendToArgs.dwFlags,
(SOCKADDR *)&ol->SendToArgs.To,
ol->SendToArgs.iToLen,
&ol->ProviderOverlapped,
routine,
&ol->CallerThreadId,
&err
);
break;
case LSP_OP_TRANSMITFILE:
lpdwFlags = &ol->TransmitFileArgs.dwFlags;
lpdwBytes = NULL;
ret = Provider->NextProcTableExt.lpfnTransmitFile(
ol->ProviderSocket,
ol->TransmitFileArgs.hFile,
ol->TransmitFileArgs.nNumberOfBytesToWrite,
ol->TransmitFileArgs.nNumberOfBytesPerSend,
&ol->ProviderOverlapped,
ol->TransmitFileArgs.lpTransmitBuffers,
ol->TransmitFileArgs.dwFlags
);
if ( FALSE == ret )
{
ret = SOCKET_ERROR;
err = WSAGetLastError();
WSASetLastError(err);
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_ACCEPTEX:
lpdwFlags = NULL;
lpdwBytes = &ol->AcceptExArgs.dwBytesReceived;
ret = Provider->NextProcTableExt.lpfnAcceptEx(
ol->ProviderSocket,
ol->AcceptExArgs.sProviderAcceptSocket,
ol->AcceptExArgs.lpOutputBuffer,
ol->AcceptExArgs.dwReceiveDataLength,
ol->AcceptExArgs.dwLocalAddressLength,
ol->AcceptExArgs.dwRemoteAddressLength,
&ol->AcceptExArgs.dwBytesReceived,
&ol->ProviderOverlapped
);
if ( FALSE == ret )
{
ret = SOCKET_ERROR;
err = WSAGetLastError();
WSASetLastError(err);
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_CONNECTEX:
lpdwFlags = NULL;
lpdwBytes = &ol->ConnectExArgs.dwBytesSent;
ret = Provider->NextProcTableExt.lpfnConnectEx(
ol->ProviderSocket,
(SOCKADDR *)&ol->ConnectExArgs.name,
ol->ConnectExArgs.namelen,
ol->ConnectExArgs.lpSendBuffer,
ol->ConnectExArgs.dwSendDataLength,
&ol->ConnectExArgs.dwBytesSent,
&ol->ProviderOverlapped
);
if ( FALSE == ret )
{
ret = SOCKET_ERROR;
err = WSAGetLastError();
WSASetLastError(err);
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_DISCONNECTEX:
lpdwFlags = &ol->DisconnectExArgs.dwFlags;
lpdwBytes = NULL;
ret = Provider->NextProcTableExt.lpfnDisconnectEx(
ol->ProviderSocket,
&ol->ProviderOverlapped,
ol->DisconnectExArgs.dwFlags,
ol->DisconnectExArgs.dwReserved
);
if ( FALSE == ret )
{
ret = SOCKET_ERROR;
err = WSAGetLastError();
WSASetLastError(err);
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_TRANSMITPACKETS:
lpdwFlags = &ol->TransmitPacketsArgs.dwFlags;
lpdwBytes = NULL;
ret = Provider->NextProcTableExt.lpfnTransmitPackets(
ol->ProviderSocket,
ol->TransmitPacketsArgs.lpPacketArray,
ol->TransmitPacketsArgs.nElementCount,
ol->TransmitPacketsArgs.nSendSize,
&ol->ProviderOverlapped,
ol->TransmitPacketsArgs.dwFlags
);
if ( FALSE == ret )
{
ret = SOCKET_ERROR;
err = WSAGetLastError();
WSASetLastError(err);
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_RECVMSG:
lpdwFlags = NULL;
lpdwBytes = &ol->RecvMsgArgs.dwNumberOfBytesRecvd;
ret = Provider->NextProcTableExt.lpfnWSARecvMsg(
ol->ProviderSocket,
&ol->RecvMsgArgs.WsaMsg,
&ol->RecvMsgArgs.dwNumberOfBytesRecvd,
&ol->ProviderOverlapped,
routine
);
if ( SOCKET_ERROR == ret )
{
err = WSAGetLastError();
WSASetLastError( err );
}
else
{
ret = NO_ERROR;
}
break;
case LSP_OP_SENDMSG:
lpdwFlags = &ol->SendMsgArgs.SendMsg.dwFlags;
lpdwBytes = ol->SendMsgArgs.SendMsg.lpNumberOfBytesSent;
ret = Provider->NextProcTable.lpWSPIoctl(
ol->ProviderSocket,
SIO_EXT_SENDMSG,
&ol->SendMsgArgs.SendMsg,
sizeof(ol->SendMsgArgs.SendMsg),
&err,
sizeof(err),
&dwBytes,
NULL,
NULL,
&ol->CallerThreadId,
&err
);
if (NO_ERROR != err)
{
ret = SOCKET_ERROR;
}
else
{
ret = NO_ERROR;
}
break;
default:
dbgprint("ExecuteOverlappedOperation: Unknown operation!");
ret = SOCKET_ERROR;
break;
}
if ( ( NO_ERROR != ret ) && ( WSA_IO_PENDING != err ) )
{
//
// If the call immediately fails, update the OVERLAPPED info and return
//
ol->lpCallerOverlapped->Offset = (lpdwFlags ? *lpdwFlags : 0);
ol->lpCallerOverlapped->OffsetHigh = err;
ol->lpCallerOverlapped->InternalHigh = (lpdwBytes ? *lpdwBytes : 0);
ol->lpCallerOverlapped->Internal = 0;
dbgprint("Overlap op failed immediately: %d", ol->Error);
CheckForContextCleanup( ol );
FreeOverlappedStructure( ol );
}
else if ( ( NO_ERROR == ret ) && ( FALSE == bSynchronous ) )
{
//
// NOTE: We could return success from here, but if you want to perform
// some post processing on the data buffers before indicating
// success to the upper layer (because once success is returned, the
// upper layer can inspect the buffers). Because of this we return
// pending and in the completion processing (either via IOCP or
// APC), we can do post processing there.
//
err = WSA_IO_PENDING;
ret = SOCKET_ERROR;
}
else if ( ( NO_ERROR == ret ) && ( TRUE == bSynchronous ) )
{
// The winsock call actually blocked and there will be no completion
// notification on the IOCP.
//
dbgprint("Succeeded without error - synchronous socket though");
}
return ( ( NO_ERROR == ret ) ? ret : err );
}
//
// Function: OverlappedManagerThread
//
// Description:
// This thread receives the completion notifications for operations
// posted to our IO completion port. Once we receive a completion
// we'll complete the operation back to the calling app.
//
DWORD WINAPI
OverlappedManagerThread(
LPVOID lpParam
)
{
WSAOVERLAPPEDPLUS *pOverlapPlus = NULL;
WSAOVERLAPPED *pOverlap = NULL;
HANDLE hIocp = (HANDLE)lpParam;
ULONG_PTR key;
DWORD dwBytesXfered;
int ret;
while ( TRUE )
{
if ( NULL != hIocp )
{
ret = GetQueuedCompletionStatus(
hIocp,
&dwBytesXfered,
&key,
&pOverlap,
INFINITE
);
if ( 0 == ret )
{
// Socket failures could be reported here so we still
// call IntermediateCompletionRoutine
dbgprint("GetQueuedCompletionStatus() failed: %d", GetLastError());
}
if ( -1 == dwBytesXfered )
{
//
// StopOverlappedManager will send a completion packet with -1
// bytes transfered to indicate the completion routine
// should exit
//
dbgprint("OverlappedManagerThread: Received exit message");
goto cleanup;
}
// Handle the IO that completed
IntermediateCompletionRoutine(
WSA_IO_PENDING,
dwBytesXfered,
pOverlap,
0
);
}
else
{
ret = WaitForSingleObjectEx(
gWakeupSemaphore,
INFINITE,
TRUE
);
if ( WAIT_IO_COMPLETION == ret )
{
// An APC fired so keep waiting until semaphore is signaled
continue;
}
else if ( WAIT_OBJECT_0 == ret )
{
pOverlapPlus = DequeueOverlappedOperation();
if ( NULL == pOverlapPlus )
continue;
ExecuteOverlappedOperation( pOverlapPlus, FALSE );
}
else
{
dbgprint("OverlappedManagerThread: WaitForSingleObjectEx() failed: %d (error = %d)",
ret, GetLastError() );
goto cleanup;
}
}
}
cleanup:
ExitThread( 0 );
}
//
// Function: AllocOverlappedStructure
//
// Description:
// This returns an unused WSAOVERLAPPEDPLUS structure. We maintain a list
// of freed structures so that we don't spend too much time allocating and
// freeing memory.
//
LPWSAOVERLAPPEDPLUS
AllocOverlappedStructure(
SOCK_INFO *SocketContext
)
{
LPWSAOVERLAPPEDPLUS lpWorkerOverlappedPlus = NULL;
if ( NULL == SocketContext )
{
dbgprint("AllocOverlappedStructure: SocketContext is NULL!");
return NULL;
}
EnterCriticalSection( &gOverlappedCS );
AcquireSocketLock( SocketContext );
//
// We have to keep track of the number of outstanding overlapped requests
// an application has. Otherwise, if the app were to close a socket that
// had oustanding overlapped ops remaining, we'd start leaking structures
// in gOverlappedPool. The idea here is to force the CallerSocket to remain
// open until the lower provider has processed all the overlapped requests.
// If we closed both the lower socket and the caller socket, we would no
// longer be able to correlate completed requests to any apps sockets.
//
(SocketContext->dwOutstandingAsync)++;
if ( IsListEmpty( &gFreeOverlappedPlus ) )
{
int err;
lpWorkerOverlappedPlus = (WSAOVERLAPPEDPLUS *) LspAlloc(
sizeof(WSAOVERLAPPEDPLUS),
&err
);
}
else
{
LIST_ENTRY *entry = NULL;
entry = RemoveHeadList( &gFreeOverlappedPlus );
lpWorkerOverlappedPlus = CONTAINING_RECORD( entry, WSAOVERLAPPEDPLUS, Link );
}
ReleaseSocketLock( SocketContext );
LeaveCriticalSection( &gOverlappedCS );
return lpWorkerOverlappedPlus;
}
//
// Function: FreeOverlappedStructure
//
// Description:
// Once we're done using a WSAOVERLAPPEDPLUS structure we return it to
// a list of free structures for re-use later.
//
void
FreeOverlappedStructure(
WSAOVERLAPPEDPLUS *olp
)
{
EnterCriticalSection( &gOverlappedCS );
switch ( olp->Operation )
{
case LSP_OP_RECV:
FreeWSABuf( olp->RecvArgs.lpBuffers );
break;
case LSP_OP_RECVFROM:
FreeWSABuf( olp->RecvFromArgs.lpBuffers );
break;
case LSP_OP_SEND:
FreeWSABuf( olp->SendArgs.lpBuffers );
break;
case LSP_OP_SENDTO:
FreeWSABuf( olp->SendToArgs.lpBuffers );
break;
default:
break;
}
memset( olp, 0, sizeof( WSAOVERLAPPEDPLUS ) );
InsertHeadList( &gFreeOverlappedPlus, &olp->Link );
LeaveCriticalSection( &gOverlappedCS );
}
//
// Function: SetOverlappedInProgress
//
// Description:
// Simply set the interal fields of an OVERLAPPED structure to the
// "in progress" state.
//
void
SetOverlappedInProgress(
OVERLAPPED *ol
)
{
ol->Internal = WSS_OPERATION_IN_PROGRESS;
ol->InternalHigh = 0;
}
//
// Function: IntermediateCompletionRoutine
//
// Description:
// Once an overlapped operation completes we call this routine to keep
// count of the bytes sent/received as well as to complete the operation
// to the application.
//
void CALLBACK
IntermediateCompletionRoutine(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
)
{
LPWSAOVERLAPPEDPLUS olp = NULL;
SOCK_INFO *SocketContext = NULL,
*AcceptSocketContext = NULL;
int Error,
ret;
if ( NULL == lpOverlapped )
{
dbgprint("IntermediateCompletionRoutine: lpOverlapped == NULL!");
goto cleanup;
}
ASSERT( lpOverlapped );
olp = CONTAINING_RECORD( lpOverlapped, WSAOVERLAPPEDPLUS, ProviderOverlapped );
//
// We actually already have the socket context for this operation (its in
// the WSAOVERLAPPEDPLUS structure but do this anyway to make sure the
// socket hasn't been closed as well as to increment the ref count while
// we're accessing the SOCK_INFO structure.
//
SocketContext = FindAndRefSocketContext(olp->CallerSocket, &Error);
if ( NULL == SocketContext )
{
dbgprint( "IntermediateCompletionRoutine: FindAndRefSocketContext failed!" );
goto cleanup;
}
if ( WSA_IO_PENDING == dwError )
{
//
// Get the results of the operation
//
ASSERT( olp->Provider );
ASSERT( olp->Provider->NextProcTable.lpWSPGetOverlappedResult );
dwError = NO_ERROR;
ret = olp->Provider->NextProcTable.lpWSPGetOverlappedResult(
olp->ProviderSocket,
lpOverlapped,
&cbTransferred,
FALSE,
&dwFlags,
(int *)&dwError
);
if ( FALSE == ret )
{
dbgprint("IntermediateCompletionRoutine: WSPGetOverlappedResult failed: %d", dwError);
}
else
{
dbgprint("Bytes transferred on socket 0x%x: %d [op=%d; err=%d]",
olp->CallerSocket, cbTransferred, olp->Operation, dwError);
}
}
olp->lpCallerOverlapped->Offset = dwFlags;
olp->lpCallerOverlapped->OffsetHigh = dwError;
olp->lpCallerOverlapped->InternalHigh = cbTransferred;
SocketContext->LastError = dwError;
if ( 0 == dwError )
{
AcquireSocketLock( SocketContext );
//
// NOTE: This is where any post processing should go for overlapped operations.
// For example, if you wanted to inspect the data received, you would do
// that here for any operation that receives data (don't forget AcceptEx!).
// In this sample, all we do is count the bytes sent and received on the
// socket.
//
switch ( olp->Operation )
{
case LSP_OP_RECV:
SocketContext->BytesRecv += cbTransferred;
FreeWSABuf(olp->RecvArgs.lpBuffers);
break;
case LSP_OP_RECVFROM:
SocketContext->BytesRecv += cbTransferred;
FreeWSABuf(olp->RecvFromArgs.lpBuffers);
break;
case LSP_OP_SEND:
SocketContext->BytesSent += cbTransferred;
FreeWSABuf(olp->SendArgs.lpBuffers);
break;
case LSP_OP_SENDTO:
SocketContext->BytesSent += cbTransferred;
FreeWSABuf(olp->SendToArgs.lpBuffers);
break;
case LSP_OP_TRANSMITFILE:
SocketContext->BytesSent += cbTransferred;
break;
case LSP_OP_TRANSMITPACKETS:
SocketContext->BytesSent += cbTransferred;
break;
case LSP_OP_ACCEPTEX:
ReleaseSocketLock( SocketContext );
AcceptSocketContext = FindAndRefSocketContext(
olp->AcceptExArgs.sAcceptSocket,
&Error
);
if (AcceptSocketContext == NULL)
{
dbgprint( "IntermediateCompletionRoutine: FindAndRefSocketContext failed! (LSP_OP_ACCEPTEX)" );
dwError = WSAENOTSOCK;
olp->lpCallerOverlapped->OffsetHigh = dwError;
}
else
{
AcquireSocketLock( AcceptSocketContext );
// Release the reference on the accepted socket object
AcceptSocketContext->dwOutstandingAsync--;
AcceptSocketContext->BytesRecv += cbTransferred;
ReleaseSocketLock( AcceptSocketContext );
DerefSocketContext(AcceptSocketContext, &Error);
}
break;
case LSP_OP_CONNECTEX:
SocketContext->BytesSent += cbTransferred;
break;
case LSP_OP_RECVMSG:
SocketContext->BytesRecv += cbTransferred;
FreeWSABuf(olp->RecvMsgArgs.WsaMsg.lpBuffers);
break;
case LSP_OP_SENDMSG:
SocketContext->BytesSent += cbTransferred;
FreeWSABuf(olp->SendMsgArgs.SendMsg.lpMsg->lpBuffers);
break;
default:
break;
}
// Already released for AcceptEx operations
if ( LSP_OP_ACCEPTEX != olp->Operation )
ReleaseSocketLock( SocketContext );
}
DerefSocketContext( SocketContext, &Error );
if ( NULL != olp->lpCallerCompletionRoutine )
{
//
// If the app supplied a completion routine, queue it up for completion
//
olp->lpCallerOverlapped->Internal = (ULONG_PTR)olp->lpCallerCompletionRoutine;
if ( olp->lpCallerOverlapped->Internal == (ULONG_PTR)0x103)
DebugBreak();
ret = gMainUpCallTable.lpWPUQueueApc(
&olp->CallerThreadId,
CallUserApcProc,
(DWORD_PTR) olp->lpCallerOverlapped,
&Error
);
if ( SOCKET_ERROR == ret )
{
dbgprint("IntermediateCompletionRoutine: WPUQueueApc() failed: %d", Error);
}
}
else
{
//
// Otherwise we signal that the op has completed
//
ret = WPUCompleteOverlappedRequest(
olp->CallerSocket,
olp->lpCallerOverlapped,
dwError,
cbTransferred,
&Error
);
if ( SOCKET_ERROR == ret )
{
dbgprint("WPUCompleteOverlappedRequest failed: %d (provider socket 0x%x)",
Error, olp->CallerSocket );
}
}
if ( ( NULL != olp ) && ( TRUE == olp->CloseThread ) )
{
ret = gMainUpCallTable.lpWPUCloseThread( &olp->CallerThreadId, &Error );
if ( SOCKET_ERROR == ret )
{
dbgprint("WPUCloseThread failed: %d", Error );
}
olp->CloseThread = FALSE;
}
//
// Cleanup the accounting on the socket
//
CheckForContextCleanup( olp );
cleanup:
if ( NULL != olp )
FreeOverlappedStructure( olp );
return;
}
//
// Function: CallerUserApcProc
//
// Description:
// This function completes an overlapped request that supplied an APC.
//
VOID CALLBACK
CallUserApcProc(
ULONG_PTR Context
)
{
LPOVERLAPPED lpOverlapped;
LPWSAOVERLAPPED_COMPLETION_ROUTINE UserCompletionRoutine;
lpOverlapped = (LPOVERLAPPED) Context;
UserCompletionRoutine = (LPWSAOVERLAPPED_COMPLETION_ROUTINE)lpOverlapped->Internal;
lpOverlapped->Internal = 0; // Remove the WSS_OPERATION_IN_PROGRESS value
UserCompletionRoutine(
(DWORD)lpOverlapped->OffsetHigh,
(DWORD)lpOverlapped->InternalHigh,
lpOverlapped,
(DWORD)lpOverlapped->Offset
);
return;
}
//
// Function: CheckForContextCleanup
//
// Description:
// After an overlapped operation completes (or from WSPCloseSocket) we
// need to see if the socket has been marked for closure and make sure
// no one else is referencing it. If so then the associated structures
// can be freed and the socket closed.
//
void
CheckForContextCleanup(
WSAOVERLAPPEDPLUS *ol
)
{
SOCK_INFO *SocketContext = NULL;
int Error,
ret;
SocketContext = FindAndRefSocketContext( ol->CallerSocket, &Error );
if ( NULL == SocketContext )
{
dbgprint( "CheckForContextCleanup: FindAndRefSocketContext failed!" );
return;
}
EnterCriticalSection( &gCriticalSection );
AcquireSocketLock( ol->SockInfo );
(ol->SockInfo->dwOutstandingAsync)--;
if ( ( TRUE == ol->SockInfo->bClosing ) &&
( 0 == ol->SockInfo->dwOutstandingAsync ) &&
( 1 == ol->SockInfo->RefCount )
)
{
//
// If the calling app closed the socket while there were still outstanding
// async operations then all the outstanding operations have completed so
// we can close the apps socket handle.
//
ret = gMainUpCallTable.lpWPUCloseSocketHandle(
ol->CallerSocket,
&ol->Error
);
if ( SOCKET_ERROR == ret )
{
dbgprint("CheckForContextClenaup: WPUCloseSocketHandle() failed: %d", ol->Error);
}
ol->SockInfo->LayeredSocket = INVALID_SOCKET;
RemoveSocketInfo( ol->SockInfo->Provider, ol->SockInfo );
dbgprint("CheckForContxtCleanup: Closing socket %d Bytes Sent [%lu] Bytes Recv [%lu]",
ol->CallerSocket, ol->SockInfo->BytesSent, ol->SockInfo->BytesRecv);
ReleaseSocketLock( ol->SockInfo );
ol->SockInfo = NULL;
FreeSockInfo( SocketContext );
goto cleanup;
}
ReleaseSocketLock( ol->SockInfo );
DerefSocketContext( SocketContext, &Error );
cleanup:
LeaveCriticalSection( &gCriticalSection );
return;
}
//
// Function: PrepareOverlappedOperation
//
// Description:
// This function simply allocates and initializes the common fields of a
// WSAOVERLAPPEDPLUS structure. These fields are required in order to handle
// overlapped IO correctly.
//
WSAOVERLAPPEDPLUS *
PrepareOverlappedOperation(
SOCK_INFO *SocketContext,
LspOperation operation,
WSABUF *lpBuffers,
DWORD dwBufferCount,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadId,
int *lpErrno
)
{
WSAOVERLAPPEDPLUS *ProviderOverlapped = NULL;
int ret = SOCKET_ERROR,
err = 0;
// Allocate a WSAOVERLAPPEDPLUS structure
ProviderOverlapped = AllocOverlappedStructure( SocketContext );
if ( NULL == ProviderOverlapped )
{
*lpErrno = WSAENOBUFS;
goto cleanup;
}
__try
{
// Check for an event and reset if. Also, copy the offsets from the upper
// layer's WSAOVERLAPPED structure.
if ( ( NULL == lpCompletionRoutine ) &&
( NULL != lpOverlapped->hEvent ) )
{
ULONG_PTR ptr = 1;
ret = ResetEvent( (HANDLE) ( (ULONG_PTR) lpOverlapped->hEvent & ~ptr ) );
if (ret == 0)
{
*lpErrno = ERROR_INVALID_HANDLE;
ret = SOCKET_ERROR;
goto cleanup;
}
}
// Copy any offset information from the caller's overlapped to ours
CopyOffset( &ProviderOverlapped->ProviderOverlapped, lpOverlapped );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
*lpErrno = WSAEFAULT;
goto cleanup;
}
if ( NULL != lpThreadId )
{
ProviderOverlapped->CallerThreadId = *lpThreadId;
}
else
{
// If thread info wasn't passed to us, we need to open the thread context
ret = gMainUpCallTable.lpWPUOpenCurrentThread( &ProviderOverlapped->CallerThreadId, &err );
if ( SOCKET_ERROR == ret )
{
dbgprint("WPUOpenCurrentThread failed: %d", err);
}
else
{
// Need to remember for later to close the context since we opened it
ProviderOverlapped->CloseThread = TRUE;
}
}
// Fill in the remaining fields
ProviderOverlapped->Provider = SocketContext->Provider;
ProviderOverlapped->lpCallerOverlapped = lpOverlapped;
ProviderOverlapped->lpCallerCompletionRoutine = lpCompletionRoutine;
ProviderOverlapped->SockInfo = SocketContext;
ProviderOverlapped->CallerSocket = SocketContext->LayeredSocket;
ProviderOverlapped->ProviderSocket = SocketContext->ProviderSocket;
ProviderOverlapped->Error = NO_ERROR;
ProviderOverlapped->Operation = operation;
if ( ( NULL != lpBuffers) && ( dwBufferCount ) )
{
// Depending on the underlying operation, copy some parameters specific to it
switch ( operation )
{
case LSP_OP_RECV:
ProviderOverlapped->RecvArgs.dwBufferCount = dwBufferCount;
ProviderOverlapped->RecvArgs.lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if ( NULL == ProviderOverlapped->RecvArgs.lpBuffers )
goto cleanup;
break;
case LSP_OP_RECVFROM:
ProviderOverlapped->RecvFromArgs.dwBufferCount = dwBufferCount;
ProviderOverlapped->RecvFromArgs.lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if ( NULL == ProviderOverlapped->RecvFromArgs.lpBuffers )
goto cleanup;
break;
case LSP_OP_SEND:
ProviderOverlapped->SendArgs.dwBufferCount = dwBufferCount;
ProviderOverlapped->SendArgs.lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if ( NULL == ProviderOverlapped->SendArgs.lpBuffers )
goto cleanup;
break;
case LSP_OP_SENDTO:
ProviderOverlapped->SendToArgs.dwBufferCount = dwBufferCount;
ProviderOverlapped->SendToArgs.lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if ( NULL == ProviderOverlapped->SendToArgs.lpBuffers )
goto cleanup;
break;
case LSP_OP_RECVMSG:
ProviderOverlapped->RecvMsgArgs.WsaMsg.dwBufferCount = dwBufferCount;
ProviderOverlapped->RecvMsgArgs.WsaMsg.lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if (NULL == ProviderOverlapped->RecvMsgArgs.WsaMsg.lpBuffers)
goto cleanup;
break;
case LSP_OP_SENDMSG:
ProviderOverlapped->SendMsgArgs.SendMsg.lpMsg =
&ProviderOverlapped->SendMsgArgs.WsaMsg;
ProviderOverlapped->SendMsgArgs.SendMsg.lpMsg->dwBufferCount = dwBufferCount;
ProviderOverlapped->SendMsgArgs.SendMsg.lpOverlapped = &ProviderOverlapped->ProviderOverlapped;
ProviderOverlapped->SendMsgArgs.SendMsg.lpMsg->lpBuffers = CopyWSABuf(
lpBuffers,
dwBufferCount,
lpErrno
);
if (NULL == ProviderOverlapped->SendMsgArgs.SendMsg.lpMsg->lpBuffers)
goto cleanup;
break;
default:
break;
}
}
ret = NO_ERROR;
cleanup:
if ( SOCKET_ERROR == ret )
{
UndoOverlappedOperation( SocketContext, ProviderOverlapped );
ProviderOverlapped = NULL;
}
else
{
ASSERT( NO_ERROR == ret );
}
return ProviderOverlapped;
}
//
// Function: UndoOverlappedOperation
//
// Description:
// This function decrements the outstanding asynchronous count as well as
// freeing any resources associated with this structure (such as the copied
// WSABUF array for those asynchronous calls that use them).
//
void
UndoOverlappedOperation(
SOCK_INFO *SocketContext,
WSAOVERLAPPEDPLUS *ProviderOverlapped
)
{
AcquireSocketLock( SocketContext );
(SocketContext->dwOutstandingAsync)--;
ReleaseSocketLock( SocketContext );
FreeOverlappedStructure( ProviderOverlapped );
}
//
// Function: FreeOverlappedLookasideList
//
// Description:
// This routine frees all WSAOVELRAPPEDPLUS structures in the lookaside list.
// This function is called when the LSP is unloading.
//
void
FreeOverlappedLookasideList(
)
{
WSAOVERLAPPEDPLUS *olp = NULL;
LIST_ENTRY *entry = NULL;
EnterCriticalSection( &gOverlappedCS );
while ( ! IsListEmpty( &gFreeOverlappedPlus ) )
{
entry = RemoveHeadList( &gFreeOverlappedPlus );
olp = CONTAINING_RECORD( entry, WSAOVERLAPPEDPLUS, Link );
LspFree( olp );
}
LeaveCriticalSection( &gOverlappedCS );
}