windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/atq/atqsupp.cxx
2020-09-26 16:20:57 +08:00

2231 lines
61 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1994-1996 Microsoft Corporation
Module Name :
atqsupp.cxx
Abstract:
Contains internal support routines for the ATQ package
From atqnew.c
Author:
Murali R. Krishnan (MuraliK) 02-Apr-1996
Project:
Internet Server Common DLL
--*/
#include "isatq.hxx"
#include <iscaptrc.h>
DWORD AtqPoolThread( LPDWORD param );
extern PBANDWIDTH_INFO g_pBandwidthInfo;
extern DWORD IISCapTraceFlag;
extern TRACEHANDLE IISCapTraceLoggerHandle;
DWORD g_fAlwaysReuseSockets = FALSE;
/************************************************************
* Functions for ATQ_CONTEXT
************************************************************/
PATQ_CONT
I_AtqAllocContextFromCache( VOID);
VOID
I_AtqFreeContextToCache(
IN PATQ_CONT pAtqContext,
IN BOOL UnlinkContext
);
PATQ_CONT
I_AtqAllocContextFromCache( VOID)
/*++
This function attempts to allocate an ATQ context from the allocation cache.
It then initializes the state information in the ATQ context object and
returns the context on success.
Arguments:
None
Returns:
On success a valid pointer to ATQ_CONT. Otherwise NULL.
--*/
{
PATQ_CONT pAtqContext;
DBG_ASSERT( NULL != g_pachAtqContexts);
pAtqContext = (ATQ_CONTEXT * ) g_pachAtqContexts->Alloc();
if ( NULL != pAtqContext ) {
//
// Make sure everything is zeroed out so there is
// no crud from previous use.
//
memset(pAtqContext, 0, sizeof(ATQ_CONTEXT));
pAtqContext->ContextList =
&AtqActiveContextList[(++AtqGlobalContextCount %
g_dwNumContextLists)];
pAtqContext->Signature = ATQ_CONTEXT_SIGNATURE;
}
return (pAtqContext);
} // I_AtqAllocContextFromCache()
VOID
I_AtqFreeContextToCache(
IN PATQ_CONT pAtqContext
)
/*++
This function releases the given context to the allocation cache.
Arguments:
pAtqContext pointer to the ATQ_CONTEXT that is being freed.
Returns:
None
Issues:
This function also performs some other cleanup specific to AtqContexts.
--*/
{
#if 0
ATQ_PRINTF(( DBG_CONTEXT,
"[I_AtqFreeCtxtToCache] Freed up %08x\n",
pAtqContext
));
#endif
DBG_ASSERT( pAtqContext->Signature == ATQ_FREE_CONTEXT_SIGNATURE);
DBG_ASSERT( pAtqContext->lSyncTimeout ==0);
DBG_ASSERT( pAtqContext->m_nIO ==0);
DBG_ASSERT( pAtqContext->m_acFlags == 0);
DBG_ASSERT( pAtqContext->m_acState == 0);
DBG_ASSERT( pAtqContext->m_leTimeout.Flink == NULL);
DBG_ASSERT( pAtqContext->m_leTimeout.Blink == NULL);
DBG_ASSERT( pAtqContext->pvBuff == NULL);
DBG_ASSERT( pAtqContext->pEndpoint == NULL);
DBG_ASSERT( pAtqContext->hAsyncIO == NULL);
DBG_REQUIRE( g_pachAtqContexts->Free( pAtqContext));
return;
} // I_AtqFreeContextToCache
void
ATQ_CONTEXT::Print( void) const
{
DBGPRINTF(( DBG_CONTEXT,
" ATQ_CONTEXT (%08x)\n"
"\thAsyncIO = %p Signature = %08lx\n"
"\tOverlapped.Internal = %p Overlapped.Offset= %08lx\n"
"\tm_leTimeout.Flink = %p m_leTimeout.Blink= %p\n"
"\tClientContext = %p ContextList = %p\n"
"\tpfnCompletion = %p ()\n"
"\tpEndPoint = %p fAcceptExContext = %s\n"
"\tlSyncTimeout = %8d fInTimeout = %s\n"
"\tTimeOut = %08lx NextTimeout = %08lx\n"
"\tBytesSent = %d (0x%08lx)\n"
"\tpvBuff = %p JraAsyncIo = %p\n"
"\tfConnectionIndicated= %s fBlocked = %8lx\n"
"\tState = %8lx Flags = %8lx\n",
this,
hAsyncIO,
Signature,
Overlapped.Internal,
Overlapped.Offset,
m_leTimeout.Flink,
m_leTimeout.Blink,
ClientContext,
ContextList,
pfnCompletion,
pEndpoint,
(IsAcceptExRootContext() ? "TRUE" : "FALSE"),
lSyncTimeout,
(IsFlag( ACF_IN_TIMEOUT) ? "TRUE" : "FALSE"),
TimeOut,
NextTimeout,
BytesSent,
BytesSent,
pvBuff,
hJraAsyncIO,
(IsFlag( ACF_CONN_INDICATED) ? "TRUE" : "FALSE"),
IsBlocked(),
m_acState, m_acFlags
));
// Print the buffer if necessary.
return;
} // ATQ_CONTEXT::Print()
VOID
ATQ_CONTEXT::HardCloseSocket( VOID)
/*++
Description:
This socket closes the socket by forcibly calling closesocket() on
the socket. This function is used during the endpoint shutdown
stage for an atq context
Arguments:
None
Returns:
None
--*/
{
HANDLE haio = (HANDLE )
InterlockedExchangePointer( (PVOID *)&hAsyncIO, NULL );
DBG_ASSERT( IsState( ACS_SOCK_LISTENING) ||
IsState( ACS_SOCK_CONNECTED) ||
IsState( ACS_SOCK_CLOSED) ||
IsState( ACS_SOCK_UNCONNECTED)
);
MoveState( ACS_SOCK_CLOSED);
//
// Let us do a hard close on the socket (handle).
// This should generate an IO completion which will free this
// ATQ context
//
if ( (haio != NULL) &&
(closesocket( HANDLE_TO_SOCKET(haio) ) == SOCKET_ERROR)
) {
ATQ_PRINTF(( DBG_CONTEXT,
"Warning - "
" Context=%08x closesocket failed,"
" error %d, socket = %x\n",
this,
GetLastError(),
haio ));
Print();
}
return;
} // ATQ_CONTEXT::HardCloseSocket()
VOID
ATQ_CONTEXT::InitWithDefaults(
IN ATQ_COMPLETION pfnCompletion,
IN DWORD TimeOut,
IN HANDLE hAsyncIO
)
{
DBG_ASSERT( this->Signature == ATQ_CONTEXT_SIGNATURE);
this->InitTimeoutListEntry();
this->Signature = ATQ_CONTEXT_SIGNATURE;
// start life at 1. This ref count will be freed up by AtqFreeContext()
this->m_nIO = 1;
this->pfnCompletion = pfnCompletion;
this->TimeOut = TimeOut;
this->TimeOutScanID = 0;
this->lSyncTimeout = 0;
this->hAsyncIO = hAsyncIO;
this->hJraAsyncIO = (ULONG_PTR)hAsyncIO | 0x80000000;
this->m_acState = 0;
this->m_acFlags = 0;
// Initialize pbandwidthinfo to point to global object
this->m_pBandwidthInfo = g_pBandwidthInfo;
ZeroMemory(
&this->Overlapped,
sizeof( this->Overlapped )
);
DBG_ASSERT( this->lSyncTimeout == 0);
//
// Following added for bandwidth throttling purposes
//
DBG_ASSERT( !this->IsBlocked());
this->arInfo.atqOp = AtqIoNone;
this->arInfo.lpOverlapped = NULL;
// bandwidth throttling initialization ends here.
//
// Should we force socket closure?
//
this->m_fForceClose = FALSE;
} // ATQ_CONTEXT::InitWithDefaults()
VOID
ATQ_CONTEXT::InitNonAcceptExState(
IN PVOID pClientContext
)
{
//
// Note that if we're not using AcceptEx, then we consider the client
// to have been notified externally (thus ACF_CONN_INDICATED is set).
// Also we set the next timeout to be infinite, which may be reset
// when the next IO is submitted.
//
this->NextTimeout = ATQ_INFINITE;
this->ClientContext = pClientContext;
this->pEndpoint = NULL;
this->SetFlag( ACF_CONN_INDICATED);
this->SetState( ACS_SOCK_CONNECTED);
this->ResetFlag( ACF_ACCEPTEX_ROOT_CONTEXT);
//
// Insert this into the active list - since this is a non-acceptex socket
//
DBG_ASSERT( this->ContextList != NULL);
this->ContextList->InsertIntoActiveList( &this->m_leTimeout );
return;
} // ATQ_CONTEXT::InitNonAcceptExState()
VOID
ATQ_CONTEXT::InitAcceptExState(
IN DWORD NextTimeOut
)
{
this->NextTimeout = NextTimeOut;
this->ClientContext = NULL;
this->lSyncTimeout = 0;
this->ResetFlag( ACF_CONN_INDICATED);
this->SetState( ACS_SOCK_LISTENING);
//
// Add it to the pending accept ex list
//
DBG_ASSERT( this->ContextList != NULL);
this->ContextList->InsertIntoPendingList( &this->m_leTimeout);
return;
} // ATQ_CONTEXT::InitAcceptExState()
BOOL
ATQ_CONTEXT::PrepareAcceptExContext(
PATQ_ENDPOINT pEndpoint
)
/*++
Routine Description:
Initializes the state for completely initializing the state and
hence prepares the context for AcceptEx
It expects the caller to send a AtqContext with certain characteristics
1) this is not NULL
2) this->pvBuff has valid values
In the case of failure, caller should call
pAtqContext->CleanupAndRelese() to free the memory associated with
this object.
Arguments:
pEndpoint - pointer to endpoint object for this context
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
The caller should free the object on a failure.
--*/
{
DBG_ASSERT( g_fUseAcceptEx); // only support AcceptEx() cases
DBG_ASSERT( pEndpoint != NULL);
DBG_ASSERT( this != NULL);
DBG_ASSERT( this->pvBuff != NULL);
//
// Make sure that we are adding a AcceptEx() version of AtqContext
//
DBG_ASSERT( pEndpoint->ConnectExCompletion != NULL);
DBG_ASSERT( pEndpoint->UseAcceptEx);
//
// Fill out the context. We set NextTimeout to INFINITE
// so the timeout thread will ignore this entry until an IO
// request is made unless this is an AcceptEx socket, that means
// we're about to submit the IO.
//
this->
InitWithDefaults(
pEndpoint->IoCompletion,
pEndpoint->AcceptExTimeout, // canonical Timeout
this->hAsyncIO
);
//
// TBD: What is the circumstance in which this->pEndpoint!= NULL?
//
if ( this->pEndpoint == NULL ) {
pEndpoint->Reference();
this->pEndpoint = pEndpoint;
}
this->ResetFlag( ACF_ACCEPTEX_ROOT_CONTEXT );
this->InitAcceptExState( AtqGetCurrentTick() + TimeOut);
DBG_ASSERT( this->pvBuff != NULL);
//
// Initialize capacity planning trace info
//
m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_START;
m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
m_CapTraceInfo.IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
m_CapTraceInfo.IISCapTraceHeader.TraceContext.Length = sizeof(ULONGLONG);
// Will get over-written
m_CapTraceInfo.IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG)
(&m_CapTraceInfo.IISCapTraceHeader.TraceContext.DataPtr);
return (TRUE);
} // ATQ_CONTEXT::PrepareAcceptExContext()
VOID
ATQ_CONTEXT::CleanupAndRelease( VOID)
/*++
Routine Description:
This function does the cleanup of the ATQ context. It does not
attempt to do any reuse of the atq context. After cleanup
the context is freed to the ATQ pool. Supplied context
is not valid after calling this function.
Arguments:
None
Returns:
None
--*/
{
DBG_ASSERT( this->m_nIO == 0);
//
// Cleanup and free the ATQ Context entirely
//
if ( this->hAsyncIO != NULL ) {
// It is too dangerous to assume that the handle is a socket!
// But we will do that for fast-pathing IIS operations.
HANDLE hTmp =
(HANDLE)InterlockedExchangePointer( (PVOID *)&this->hAsyncIO,
NULL );
SOCKET hIO = HANDLE_TO_SOCKET(hTmp);
if ( hIO != NULL &&
(closesocket( hIO ) == SOCKET_ERROR ) ) {
ATQ_PRINTF(( DBG_CONTEXT,
"ATQ_CONTEXT(%08x)::CleanupAndRelease() : Warning"
" - Context=%08x, "
" closesocket failed, error %d, socket = %x\n",
this,
GetLastError(),
hIO ));
this->Print();
}
}
DBG_ASSERT( this->hAsyncIO == NULL);
if ( this->pvBuff != NULL ) {
LocalFree( this->pvBuff );
this->pvBuff = NULL;
}
//
// Unlink from the list
//
DBG_ASSERT( this->ContextList != NULL);
// NYI: Can I avoid this comparison?
//
// Check if this context is part of a timeout list.
// If it is then remove it from the list
// Only during shutdown code path, we will see trouble here.
//
if ( this->m_leTimeout.Flink != NULL ) {
this->ContextList->RemoveFromList( &this->m_leTimeout);
}
//
// Deref the listen info if this context is associated with one
//
if ( this->pEndpoint != NULL ) {
this->pEndpoint->Dereference();
this->pEndpoint = NULL;
}
this->Signature = ATQ_FREE_CONTEXT_SIGNATURE;
this->lSyncTimeout = 0;
this->m_acState = 0;
this->m_acFlags = 0;
I_AtqFreeContextToCache( this);
return;
} // ATQ_CONTEXT::CleanupAndRelease()
inline VOID
DBG_PRINT_ATQ_SPUDCONTEXT( IN PATQ_CONT pAtqContext,
IN PSPUD_REQ_CONTEXT reqContext)
{
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqPoolThread] pAtqContext = %08lx\n"
"[AtqPoolThread] IoStatus1.Status = %08lx\n"
"[AtqPoolThread] IoStatus1.Information = %08lx\n"
"[AtqPoolThread] IoStatus2.Status = %08lx\n"
"[AtqPoolThread] IoStatus2.Information = %08lx\n"
,
pAtqContext,
reqContext->IoStatus1.Status,
reqContext->IoStatus1.Information,
reqContext->IoStatus2.Status,
reqContext->IoStatus2.Information
));
return;
} // DBG_PRINT_ATQ_SPUDCONTEXT()
VOID
AtqpUpdateBandwidth( IN PATQ_CONT pAtqContext,
IN DWORD cbWritten)
{
PBANDWIDTH_INFO pBandwidthInfo = pAtqContext->m_pBandwidthInfo;
DBG_ASSERT( pBandwidthInfo != NULL );
DBG_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
// add the bandwidth info to active list if necessary
pBandwidthInfo->AddToActiveList();
//this will have problems when we use XmitFile for large files.
pBandwidthInfo->UpdateBytesXfered( pAtqContext, cbWritten );
} // AtqpUpdateBandwidth()
VOID
AtqpCallOplockCompletion( IN PATQ_CONT pAtqContext,
IN DWORD cbWritten)
{
ATQ_OPLOCK_COMPLETION pfnOplockCompletion;
PVOID OplockContext;
POPLOCK_INFO pOplock;
IF_DEBUG( SPUD) {
DBGPRINTF(( DBG_CONTEXT,
"CallOplockCompletion on OpLockInfo=%08x.cbWritten = %d\n",
(POPLOCK_INFO ) pAtqContext, cbWritten));
}
//
// The ATQ context object received is a fake one. We actually get
// back POPLOCK_INFO object that is used to extract the callback
// function & context for the callback
//
pOplock = (POPLOCK_INFO)pAtqContext;
pfnOplockCompletion = (ATQ_OPLOCK_COMPLETION)pOplock->pfnOplockCompletion;
OplockContext = (PVOID)pOplock->Context;
LocalFree(pOplock);
(*pfnOplockCompletion)(OplockContext, (DWORD)cbWritten);
return;
} // AtqpCallOplockCompletion()
VOID
AtqpProcessContext( IN PATQ_CONT pAtqContext,
IN DWORD cbWritten,
IN LPOVERLAPPED lpo,
IN BOOL fRet)
{
BOOL fDriverCall = FALSE;
BOOL fRecvCalled = FALSE;
PSPUD_REQ_CONTEXT reqContext;
DWORD dwError;
DBG_ASSERT( pAtqContext != NULL);
//
// Check to see if this is a completion request from the
// NTS kernel driver.
//
if ( lpo == NULL ) {
if ( cbWritten == 0xffffffff ) {
//
// One of the SPUD's IO completion. Handle it appropriately.
//
reqContext = (PSPUD_REQ_CONTEXT)pAtqContext;
pAtqContext = CONTAINING_RECORD( reqContext, ATQ_CONTEXT,
spudContext );
IF_DEBUG( SPUD) {
DBG_PRINT_ATQ_SPUDCONTEXT( pAtqContext, reqContext);
}
#if CC_REF_TRACKING
//
// ATQ notification trace
//
// Notify client context of all non-oplock notification.
// This is for debugging purpose only.
//
// Code 0xfcfcfcfc indicates a SPUD I/O Completion
//
pAtqContext->NotifyIOCompletion( (ULONG_PTR)pAtqContext, reqContext->IoStatus1.Status, 0xfcfcfcfc );
#endif
cbWritten = (DWORD)reqContext->IoStatus1.Information;
fRet = (reqContext->IoStatus1.Status == STATUS_SUCCESS);
SetLastError(g_pfnRtlNtStatusToDosError(reqContext->IoStatus1.Status));
lpo = &pAtqContext->Overlapped;
//
// If the TransmitFile fails then the receive is not issued.
//
if ( fRet ) {
fDriverCall = TRUE;
} else {
DBG_ASSERT( fDriverCall == FALSE);
pAtqContext->ResetFlag( ACF_RECV_ISSUED);
}
} else {
//
// An Oplock notification - handle it via oplock path.
//
AtqpCallOplockCompletion( pAtqContext, cbWritten);
return;
}
}
dwError = (fRet) ? NO_ERROR: GetLastError();
//
// If this is an AcceptEx listen socket atq completion, then the
// client Atq context we really want is keyed from the overlapped
// structure that is stored in the client's Atq context.
//
if ( pAtqContext->IsAcceptExRootContext() ) {
pAtqContext = CONTAINING_RECORD( lpo, ATQ_CONTEXT, Overlapped );
}
#if CC_REF_TRACKING
//
// ATQ notification trace
//
// Notify client context of all non-oplock notification.
// This is for debugging purpose only.
//
pAtqContext->NotifyIOCompletion( cbWritten, (fRet) ? NO_ERROR: GetLastError(), 0xfefefefe );
#endif
DBG_CODE(
if ( ATQ_CONTEXT_SIGNATURE != pAtqContext->Signature) {
pAtqContext->Print();
DBG_ASSERT( FALSE);
});
//
// m_nIO also acts as the reference count for the atq contexts
// So, increment the count now, so that there is no other thread
// that will free up this ATQ context accidentally.
//
InterlockedIncrement( &pAtqContext->m_nIO);
//
// Busy wait for timeout processing to complete!
// This is ugly :( A fix in time for IIS 2.0/Catapult 1.0 release
//
InterlockedIncrement( &pAtqContext->lSyncTimeout);
while ( pAtqContext->IsFlag( ACF_IN_TIMEOUT)) {
AcIncrement( CacAtqWaitsForTimeout);
Sleep( ATQ_WAIT_FOR_TIMEOUT_PROCESSING);
};
//
// We need to make sure the timeout thread doesn't time this
// request out so reset the timeout value
//
InterlockedExchange( (LPLONG )&pAtqContext->NextTimeout,
(LONG ) ATQ_INFINITE);
//
// Update Bandwidth information on successful completion, if needed
//
if ( BANDWIDTH_INFO::GlobalEnabled() && fRet && cbWritten > 0)
{
AtqpUpdateBandwidth( pAtqContext, cbWritten);
}
//
// Since the IO completion means that one of the async operation finished
// decrement our internal ref count appropriately to balance the addition
// when the IO operation was submitted.
//
InterlockedDecrement( &pAtqContext->m_nIO);
//
// Is this a connection indication?
//
if ( !pAtqContext->IsFlag( ACF_CONN_INDICATED) ) {
PATQ_ENDPOINT pEndpoint = pAtqContext->pEndpoint;
if ( NULL == pEndpoint) {
pAtqContext->Print();
OutputDebugString( "Found an ATQ context with bad Endpoint\n");
DBG_ASSERT( FALSE);
DBG_REQUIRE( InterlockedDecrement( &pAtqContext->lSyncTimeout) == 0);
InterlockedDecrement( &pAtqContext->m_nIO); // balance entry count
return;
}
DBG_ASSERT( pEndpoint != NULL );
//
// If the endpoint isn't active it may not be safe to call
// the connection completion function. Setting an error
// state will close the connection below.
//
if ( fRet && !IS_BLOCK_ACTIVE( pEndpoint ) ) {
fRet = FALSE;
dwError = ERROR_OPERATION_ABORTED;
}
//
// Indicate this socket is in use
//
InterlockedDecrement( &pEndpoint->nSocketsAvail );
//
// If we're running low on sockets, add some more now
//
if ( pEndpoint->nSocketsAvail <
(LONG )(pEndpoint->nAcceptExOutstanding >> 2) ) {
AcIncrement( CacAtqPrepareContexts);
(VOID ) I_AtqPrepareAcceptExSockets(pEndpoint,
pEndpoint->nAcceptExOutstanding
);
}
//
// If an error occurred on this completion,
// shutdown the socket
//
if ( !fRet ) {
IF_DEBUG( ERROR) {
if ( dwError != ERROR_OPERATION_ABORTED &&
dwError != ERROR_NETNAME_DELETED ) {
ATQ_PRINTF(( DBG_CONTEXT,
" Free Context(%08x, EP=%08x) to cache. "
"Err=%d, sock=%08x\n",
pAtqContext, pEndpoint,
dwError,
pAtqContext->hAsyncIO));
}
}
DBG_REQUIRE( InterlockedDecrement( &pAtqContext->lSyncTimeout) == 0);
InterlockedDecrement( &pAtqContext->m_nIO); // balance entry count
// balance original count
InterlockedDecrement( &pAtqContext->m_nIO);
// Free up the atq context without Reuse
pAtqContext->CleanupAndRelease();
return;
}
//
// Shutdown may close the socket from underneath us so don't
// assert, just warn.
//
if ( !pAtqContext->IsState( ACS_SOCK_LISTENING) ) {
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqPoolThread] Warning-Socket state not listening\n"
));
DBG_CODE( pAtqContext->Print());
}
pAtqContext->MoveState( ACS_SOCK_CONNECTED);
//
// Remove the context from the pending list and put
// it on the active list
//
DBG_ASSERT( pAtqContext->ContextList != NULL);
pAtqContext->ContextList->MoveToActiveList( &pAtqContext->m_leTimeout);
//
// Set the connection indicated flag. After we return from
// the connection completion routine we assume it's
// safe to call the IO completion routine
// (or the connection indication routine should do cleanup
// and never issue an IO request). This is primarily for
// the timeout thread.
//
pAtqContext->ConnectionCompletion( cbWritten, lpo);
} else {
//
// Not a connection completion indication. I/O completion.
//
//
// If an error occurred on a TransmitFile (or other IO),
// set the state to connected so the socket will get
// closed on cleanup
//
if ( !fRet &&
pAtqContext->IsState( ACS_SOCK_UNCONNECTED)
){
pAtqContext->MoveState( ACS_SOCK_CONNECTED);
}
#if 0
if (fDriverCall) {
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqPoolThread] pfnCompletion1(%08lx)\n",
pAtqContext ));
}
#endif
pAtqContext->IOCompletion( cbWritten, dwError, lpo);
if (fDriverCall) {
pAtqContext->ResetFlag( ACF_RECV_ISSUED);
fRet = (reqContext->IoStatus2.Status == STATUS_SUCCESS);
SetLastError( g_pfnRtlNtStatusToDosError(
reqContext->IoStatus2.Status));
//
// If an error occurred on a TransmitFile (or other IO),
// set the state to connected so the socket will get
// closed on cleanup
//
if ( !fRet &&
pAtqContext->IsState( ACS_SOCK_UNCONNECTED) ) {
pAtqContext->MoveState( ACS_SOCK_CONNECTED);
}
#if CC_REF_TRACKING
//
// ATQ notification trace
//
// Notify client context of status after 1st notification
// This is for debugging purpose only.
//
// Code 0xfafafafa means we're processing a recv that
// SPUD combined with another notification
//
// pAtqContext->NotifyIOCompletion( pAtqContext->m_acFlags, reqContext->IoStatus1.Status, 0xfafafafa );
#endif
if ( pAtqContext->IsFlag( ACF_RECV_CALLED ) ) {
fRecvCalled = TRUE;
pAtqContext->ResetFlag( ACF_RECV_CALLED);
}
if ((reqContext->IoStatus1.Status == STATUS_SUCCESS) &&
(pAtqContext->ClientContext != NULL) &&
fRecvCalled ) {
#if CC_REF_TRACKING
//
// ATQ notification trace
//
// Notify client context of all non-oplock notification.
// This is for debugging purpose only.
//
// Code 0xfdfdfdfd means we're processing a recv that
// SPUD combined with another notification
//
pAtqContext->NotifyIOCompletion( cbWritten, (fRet) ? NO_ERROR: GetLastError(), 0xfdfdfdfd );
#endif
IF_DEBUG( SPUD) {
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqPoolThread] pfnCompletion2(%08lx)\n",
pAtqContext ));
};
pAtqContext->IOCompletion(
(DWORD)reqContext->IoStatus2.Information,
(fRet) ? NO_ERROR : GetLastError(),
lpo
);
}
}
}
DBG_ASSERT( pAtqContext->lSyncTimeout > 0);
InterlockedDecrement( &pAtqContext->lSyncTimeout);
//
// We do an interlocked decrement on m_nIO to sync up state
// so that the context is not prematurely deleted.
//
if ( InterlockedDecrement( &pAtqContext->m_nIO) == 0) {
//
// The number of outstanding ref holders is ZERO.
// Free up this ATQ context.
//
// We really do not free up the context - but try to reuse
// it if possible
//
// free the atq context now or reuse if possible.
AtqpReuseOrFreeContext( pAtqContext,
(pAtqContext->
IsFlag( ACF_REUSE_CONTEXT) != 0)
);
}
return;
} // AtqpProcessContext()
DWORD
AtqPoolThread(
LPDWORD param
)
/*++
Routine Description:
This is the pool thread wait and dispatch routine
Arguments:
param : unused.
Return Value:
Thread return value (ignored)
--*/
{
PATQ_CONT pAtqContext = NULL;
BOOL fRet;
LPOVERLAPPED lpo;
DWORD cbWritten;
DWORD returnValue;
DWORD availThreads;
for(;;) {
pAtqContext = NULL;
InterlockedIncrement( &g_cAvailableThreads );
fRet = g_pfnGetQueuedCompletionStatus( g_hCompPort,
&cbWritten,
(PULONG_PTR)&pAtqContext,
&lpo,
g_msThreadTimeout );
availThreads = InterlockedDecrement( &g_cAvailableThreads );
if ( fRet || lpo ) {
if ( pAtqContext == NULL) {
if ( g_fShutdown ) {
//
// This is our signal to exit.
//
returnValue = NO_ERROR;
break;
}
OutputDebugString( "A null context received\n");
continue; // some error in the context has occured.
}
//
// Make sure we're not running out of threads
//
if ( availThreads == 0 ) {
//
// Make sure there are pool threads to service the request
//
(VOID)I_AtqCheckThreadStatus();
}
if ( (ULONG_PTR) param == ATQ_DEBUG_THREAD )
{
//
// If this is a DEBUG thread, we are only concerned with new
// connections. Anything else we ignore.
//
if ( pAtqContext->IsFlag( ACF_CONN_INDICATED ) )
{
continue;
}
}
//
// Capacity planning log
//
if (IISCapTraceFlag && pAtqContext)
{
PIIS_CAP_TRACE_INFO pCapTraceInfo = pAtqContext->GetCapTraceInfo();
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_START;
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
pCapTraceInfo->IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG) &pAtqContext;
pCapTraceInfo->IISCapTraceHeader.TraceContext.Length = sizeof(ULONG_PTR);
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
if ( ERROR_INVALID_HANDLE == TraceEvent ( IISCapTraceLoggerHandle,
(PEVENT_TRACE_HEADER) pCapTraceInfo))
{
IISCapTraceFlag = FALSE;
}
}
AtqpProcessContext( pAtqContext, cbWritten, lpo, fRet);
//
// Capacity planning log
//
if (IISCapTraceFlag && pAtqContext)
{
PIIS_CAP_TRACE_INFO pCapTraceInfo = pAtqContext->GetCapTraceInfo();
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Class.Type = EVENT_TRACE_TYPE_END;
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Size = sizeof (IIS_CAP_TRACE_HEADER);
pCapTraceInfo->IISCapTraceHeader.TraceContext.DataPtr = (ULONGLONG) &pAtqContext;
pCapTraceInfo->IISCapTraceHeader.TraceContext.Length = sizeof(ULONG_PTR);
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Guid = IISCapTraceGuid;
pCapTraceInfo->IISCapTraceHeader.TraceHeader.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
if ( ERROR_INVALID_HANDLE == TraceEvent ( IISCapTraceLoggerHandle,
(PEVENT_TRACE_HEADER) pCapTraceInfo))
{
IISCapTraceFlag = FALSE;
}
}
} else {
//
// don't kill the initial thread - any thread that doesn't have
// pending I/Os go ahead and allow to die
//
if ( ((ULONG_PTR)param == ATQ_INITIAL_THREAD) && !g_fShutdown ) {
continue;
}
if ( !g_fShutdown && GetLastError() == WAIT_TIMEOUT ) {
NTSTATUS status;
ULONG flag;
status = NtQueryInformationThread(
NtCurrentThread(),
ThreadIsIoPending,
&flag,
sizeof(flag),
NULL );
IF_DEBUG( TIMEOUT ) {
ATQ_PRINTF(( DBG_CONTEXT,
"[ATQ Pool Thread] NtQueryInformationThread() returned 0x%08x, flag = %d\n",
status,
flag ));
}
if ( NT_SUCCESS( status ) && flag ) {
//
// There are pending I/Os on this thread so don't exit
//
continue;
}
}
IF_DEBUG( TIMEOUT ) {
ATQ_PRINTF(( DBG_CONTEXT,
"[ATQ Pool Thread] Exiting thread 0x%x\n",
GetCurrentThread() ));
}
//
// An error occurred. Either the thread timed out, the handle
// is going away or something bad happened. Let the thread exit.
//
returnValue = GetLastError();
break;
}
} // for
if ( NULL != g_pfnExitThreadCallback) {
//
// Client wishes to be told when ATQ threads terminate.
//
g_pfnExitThreadCallback();
}
if ( InterlockedDecrement( &g_cThreads ) == 0 ) {
//
// Wake up ATQTerminate()
//
IF_DEBUG( ERROR) {
ATQ_PRINTF(( DBG_CONTEXT,
"AtqPoolThread() - setting shutdown event %08x."
" g_cThreads = %d\n",
g_hShutdownEvent, g_cThreads
));
}
SetEvent( g_hShutdownEvent );
}
return returnValue;
} // AtqPoolThread
BOOL
I_AtqCheckThreadStatus(
PVOID Context
)
/*++
Routine Description:
This routine makes sure there is at least one thread in
the thread pool. We're fast and loose so a couple of extra
threads may be created.
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/
{
BOOL fRet = TRUE;
BOOL fIsDebugThread = ( (ULONG_PTR) Context == ( ATQ_DEBUG_THREAD ) );
//
// If no threads are available, kick a new one off up to the limit
//
// WE NEED TO CHANGE THE CONDITIONS FOR STARTING ANOTHER THREAD
// IT SHOULD NOT BE VERY EASY TO START A THREAD ....
//
if ( ( (g_cAvailableThreads == 0) &&
(g_cThreads < g_cMaxThreads) &&
(g_cThreads < g_cMaxThreadLimit) ) ||
fIsDebugThread )
{
HANDLE hThread;
DWORD dwThreadID;
if ( !fIsDebugThread )
{
InterlockedIncrement( &g_cThreads );
}
hThread = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)AtqPoolThread,
Context,
0,
&dwThreadID );
if ( hThread ) {
CloseHandle( hThread ); // Free system resources
} else if ( !fIsDebugThread ) {
//
// We fail if there are no threads running
//
if ( InterlockedDecrement( &g_cThreads ) == 0) {
ATQ_PRINTF(( DBG_CONTEXT,
"AtqCheckThread: Cannot create ATQ threads\n"));
fRet = FALSE;
}
}
}
return fRet;
} // I_AtqCheckThreadStatus()
/************************************************************
* Functions to Add/Delete Atq Contexts
************************************************************/
BOOL
I_AtqAddAsyncHandle(
IN OUT PATQ_CONT * ppAtqContext,
IN PATQ_ENDPOINT pEndpoint,
PVOID ClientContext,
ATQ_COMPLETION pfnCompletion,
DWORD TimeOut,
HANDLE hAsyncIO
)
/*++
Description:
This functio adds creates a new NON-AcceptEx() based Atq Context,
and includes it in proper lists fo ATQ Context management.
Note:
The client should call this after the IO handle is openned
and before the first IO request is made
Even in the case of failure, client should call AtqFreeContext() and
free the memory associated with this object.
--*/
{
BOOL fReturn = TRUE;
DBG_ASSERT( ppAtqContext != NULL);
DBG_ASSERT( ClientContext != NULL);
*ppAtqContext = NULL; // initialize
if ( g_fShutdown) {
SetLastError( ERROR_NOT_READY);
return (FALSE);
} else {
PATQ_CONT pAtqContext;
//
// Note we take and release the lock here as we're
// optimizing for the reuseable context case
//
pAtqContext = I_AtqAllocContextFromCache();
if ( pAtqContext == NULL) {
return (FALSE);
}
//
// Fill out the context. We set NextTimeout to INFINITE
// so the timeout thread will ignore this entry until an IO
// request is made unless this is an AcceptEx socket, that means
// we're about to submit the IO.
//
pAtqContext->InitWithDefaults(pfnCompletion,
CanonTimeout( TimeOut ), hAsyncIO);
//
// These data members are used if we're doing AcceptEx processing
//
pAtqContext->SetAcceptExBuffer( NULL);
pAtqContext->InitNonAcceptExState(ClientContext);
//
// If an endpoint is provided, reference it
//
if ( pEndpoint != NULL ) {
pEndpoint->Reference();
pAtqContext->pEndpoint = pEndpoint;
}
*ppAtqContext = pAtqContext;
}
return (TRUE);
} // I_AtqAddAsyncHandle()
BOOL
I_AtqAddListenEndpointToPort(
IN OUT PATQ_CONT * ppAtqContext,
IN PATQ_ENDPOINT pEndpoint
)
/*++
Description:
This function creates a new AtqContext for the given ListenSocket.
It uses the listen socket as the AcceptEx() socket too for adding
the atq context to the completion port.
It assumes
TimeOut to be INFINITE, with no Endpoint structure.
Arguments:
ppAtqContext - pointer to location that will contain the atq context
on successful return.
pEndpoint - pointer to the endpoint.
Returns:
TRUE on success
FALSE if there is a failure.
Note:
The caller should free the *ppAtqContext if there is a failure.
--*/
{
BOOL fReturn = TRUE;
PATQ_CONT pAtqContext;
DBG_ASSERT( g_fUseAcceptEx); // only support AcceptEx() cases
*ppAtqContext = NULL; // initialize
if ( g_fShutdown) {
SetLastError( ERROR_NOT_READY);
return (FALSE);
} else {
//
// Note we take and release the lock here as we're
// optimizing for the reuseable context case
//
pAtqContext = I_AtqAllocContextFromCache();
if ( pAtqContext == NULL) {
return (FALSE);
}
//
// Fill out the context.
// We set the TimeOut for this object to be ATQ_INFINITE,
// since we do not want any interference from the Timeout loop.
//
pAtqContext->InitWithDefaults(
pEndpoint->IoCompletion,
ATQ_INFINITE,
SOCKET_TO_HANDLE(pEndpoint->ListenSocket)
);
//
// These data members are used if we're doing AcceptEx processing
//
pAtqContext->SetAcceptExBuffer( NULL);
//
// Among AcceptEx ATQ Contexts,
// only the listen ATQ context will have the Endpoint field as NULL
//
pAtqContext->pEndpoint = NULL;
pAtqContext->SetFlag( ACF_ACCEPTEX_ROOT_CONTEXT );
//
// We set NextTimeout to INFINITE
// so the timeout thread will ignore this entry until an IO
// request is made unless this is an AcceptEx socket, that means
// we're about to submit the IO.
DBG_ASSERT( g_fUseAcceptEx && pEndpoint->ConnectExCompletion != NULL);
pAtqContext->InitAcceptExState( ATQ_INFINITE);
*ppAtqContext = pAtqContext;
}
fReturn = I_AddAtqContextToPort( pAtqContext);
return (fReturn);
} // I_AtqAddListenEndpointToPort()
BOOL
I_AtqAddAcceptExSocket(
IN PATQ_ENDPOINT pEndpoint,
IN PATQ_CONT pAtqContext
)
/*++
Routine Description:
Adds the AtqContext to the AcceptEx() waiters list,
after allocating a new socket, since pAtqContext->hAsyncIO = NULL.
Arguments:
pEndpoint - Information about this listenning socket
patqReusedContext - optional context to use
Return Value:
TRUE on success, FALSE on failure.
On failure the caller should free the pAtqContext
--*/
{
BOOL fAddToPort = FALSE;
BOOL fSuccess = TRUE;
DBG_ASSERT( pAtqContext != NULL);
DBG_ASSERT( g_pfnAcceptEx != NULL);
DBG_ASSERT( pAtqContext->pvBuff != NULL);
DBG_ASSERT( !TsIsWindows95() );
//
// If this listen socket isn't accepting new connections, just return
//
if ( !IS_BLOCK_ACTIVE(pEndpoint) ) {
SetLastError( ERROR_NOT_READY );
return ( FALSE);
}
//
// Use the supplied socket if any.
// Otherwise create a new socket
//
if ( pAtqContext->hAsyncIO == NULL) {
SOCKET sAcceptSocket;
#if WINSOCK11
sAcceptSocket = socket(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP
);
#else
sAcceptSocket = WSASocketW(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL, // protocol info
0, // Group ID = 0 => no constraints
(g_fUseFakeCompletionPort ?
0:
WSA_FLAG_OVERLAPPED // completion port notifications
)
);
#endif // WINSOCK11
if ( sAcceptSocket == INVALID_SOCKET ) {
fSuccess = FALSE;
sAcceptSocket = NULL;
//
// no need to unlink from any list, since we did not add it to any
//
} else {
//
// Setup the accept ex socket in the atq context.
//
pAtqContext->hAsyncIO = SOCKET_TO_HANDLE(sAcceptSocket);
pAtqContext->hJraAsyncIO = (ULONG_PTR)sAcceptSocket | 0x80000000;
fAddToPort = TRUE;
DBG_ASSERT( fSuccess);
}
}
if ( fSuccess) {
DWORD cbRecvd;
if ( g_fShutdown) {
//
// no need to unlink from any list, since we did not add it to any
//
SetLastError( ERROR_NOT_READY);
return (FALSE);
}
DBG_ASSERT( pAtqContext->hAsyncIO != NULL);
//
// 1. Call I_AtqAddAsyncHandleEx() to establish the links with
// proper AcceptEx & AtqContext processing lists.
//
// After 1, the atqcontext will be in the lists, so
// cleanup should remove the context from proper lists.
//
// 2. Add the socket to Completion Port (if new),
// i.e. if fAddToPort is true)
//
// 3. Submit the new socket to AcceptEx() so that it may be
// used for processing about the new connections.
//
// 1.
DBG_REQUIRE( pAtqContext->PrepareAcceptExContext(pEndpoint));
// increment outstanding async io operations before AcceptEx() call
InterlockedIncrement( &pAtqContext->m_nIO);
fSuccess = (// 2.
( !fAddToPort || I_AddAtqContextToPort( pAtqContext))
&&
// 3.
(
g_pfnAcceptEx( pEndpoint->ListenSocket,
HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
pAtqContext->pvBuff,
pEndpoint->InitialRecvSize,
MIN_SOCKADDR_SIZE,
MIN_SOCKADDR_SIZE,
&cbRecvd,
&pAtqContext->Overlapped )
||
(GetLastError() == ERROR_IO_PENDING)
)
);
if ( fSuccess) {
//
// We've successfully added this socket, increment the count
//
InterlockedIncrement( &pEndpoint->nSocketsAvail );
} else {
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqAddAcceptExSocket] Reusing an old context (%08x)"
" failed; error %d:%d, sAcceptSocket = %x, "
" pEndpoint = %lx, parm4 = %d, parm7 = %lx,"
" parm8 = %lx\n",
pAtqContext,
GetLastError(),
WSAGetLastError(),
pAtqContext->hAsyncIO,
pEndpoint,
pEndpoint->InitialRecvSize,
&cbRecvd,
&pAtqContext->Overlapped ));
//
// Unlink from the current list, where it was added as a result of
// step 1 above.
//
DBG_ASSERT( pAtqContext->ContextList != NULL);
// balance the increment of the async operations outstanding
DBG_REQUIRE( InterlockedDecrement( &pAtqContext->m_nIO) > 0);
DBG_ASSERT( pAtqContext->m_leTimeout.Flink != NULL);
pAtqContext->ContextList->
RemoveFromList( &pAtqContext->m_leTimeout);
//
// balance the increment done
// by pAtqContext->PrepareAcceptExContext()
//
DBG_REQUIRE( InterlockedDecrement( &pAtqContext->m_nIO) == 0);
DBG_ASSERT( !fSuccess);
//
// the caller will free the Atq context on failure
//
}
}
return ( fSuccess);
} // I_AtqAddAcceptExSocket()
VOID
AtqpReuseContext( PATQ_CONT pAtqContext)
/*++
Description:
This function attempts to reuse the ATQ context.
It first cleans up the state and then uses the function
I_AtqAddAccetpEx() socket to re-add the context to acceptex pool
Arguments:
pAtqContext - pointer to ATQ context that can be reused
Returns:
None
--*/
{
PATQ_ENDPOINT pEndpoint = pAtqContext->pEndpoint;
DBG_ASSERT( pEndpoint != NULL);
DBG_ASSERT( pEndpoint->UseAcceptEx);
//
// Complete connection has been processed prior to coming here
//
DBG_ASSERT(pAtqContext->IsFlag( ACF_CONN_INDICATED));
//
// Remove from the current active list
//
if ( pAtqContext->m_leTimeout.Flink != NULL ) {
pAtqContext->ContextList->RemoveFromList( &pAtqContext->m_leTimeout );
}
DBG_ASSERT( pAtqContext->m_leTimeout.Flink == NULL);
DBG_ASSERT( pAtqContext->m_leTimeout.Blink == NULL);
DBG_ASSERT( pEndpoint->Signature == ATQ_ENDPOINT_SIGNATURE );
//
// Either there is no socket or the socket must be in the
// unconnected state (meaning reused after TransmitFile)
//
if ( !(!pAtqContext->hAsyncIO ||
(pAtqContext->hAsyncIO &&
pAtqContext->IsState( ACS_SOCK_UNCONNECTED |
ACS_SOCK_TOBE_FREED)
)
)) {
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqReuseContext] Warning:"
" state = %08x, socket = %x (context %lx), "
" was Free called w/o close?\n",
pAtqContext->m_acState,
pAtqContext->hAsyncIO,
pAtqContext ));
DBG_ASSERT( FALSE);
}
//
// Need to make sure that the state information is cleaned up
// before re-adding the context to the list. Also reset socket options.
//
if ( pAtqContext->hAsyncIO && pAtqContext->IsFlag(ACF_TCP_NODELAY))
{
INT optValue = 0;
setsockopt( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
IPPROTO_TCP,
TCP_NODELAY,
(char *)&optValue,
sizeof(INT)
);
//
// No need to reset the flag. It will be 0'd out during the Add
//
}
if ( !I_AtqAddAcceptExSocket(pEndpoint, pAtqContext) ) {
//
// Failed to add the socket, free up the context without reuse
//
ATQ_PRINTF(( DBG_CONTEXT,
"[AtqpReuseContext] for (%08x) failed with "
" Error = %d; Now freeing the context ...\n",
pAtqContext, GetLastError()
));
DBG_ASSERT( pAtqContext->m_nIO == 0);
// free without reuse
pAtqContext->CleanupAndRelease();
}
return;
} // AtqpReuseContext()
VOID
AtqpReuseOrFreeContext(
PATQ_CONT pAtqContext,
BOOL fReuseContext
)
/*++
Routine Description:
This function does a free-up of the ATQ contexts. During the free-up
path, we also attempt to reuse the ATQ context if the fReuseContext is
set.
Arguments:
pAtqContext - pointer to the ATQ context that needs to be freedup
fReuseContext - BOOLEAN flag indicating if this context should be reused
Returns:
None
--*/
{
//
// Get this object out of the Blocked Requests List.
//
if ( pAtqContext->IsBlocked()) {
ATQ_REQUIRE( pAtqContext->m_pBandwidthInfo
->RemoveFromBlockedList( pAtqContext ));
DBG_ASSERT( !pAtqContext->IsBlocked());
}
DBG_ASSERT( pAtqContext->m_pBandwidthInfo != NULL);
pAtqContext->m_pBandwidthInfo->Dereference();
//
// Conditions for Reuse:
// 1) fReuseContext == TRUE => caller wants us to reuse context
// 2) pAtqContext->pEndpoint != NULL => valid endpoint exists
// 3) pEndpoint->UseAcceptEx => AcceptEx is enabled
// 4) pEndpoint->nSocketsAvail < nAcceptExOutstanding * 2 =>
// We do not have lots of outstanding idle sockets
// Condition (4) ensures that we do not flood the system
// with too many AcceptEx sockets as a result of some spike.
// AcceptEx sockets once added to the pool are hard to
// remove, because of various timing problems.
// Hence we want to prevent arbitrarily adding AcceptEx sockets.
//
// In condition (4) I use a fudge factor of "2", so that
// we do continue to prevent reuse of sockets prematurely.
//
if ( fReuseContext &&
(pAtqContext->pEndpoint != NULL) &&
(pAtqContext->pEndpoint->UseAcceptEx)
&&
( g_fAlwaysReuseSockets ||
((DWORD )pAtqContext->pEndpoint->nSocketsAvail <
pAtqContext->pEndpoint->nAcceptExOutstanding * 2)
)
) {
//
// Call the function to reuse context. On failure
// the AtqpReuseContext will free up the context
//
AcIncrement( CacAtqContextsReused);
AtqpReuseContext( pAtqContext);
} else {
AcIncrement( CacAtqContextsCleanedup);
pAtqContext->CleanupAndRelease();
}
return;
} // AtqpReuseOrFreeContext()
BOOL
I_AtqPrepareAcceptExSockets(
IN PATQ_ENDPOINT pEndpoint,
IN DWORD nSockets
)
/*++
Routine Description:
Prepare specified number of AcceptEx sockets for the given
ListenSocket in [pEndpoint]
Arguments:
pEndpoint - Information about this listenning socket
nSockets - number of AcceptEx() sockets to be created.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
BOOL fReturn;
DWORD cbBuffer;
DWORD i;
if ( !g_fUseAcceptEx ) {
SetLastError( ERROR_NOT_SUPPORTED );
return FALSE;
}
//
// If this listen socket isn't accepting new connections, just return
//
if ( pEndpoint->State != AtqStateActive ) {
SetLastError( ERROR_NOT_READY );
return(FALSE);
}
if ( pEndpoint->fAddingSockets) {
//
// Someone is already adding sockets. Do not add more
// Just return success
//
return ( TRUE);
}
pEndpoint->fAddingSockets = TRUE;
// calculate the buffer size
cbBuffer = pEndpoint->InitialRecvSize + 2* MIN_SOCKADDR_SIZE;
for ( fReturn = TRUE, i = 0 ; fReturn && i++ < nSockets; ) {
PVOID pvBuff;
PATQ_CONT pAtqContext = NULL;
//
// Alloc a buffer for receive data
// TBD: Pool all these buffers into one large buffer.
//
pvBuff = LocalAlloc( LPTR, cbBuffer);
//
// Get the ATQ context now because we need its overlapped structure
//
if (pvBuff != NULL)
pAtqContext = I_AtqAllocContextFromCache();
//
// Now check if allocations are valid and do proper cleanup on failure
//
if ( pvBuff == NULL || pAtqContext == NULL) {
if ( pvBuff ) {
LocalFree( pvBuff );
pvBuff = NULL;
}
if ( pAtqContext ) {
pAtqContext->Signature = ATQ_FREE_CONTEXT_SIGNATURE;
I_AtqFreeContextToCache( pAtqContext );
pAtqContext = NULL;
}
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
fReturn = FALSE;
break;
} else {
//
// Add this socket to AtqContext lists & completion ports
// From now on the called function will take care of freeing up
// pAtqContext, if there is a failure.
//
pAtqContext->SetAcceptExBuffer( pvBuff);
pAtqContext->hAsyncIO = NULL;
pAtqContext->hJraAsyncIO = 0;
if ( !I_AtqAddAcceptExSocket(pEndpoint, pAtqContext) ) {
//
// Failed to add the socket, free up the context without reuse
//
ATQ_PRINTF(( DBG_CONTEXT,
"[I_AtqPrepareAcceptExSockets] for Endpoint %08x"
" and AtqContext (%08x) failed with "
" Error = %d; Now freeing the context ...\n",
pEndpoint, pAtqContext, GetLastError()
));
DWORD dwError = GetLastError();
// free without reuse
DBG_ASSERT( pAtqContext->m_nIO == 0);
pAtqContext->CleanupAndRelease();
SetLastError(dwError);
fReturn = FALSE;
}
}
} // for
//
// Finished Adding sockets. Indicate that by resetting the flab
//
pEndpoint->fAddingSockets = FALSE;
ATQ_PRINTF(( DBG_CONTEXT,
"PrepareAcceptExSockets( Endpoint[%08x], nSockets = %d)==>"
" avail = %d; Total Refs = %d.\n",
pEndpoint,
nSockets,
pEndpoint->nSocketsAvail,
pEndpoint->m_refCount
));
return ( fReturn);
} // I_AtqPrepareAcceptExSockets()
BOOL
I_AtqInitializeNtEntryPoints(
VOID
)
{
HINSTANCE tmpInstance;
//
// load kernel32 and get NT specific entry points
//
tmpInstance = LoadLibrary("kernel32.dll");
if ( tmpInstance != NULL ) {
g_pfnReadDirChangesW = (PFN_READ_DIR_CHANGES_W)
GetProcAddress( tmpInstance, "ReadDirectoryChangesW");
DBG_ASSERT(g_pfnReadDirChangesW != NULL);
//
// We can free this because we are statically linked to it
//
FreeLibrary(tmpInstance);
}
g_hMSWsock = LoadLibrary( "mswsock.dll" );
if ( g_hMSWsock != NULL ) {
SOCKET sTempSocket = INVALID_SOCKET;
GUID guidTransmitFile = WSAID_TRANSMITFILE;
GUID guidAcceptEx = WSAID_ACCEPTEX;
GUID guidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
DWORD cbReturned;
//
// Lets create a temporary socket so that we can use WSAIoctl to
// get the direct function pointers for AcceptEx(), TransmitFile(),
// etc.
//
sTempSocket = WSASocketW( AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL,
0,
0 );
if ( sTempSocket == INVALID_SOCKET )
{
ATQ_PRINTF(( DBG_CONTEXT,
"Failed to create temp socket "
"for determining WinSock provider. Error = %d",
WSAGetLastError() ));
goto cleanup;
}
WSAIoctl( sTempSocket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(LPVOID) &guidTransmitFile,
sizeof( guidTransmitFile ),
&g_pfnTransmitFile,
sizeof( g_pfnTransmitFile ),
&cbReturned,
NULL,
NULL );
WSAIoctl( sTempSocket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(LPVOID) &guidAcceptEx,
sizeof( guidAcceptEx ),
&g_pfnAcceptEx,
sizeof( g_pfnAcceptEx ),
&cbReturned,
NULL,
NULL );
WSAIoctl( sTempSocket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(LPVOID) &guidGetAcceptExSockAddrs,
sizeof( guidGetAcceptExSockAddrs ),
&g_pfnGetAcceptExSockaddrs,
sizeof( g_pfnGetAcceptExSockaddrs ),
&cbReturned,
NULL,
NULL );
//
// Close temporary socket
//
closesocket( sTempSocket );
if ( !g_pfnAcceptEx ||
!g_pfnGetAcceptExSockaddrs ||
!g_pfnTransmitFile ) {
//
// This is bad.
//
DBG_ASSERT(FALSE);
ATQ_PRINTF(( DBG_CONTEXT,
"Failed to get entry points AE %x TF %x GAE %x\n",
g_pfnAcceptEx, g_pfnTransmitFile,
g_pfnGetAcceptExSockaddrs));
goto cleanup;
}
} else {
ATQ_PRINTF((DBG_CONTEXT,
"Error %d in LoadLibrary[mswsock.dll]\n",
GetLastError()));
goto cleanup;
}
//
// load ntdll
//
if ( g_fUseDriver ) {
g_hNtdll = LoadLibrary( "ntdll.dll" );
if ( g_hNtdll != NULL ) {
g_pfnNtLoadDriver = (PFN_NT_LOAD_DRIVER)
GetProcAddress( g_hNtdll, "NtLoadDriver" );
g_pfnRtlInitUnicodeString = (PFN_RTL_INIT_UNICODE_STRING)
GetProcAddress( g_hNtdll, "RtlInitUnicodeString" );
g_pfnRtlNtStatusToDosError = (PFN_RTL_NTSTATUS_TO_DOSERR)
GetProcAddress( g_hNtdll, "RtlNtStatusToDosError" );
g_pfnRtlInitAnsiString = (PFN_RTL_INIT_ANSI_STRING)
GetProcAddress( g_hNtdll, "RtlInitAnsiString" );
g_pfnRtlAnsiStringToUnicodeString =
(PFN_RTL_ANSI_STRING_TO_UNICODE_STRING)
GetProcAddress( g_hNtdll, "RtlAnsiStringToUnicodeString" );
g_pfnRtlFreeHeap = (PFN_RTL_FREE_HEAP)
GetProcAddress( g_hNtdll, "RtlFreeHeap" );
g_pfnRtlDosPathNameToNtPathName_U =
(PFN_RTL_DOS_PATHNAME_TO_NT_PATHNAME)
GetProcAddress( g_hNtdll, "RtlDosPathNameToNtPathName_U" );
if ( !g_pfnNtLoadDriver ||
!g_pfnRtlInitUnicodeString ||
!g_pfnRtlNtStatusToDosError ||
!g_pfnRtlInitAnsiString ||
!g_pfnRtlAnsiStringToUnicodeString ||
!g_pfnRtlFreeHeap ||
!g_pfnRtlDosPathNameToNtPathName_U ) {
//
// This is bad.
//
ATQ_PRINTF(( DBG_CONTEXT,
"Failed to get entry points for ntdll.dll\n"));
DBG_ASSERT(FALSE);
goto cleanup;
}
} else {
ATQ_PRINTF((DBG_CONTEXT,
"Error %d in LoadLibrary[ntdll.dll]\n", GetLastError()));
goto cleanup;
}
}
return(TRUE);
cleanup:
if ( g_hNtdll != NULL ) {
FreeLibrary( g_hNtdll );
g_hNtdll = NULL;
}
if ( g_hMSWsock != NULL ) {
FreeLibrary( g_hMSWsock );
g_hMSWsock = NULL;
}
return(FALSE);
} // I_AtqInitializeEntryPoints