mirror of https://github.com/tongzx/nt5src
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.
3080 lines
80 KiB
3080 lines
80 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
atqmain.cxx
|
|
|
|
Abstract:
|
|
This module implements entry points for ATQ - Asynchronous Thread Queue.
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 8-Apr-1996
|
|
|
|
Environment:
|
|
|
|
User Mode -- Win32
|
|
|
|
Project:
|
|
|
|
Internet Services Common DLL
|
|
|
|
Functions Exported:
|
|
|
|
BOOL AtqInitialize();
|
|
BOOL AtqTerminate();
|
|
BOOL AtqGetCompletionPort();
|
|
|
|
DWORD AtqSetInfo();
|
|
DWORD AtqGetInfo();
|
|
BOOL AtqGetStatistics();
|
|
BOOL AtqClearStatistics();
|
|
|
|
BOOL AtqAddAcceptExSockets();
|
|
BOOL AtqAddAsyncHandle();
|
|
DWORD AtqContextSetInfo();
|
|
VOID AtqCloseSocket();
|
|
VOID AtqFreeContext();
|
|
|
|
BOOL AtqReadFile();
|
|
BOOL AtqWriteFile();
|
|
BOOL AtqTransmitFile();
|
|
BOOL AtqPostCompletionStatus();
|
|
|
|
PVOID AtqAllocateBandwidthInfo();
|
|
BOOL AtqFreeBandwidthInfo();
|
|
DWORD AtqBandwidthSetInfo();
|
|
--*/
|
|
|
|
#include "isatq.hxx"
|
|
#include <iscaptrc.h>
|
|
|
|
# define ATQ_REG_DEF_THREAD_TIMEOUT_PWS (30*60) // 30 minutes
|
|
|
|
/************************************************************
|
|
* Globals
|
|
************************************************************/
|
|
|
|
//
|
|
// specifies the registry location to use for getting the ATQ Configuration
|
|
// (Global overrides)
|
|
//
|
|
CHAR g_PSZ_ATQ_CONFIG_PARAMS_REG_KEY[] =
|
|
TEXT("System\\CurrentControlSet\\Services\\InetInfo\\Parameters");
|
|
|
|
// ----------------------------------------
|
|
// # of CPUs in machine (for thread-tuning)
|
|
// ----------------------------------------
|
|
|
|
DWORD g_cCPU = 0;
|
|
|
|
//
|
|
// concurrent # of threads to run per processor
|
|
//
|
|
|
|
DWORD g_cConcurrency = ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY;
|
|
|
|
//
|
|
// Amount of time (in ms) a worker thread will be idle before suicide
|
|
//
|
|
|
|
DWORD g_msThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT * 1000;
|
|
|
|
BOOL g_fUseAcceptEx = TRUE; // Use AcceptEx if available
|
|
|
|
//
|
|
// The absolute thread limit
|
|
//
|
|
|
|
LONG g_cMaxThreadLimit = ATQ_REG_DEF_POOL_THREAD_LIMIT;
|
|
|
|
//
|
|
// Should we use fake completion port
|
|
//
|
|
|
|
BOOL g_fUseFakeCompletionPort = FALSE;
|
|
|
|
//
|
|
// Assumed minimum file transfer rate
|
|
//
|
|
|
|
DWORD g_cbMinKbSec = ATQ_REG_DEF_MIN_KB_SEC;
|
|
|
|
//
|
|
// Size of buffers for fake xmits
|
|
//
|
|
|
|
DWORD g_cbXmitBufferSize = ATQ_REG_DEF_NONTF_BUFFER_SIZE;
|
|
|
|
//
|
|
// number of active context list
|
|
//
|
|
|
|
DWORD g_dwNumContextLists = ATQ_NUM_CONTEXT_LIST;
|
|
|
|
/*
|
|
g_pfnExitThreadCallback()
|
|
This routine sets the callback routine to be called when one of the
|
|
Atq threads exit so that thread state data can be cleaned up. Currently
|
|
support is for a single routine. One way to support multiple routines would
|
|
be for the caller to save the return value. Such an application would not
|
|
be able to delete the "saved" callback routine.
|
|
*/
|
|
ATQ_THREAD_EXIT_CALLBACK g_pfnExitThreadCallback = NULL;
|
|
|
|
// ----------------------------------
|
|
// Fake Completion port
|
|
// -----------------------------------
|
|
//
|
|
// Used to gauge pool thread creation. This variable shows number of
|
|
// ATQ contexts // ready to be processed by ATQ pool thread. Basically
|
|
// this is length of outcoming queue in SIO module and is modified by
|
|
// routines there
|
|
//
|
|
|
|
DWORD g_AtqWaitingContextsCount = 0;
|
|
|
|
//
|
|
// mswsock entry points
|
|
//
|
|
|
|
HINSTANCE g_hMSWsock = NULL;
|
|
PFN_ACCEPTEX g_pfnAcceptEx = NULL;
|
|
PFN_GETACCEPTEXSOCKADDRS g_pfnGetAcceptExSockaddrs = NULL;
|
|
PFN_TRANSMITFILE g_pfnTransmitFile = NULL;
|
|
PFN_GET_QUEUED_COMPLETION_STATUS g_pfnGetQueuedCompletionStatus = NULL;
|
|
PFN_CREATE_COMPLETION_PORT g_pfnCreateCompletionPort = NULL;
|
|
PFN_CLOSE_COMPLETION_PORT g_pfnCloseCompletionPort = NULL;
|
|
PFN_POST_COMPLETION_STATUS g_pfnPostCompletionStatus = NULL;
|
|
|
|
HINSTANCE g_hNtdll = NULL;
|
|
PFN_RTL_INIT_UNICODE_STRING g_pfnRtlInitUnicodeString = NULL;
|
|
PFN_NT_LOAD_DRIVER g_pfnNtLoadDriver = NULL;
|
|
PFN_RTL_NTSTATUS_TO_DOSERR g_pfnRtlNtStatusToDosError = NULL;
|
|
PFN_RTL_INIT_ANSI_STRING g_pfnRtlInitAnsiString = NULL;
|
|
PFN_RTL_ANSI_STRING_TO_UNICODE_STRING g_pfnRtlAnsiStringToUnicodeString = NULL;
|
|
PFN_RTL_DOS_PATHNAME_TO_NT_PATHNAME g_pfnRtlDosPathNameToNtPathName_U = NULL;
|
|
PFN_RTL_FREE_HEAP g_pfnRtlFreeHeap = NULL;
|
|
|
|
//
|
|
// NT specific
|
|
//
|
|
|
|
PFN_READ_DIR_CHANGES_W g_pfnReadDirChangesW = NULL;
|
|
|
|
// ------------------------------
|
|
// Current State Information
|
|
// ------------------------------
|
|
|
|
|
|
HANDLE g_hCompPort = NULL; // Handle for completion port
|
|
LONG g_cThreads = 0; // number of thread in the pool
|
|
LONG g_cAvailableThreads = 0; // # of threads waiting on the port.
|
|
|
|
//
|
|
// Is the NTS driver in use
|
|
//
|
|
|
|
BOOL g_fUseDriver = FALSE;
|
|
|
|
//
|
|
// Should we use the TF_USE_KERNEL_APC flag for TransmitFile
|
|
//
|
|
|
|
BOOL g_fUseKernelApc = ATQ_REG_DEF_USE_KERNEL_APC;
|
|
|
|
//
|
|
// Current thread limit
|
|
//
|
|
|
|
LONG g_cMaxThreads = ATQ_REG_DEF_PER_PROCESSOR_ATQ_THREADS;
|
|
|
|
DWORD g_cListenBacklog = ATQ_REG_DEF_LISTEN_BACKLOG;
|
|
|
|
BOOL g_fShutdown = FALSE; // if set, indicates that we are shutting down
|
|
// in that case, all threads should exit.
|
|
|
|
HANDLE g_hShutdownEvent = NULL; // set when all running threads shutdown
|
|
|
|
BOOL g_fEnableDebugThreads = FALSE; // if TRUE, debug IO threads can be
|
|
// created
|
|
|
|
BOOL g_fCreateDebugThread = FALSE; // set to TRUE to create a debug thread
|
|
|
|
// ------------------------------
|
|
// Bandwidth Throttling Info
|
|
// ------------------------------
|
|
|
|
PBANDWIDTH_INFO g_pBandwidthInfo = NULL;
|
|
|
|
// ------------------------------
|
|
// Various State/Object Lists
|
|
// ------------------------------
|
|
|
|
//
|
|
// Used to switch context between lists
|
|
//
|
|
|
|
DWORD AtqGlobalContextCount = 0;
|
|
|
|
//
|
|
// List of active context
|
|
//
|
|
|
|
ATQ_CONTEXT_LISTHEAD AtqActiveContextList[ATQ_NUM_CONTEXT_LIST];
|
|
|
|
//
|
|
// List of Endpoints in ATQ - one per listen socket
|
|
//
|
|
|
|
LIST_ENTRY AtqEndpointList;
|
|
CRITICAL_SECTION AtqEndpointLock;
|
|
|
|
PALLOC_CACHE_HANDLER g_pachAtqContexts;
|
|
|
|
#ifdef IIS_AUX_COUNTERS
|
|
|
|
LONG g_AuxCounters[NUM_AUX_COUNTERS];
|
|
|
|
#endif // IIS_AUX_COUNTERS
|
|
|
|
//
|
|
// Timeout before closing pending listens in case backlog is full
|
|
//
|
|
|
|
DWORD g_cForceTimeout;
|
|
|
|
//
|
|
// Flag enabling/disabling the backlog monitor
|
|
//
|
|
|
|
BOOL g_fDisableBacklogMonitor = FALSE;
|
|
|
|
// ------------------------------
|
|
// local to this module
|
|
// ------------------------------
|
|
|
|
LONG sg_AtqInitializeCount = -1;
|
|
BOOL g_fSpudInitialized = FALSE;
|
|
HANDLE g_hCapThread = NULL;
|
|
|
|
DWORD
|
|
I_AtqGetGlobalConfiguration(VOID);
|
|
|
|
DWORD
|
|
I_NumAtqEndpointsOpen(VOID);
|
|
|
|
DWORD
|
|
AtqDebugCreatorThread(
|
|
LPDWORD param
|
|
);
|
|
|
|
//
|
|
// Capacity Planning variables
|
|
//
|
|
|
|
extern TRACEHANDLE IISCapTraceRegistrationHandle;
|
|
|
|
//
|
|
// Ensure that initialization/termination don't happen at the same time
|
|
//
|
|
|
|
CRITICAL_SECTION g_csInitTermLock;
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
BOOL
|
|
AtqInitialize(
|
|
IN DWORD dwFlags
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Initializes the ATQ package
|
|
|
|
Arguments:
|
|
dwFlags - DWORD containing the flags for use to initialize ATQ library.
|
|
Notably in many cases one may not need the SPUD driver initialized
|
|
for processes other than the IIS main process. This dword helps
|
|
to shut off the unwanted flags.
|
|
|
|
This is an ugly way to initialize/shutdown SPUD, but that is what we
|
|
will do. SPUD supports only ONE completion port and hence when we use
|
|
ATQ in multiple processes we should be careful to initialize SPUD only
|
|
once and hopefully in the IIS main process!
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error (call GetLastError)
|
|
|
|
Note:
|
|
As of 4/16/97 the pszRegKey that is sent is no more utilized.
|
|
We always load the internal configuration parameters from
|
|
one single registry entry specified by PSZ_ATQ_CONFIG_PARAMS_REG_KEY
|
|
The parameter is left in the command line for compatibility
|
|
with old callers :( - NYI: Need to change this.
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
DWORD dwThreadID;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "AtqInitialize, %d, %x\n",
|
|
sg_AtqInitializeCount, dwFlags));
|
|
|
|
if ( InterlockedIncrement( &sg_AtqInitializeCount) != 0) {
|
|
|
|
IF_DEBUG( API_ENTRY) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqInitialize( %08x). ATQ is already initialized.\n",
|
|
dwFlags));
|
|
}
|
|
|
|
//
|
|
// we are already initialized. Ignore the new registry settings
|
|
//
|
|
|
|
return ( TRUE);
|
|
}
|
|
|
|
EnterCriticalSection( &g_csInitTermLock );
|
|
|
|
IF_DEBUG( API_ENTRY) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqInitialize[%08x]. Initializing....\n",
|
|
dwFlags));
|
|
}
|
|
|
|
// get the number of processors for this machine
|
|
// do it only for NT Server only (don't scale workstation)
|
|
if ( TsIsNtServer() ) {
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo( &si );
|
|
g_cCPU = si.dwNumberOfProcessors;
|
|
} else {
|
|
g_cCPU = 1;
|
|
}
|
|
|
|
//
|
|
// Initialize context lists and crit sects
|
|
//
|
|
|
|
ATQ_CONTEXT_LISTHEAD * pacl;
|
|
|
|
for ( pacl = AtqActiveContextList;
|
|
pacl < (AtqActiveContextList + g_dwNumContextLists);
|
|
pacl++) {
|
|
pacl->Initialize();
|
|
}
|
|
|
|
InitializeListHead( &AtqEndpointList );
|
|
INITIALIZE_CRITICAL_SECTION( &AtqEndpointLock );
|
|
|
|
//
|
|
// init bandwidth throttling
|
|
//
|
|
|
|
ATQ_REQUIRE( BANDWIDTH_INFO::AbwInitialize() );
|
|
|
|
//
|
|
// Read registry configurable Atq options. We have to read these now
|
|
// because concurrency is set for the completion port at creation time.
|
|
//
|
|
|
|
DWORD dwError = I_AtqGetGlobalConfiguration();
|
|
|
|
if ( NO_ERROR != dwError) {
|
|
SetLastError( dwError );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Setup an allocation cache for the ATQ Contexts
|
|
// NYI: Auto-tune the threshold limit
|
|
//
|
|
|
|
{
|
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
|
|
|
DWORD nCachedAtq = ATQ_CACHE_LIMIT_NTS;
|
|
|
|
if ( TsIsWindows95()) { nCachedAtq = ATQ_CACHE_LIMIT_W95; }
|
|
|
|
acConfig.nConcurrency = 1;
|
|
acConfig.nThreshold = nCachedAtq;
|
|
acConfig.cbSize = sizeof(ATQ_CONTEXT);
|
|
|
|
g_pachAtqContexts = new ALLOC_CACHE_HANDLER( "ATQ", &acConfig);
|
|
|
|
if ( NULL == g_pachAtqContexts) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create the shutdown event
|
|
//
|
|
|
|
g_hShutdownEvent = IIS_CREATE_EVENT(
|
|
"g_hShutdownEvent",
|
|
&g_hShutdownEvent,
|
|
TRUE, // Manual reset
|
|
FALSE // Not signalled
|
|
);
|
|
|
|
if ( !g_hShutdownEvent ) {
|
|
|
|
DBGERROR(( DBG_CONTEXT, "Create Shutdown event failed. Last Error = 0x%x\n",
|
|
GetLastError()
|
|
));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Create the completion port
|
|
//
|
|
|
|
g_hCompPort = g_pfnCreateCompletionPort(INVALID_HANDLE_VALUE,
|
|
NULL,
|
|
0,
|
|
g_cConcurrency
|
|
);
|
|
|
|
if ( !g_hCompPort ) {
|
|
|
|
DBGERROR(( DBG_CONTEXT, "Create IoComp port failed. Last Error = 0x%x\n",
|
|
GetLastError()
|
|
));
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// initialize spud driver
|
|
//
|
|
|
|
if ( (dwFlags & ATQ_INIT_SPUD_FLAG) && g_fUseDriver ) {
|
|
if (! I_AtqSpudInitialize() ) {
|
|
goto cleanup;
|
|
}
|
|
g_fSpudInitialized = TRUE;
|
|
}
|
|
|
|
//
|
|
// Initialize Backlog Monitor
|
|
//
|
|
|
|
if ( !g_fDisableBacklogMonitor )
|
|
{
|
|
DBG_ASSERT( g_pAtqBacklogMonitor == NULL );
|
|
g_pAtqBacklogMonitor = new ATQ_BACKLOG_MONITOR;
|
|
if (!g_pAtqBacklogMonitor) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ensure all other initializations also are done
|
|
//
|
|
|
|
g_cThreads = 0;
|
|
g_fShutdown = FALSE;
|
|
g_cAvailableThreads = 0;
|
|
|
|
if ( !I_AtqStartTimeoutProcessing( NULL ) ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
IF_DEBUG(INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"fUseAcceptEx[%d] NT CompPort[%d] Platform[%d]"
|
|
" fUseDriver[%d]\n",
|
|
g_fUseAcceptEx, !g_fUseFakeCompletionPort,
|
|
IISPlatformType(),
|
|
g_fUseDriver
|
|
));
|
|
}
|
|
|
|
//
|
|
// Create the initial ATQ thread.
|
|
//
|
|
|
|
(VOID)I_AtqCheckThreadStatus( (PVOID)ATQ_INITIAL_THREAD );
|
|
|
|
//
|
|
// Create a second thread if we are NTS
|
|
//
|
|
|
|
if ( TsIsNtServer() ) {
|
|
(VOID)I_AtqCheckThreadStatus( (PVOID)ATQ_INITIAL_THREAD );
|
|
}
|
|
|
|
//
|
|
// Initialize Capacity Planning Trace
|
|
//
|
|
// Spawn another thread to do this since IISInitializeCapTrace() can
|
|
// take a while and we are not in a state where SCM is getting
|
|
// SERVICE_STARTING messages
|
|
//
|
|
|
|
g_hCapThread = CreateThread( NULL,
|
|
0,
|
|
IISInitializeCapTrace,
|
|
NULL,
|
|
0,
|
|
&dwThreadID
|
|
);
|
|
|
|
IF_DEBUG( API_EXIT) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqInitialize( %08x) returns %d.\n",
|
|
dwFlags, TRUE));
|
|
}
|
|
|
|
//
|
|
// Create the debug thread starter if necessary
|
|
//
|
|
|
|
if ( g_fEnableDebugThreads )
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwThreadID;
|
|
HANDLE hThread;
|
|
|
|
hThread = CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)AtqDebugCreatorThread,
|
|
NULL,
|
|
0,
|
|
&dwThreadID );
|
|
if ( !hThread )
|
|
{
|
|
goto cleanup;
|
|
}
|
|
CloseHandle( hThread );
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csInitTermLock );
|
|
|
|
return TRUE;
|
|
|
|
cleanup:
|
|
DWORD dwSaveError = GetLastError();
|
|
|
|
for (i=0; i<g_dwNumContextLists; i++) {
|
|
|
|
AtqActiveContextList[i].Cleanup();
|
|
}
|
|
|
|
DeleteCriticalSection( &AtqEndpointLock);
|
|
|
|
if ( g_hShutdownEvent != NULL ) {
|
|
CloseHandle( g_hShutdownEvent );
|
|
g_hShutdownEvent = NULL;
|
|
}
|
|
|
|
if ( g_hCompPort != NULL ) {
|
|
g_pfnCloseCompletionPort( g_hCompPort );
|
|
g_hCompPort = NULL;
|
|
}
|
|
|
|
if ( NULL != g_pachAtqContexts) {
|
|
delete g_pachAtqContexts;
|
|
g_pachAtqContexts = NULL;
|
|
}
|
|
|
|
if ( NULL != g_pAtqBacklogMonitor ) {
|
|
delete g_pAtqBacklogMonitor;
|
|
g_pAtqBacklogMonitor = NULL;
|
|
}
|
|
|
|
ATQ_REQUIRE( BANDWIDTH_INFO::AbwTerminate());
|
|
|
|
IF_DEBUG( API_EXIT) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqInitialize( %08x) returns %d.\n",
|
|
dwFlags, FALSE));
|
|
}
|
|
|
|
sg_AtqInitializeCount = -1;
|
|
|
|
SetLastError(dwSaveError);
|
|
|
|
LeaveCriticalSection( &g_csInitTermLock );
|
|
|
|
return(FALSE);
|
|
|
|
} // AtqInitialize()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqTerminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up the ATQ package. Should only be called after all of the
|
|
clients of ATQ have been shutdown.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE, if ATQ was shutdown properly
|
|
FALSE, otherwise
|
|
|
|
--*/
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "AtqTerminate, %d\n", sg_AtqInitializeCount));
|
|
|
|
DWORD currentThreadCount;
|
|
ATQ_CONTEXT_LISTHEAD * pacl;
|
|
BOOL fRet = TRUE;
|
|
DWORD dwErr;
|
|
|
|
// there are outstanding users, don't fully terminate
|
|
if ( InterlockedDecrement( &sg_AtqInitializeCount) >= 0) {
|
|
|
|
/*IF_DEBUG( API_ENTRY)*/ {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqTerminate() - there are other users."
|
|
" Not terminating now\n"
|
|
));
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
EnterCriticalSection( &g_csInitTermLock );
|
|
|
|
/*IF_DEBUG( API_ENTRY)*/ {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqTerminate() - Terminating ATQ ...\n"
|
|
));
|
|
}
|
|
|
|
|
|
//
|
|
// All the ATQ endpoints should have been terminated before calling
|
|
// this ATQTerminate() function. If not, sorry return failure.
|
|
//
|
|
DWORD nEndpointsToBeClosed = I_NumAtqEndpointsOpen();
|
|
|
|
if ( nEndpointsToBeClosed > 0) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" There are %d endpoints remaining to be closed."
|
|
" Somebody above stream did not close endpoints."
|
|
" BUG IN CODE ABOVE ATQ\n"
|
|
,
|
|
nEndpointsToBeClosed
|
|
));
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRet = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
|
|
if ( (g_hShutdownEvent == NULL) || g_fShutdown ) {
|
|
|
|
//
|
|
// We have not been intialized or have already terminated.
|
|
//
|
|
SetLastError( ERROR_NOT_READY );
|
|
fRet = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// All clients should have cleaned themselves up before calling us.
|
|
//
|
|
|
|
for ( pacl = AtqActiveContextList;
|
|
pacl < (AtqActiveContextList + g_dwNumContextLists);
|
|
pacl++) {
|
|
|
|
pacl->Lock();
|
|
|
|
if ( !IsListEmpty(&pacl->ActiveListHead)) {
|
|
|
|
ATQ_ASSERT( IsListEmpty( &pacl->ActiveListHead));
|
|
pacl->Unlock();
|
|
|
|
IF_DEBUG( API_EXIT) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqTerminate() - ContextList(%08x) has "
|
|
"Active Contexts. Failed Termination.\n",
|
|
pacl
|
|
));
|
|
}
|
|
|
|
fRet = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
pacl->Unlock();
|
|
} // for
|
|
|
|
//
|
|
// Note that we are shutting down and prevent any more handles from
|
|
// being added to the completion port.
|
|
//
|
|
|
|
g_fShutdown = TRUE;
|
|
|
|
//
|
|
// Attempt and remove the TimeOut Context from scheduler queue
|
|
//
|
|
DBG_REQUIRE( I_AtqStopTimeoutProcessing());
|
|
|
|
currentThreadCount = g_cThreads;
|
|
if (currentThreadCount > 0) {
|
|
|
|
DWORD i;
|
|
BOOL fRes;
|
|
OVERLAPPED overlapped;
|
|
|
|
//
|
|
// Post a message to the completion port for each worker thread
|
|
// telling it to exit. The indicator is a NULL context in the
|
|
// completion.
|
|
//
|
|
|
|
ZeroMemory( &overlapped, sizeof(OVERLAPPED) );
|
|
|
|
for (i=0; i<currentThreadCount; i++) {
|
|
|
|
fRes = g_pfnPostCompletionStatus( g_hCompPort,
|
|
0,
|
|
0,
|
|
&overlapped );
|
|
|
|
ATQ_ASSERT( (fRes == TRUE) ||
|
|
( (fRes == FALSE) &&
|
|
(GetLastError() == ERROR_IO_PENDING) )
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now wait for the pool threads to shutdown.
|
|
//
|
|
|
|
dwErr = WaitForSingleObject( g_hShutdownEvent, ATQ_WAIT_FOR_THREAD_DEATH);
|
|
#if 0
|
|
DWORD dwWaitCount = 0;
|
|
while ( dwErr == WAIT_TIMEOUT) {
|
|
|
|
dwWaitCount++;
|
|
DebugBreak();
|
|
Sleep( 10*1000); // sleep for some time
|
|
dwErr =
|
|
WaitForSingleObject( g_hShutdownEvent, ATQ_WAIT_FOR_THREAD_DEATH);
|
|
} // while
|
|
# endif // 0
|
|
|
|
//
|
|
// At this point, no other threads should be left running.
|
|
//
|
|
//
|
|
// g_cThreads counter is decremented by AtqPoolThread().
|
|
// AtqTerminate() is called during the DLL termination
|
|
// But at DLL termination, all ATQ pool threads are killed =>
|
|
// no one is decrementing the count. Hence this assert will always fail.
|
|
//
|
|
|
|
// ATQ_ASSERT( !g_cThreads );
|
|
|
|
ATQ_REQUIRE( CloseHandle( g_hShutdownEvent ) );
|
|
g_pfnCloseCompletionPort( g_hCompPort );
|
|
|
|
g_hShutdownEvent = NULL;
|
|
g_hCompPort = NULL;
|
|
|
|
//
|
|
// Cleanup our synchronization resources
|
|
//
|
|
|
|
for ( pacl = AtqActiveContextList;
|
|
pacl < (AtqActiveContextList + g_dwNumContextLists);
|
|
pacl++) {
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pacl->Lock();
|
|
|
|
if ( !IsListEmpty( &pacl->PendingAcceptExListHead)) {
|
|
for ( pEntry = pacl->PendingAcceptExListHead.Flink;
|
|
pEntry != &pacl->PendingAcceptExListHead;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
PATQ_CONT pContext =
|
|
CONTAINING_RECORD( pEntry, ATQ_CONTEXT, m_leTimeout );
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
pContext->Print();
|
|
} // for
|
|
}
|
|
|
|
pacl->Unlock();
|
|
pacl->Cleanup();
|
|
}
|
|
|
|
|
|
//
|
|
// Free all the elements in the Allocation caching list
|
|
//
|
|
if ( NULL != g_pachAtqContexts) {
|
|
delete g_pachAtqContexts;
|
|
g_pachAtqContexts = NULL;
|
|
}
|
|
|
|
if ( g_hCapThread )
|
|
{
|
|
WaitForSingleObject( g_hCapThread, INFINITE );
|
|
CloseHandle( g_hCapThread );
|
|
g_hCapThread = NULL;
|
|
|
|
if (IISCapTraceRegistrationHandle != (TRACEHANDLE)0)
|
|
{
|
|
UnregisterTraceGuids( IISCapTraceRegistrationHandle );
|
|
}
|
|
|
|
}
|
|
|
|
DeleteCriticalSection( &AtqEndpointLock);
|
|
|
|
//
|
|
// cleanup backlog monitor
|
|
//
|
|
delete g_pAtqBacklogMonitor;
|
|
g_pAtqBacklogMonitor = NULL;
|
|
|
|
// Cleanup variables in ATQ Bandwidth throttle module
|
|
if ( !BANDWIDTH_INFO::AbwTerminate()) {
|
|
|
|
// there may be a few blocked IO. We should avoid them all.
|
|
// All clients should have cleaned themselves up before coming here.
|
|
|
|
fRet = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// cleanup driver
|
|
//
|
|
|
|
if ( g_fSpudInitialized ) {
|
|
(VOID)I_AtqSpudTerminate();
|
|
g_fSpudInitialized = FALSE;
|
|
}
|
|
|
|
if ( g_hNtdll != NULL ) {
|
|
FreeLibrary(g_hNtdll);
|
|
g_hNtdll = NULL;
|
|
}
|
|
|
|
if ( g_hMSWsock != NULL ) {
|
|
FreeLibrary(g_hMSWsock);
|
|
g_hMSWsock = NULL;
|
|
}
|
|
|
|
IF_DEBUG( API_EXIT) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqTerminate() - Successfully cleaned up.\n"
|
|
));
|
|
}
|
|
|
|
Finished:
|
|
|
|
LeaveCriticalSection( &g_csInitTermLock );
|
|
|
|
return fRet;
|
|
} // AtqTerminate()
|
|
|
|
|
|
|
|
HANDLE
|
|
AtqGetCompletionPort()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the completion port created by ATQ
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Handle to ATQ completion port
|
|
|
|
--*/
|
|
{
|
|
return g_hCompPort;
|
|
} // AtqGetCompletionPort()
|
|
|
|
|
|
ULONG_PTR
|
|
AtqSetInfo(
|
|
IN ATQ_INFO atqInfo,
|
|
IN ULONG_PTR Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets various bits of information for the ATQ module
|
|
|
|
Arguments:
|
|
|
|
atqInfo - Data item to set
|
|
data - New value for item
|
|
|
|
Return Value:
|
|
|
|
The old value of the parameter
|
|
|
|
--*/
|
|
{
|
|
ULONG_PTR oldVal = 0;
|
|
|
|
switch ( atqInfo ) {
|
|
|
|
case AtqBandwidthThrottle:
|
|
|
|
ATQ_ASSERT( g_pBandwidthInfo != NULL );
|
|
oldVal = (ULONG_PTR)g_pBandwidthInfo->SetBandwidthLevel( (DWORD)Data );
|
|
break;
|
|
|
|
case AtqBandwidthThrottleMaxBlocked:
|
|
|
|
ATQ_ASSERT( g_pBandwidthInfo != NULL );
|
|
oldVal = (ULONG_PTR)g_pBandwidthInfo->SetMaxBlockedListSize( (DWORD)Data );
|
|
break;
|
|
|
|
case AtqExitThreadCallback:
|
|
|
|
oldVal = (ULONG_PTR)g_pfnExitThreadCallback;
|
|
g_pfnExitThreadCallback = (ATQ_THREAD_EXIT_CALLBACK ) Data;
|
|
break;
|
|
|
|
case AtqMaxPoolThreads:
|
|
// the value is per processor values
|
|
// internally we maintain value for all processors
|
|
oldVal = (ULONG_PTR)( g_cMaxThreads/g_cCPU );
|
|
g_cMaxThreads = (DWORD)Data * g_cCPU;
|
|
break;
|
|
|
|
//
|
|
// Increment or decrement the max thread count. In this instance, we
|
|
// do not scale by the number of CPUs
|
|
//
|
|
|
|
case AtqIncMaxPoolThreads:
|
|
InterlockedIncrement( (LONG *) &g_cMaxThreads );
|
|
oldVal = TRUE;
|
|
break;
|
|
|
|
case AtqDecMaxPoolThreads:
|
|
InterlockedDecrement( (LONG *) &g_cMaxThreads );
|
|
oldVal = TRUE;
|
|
break;
|
|
|
|
case AtqMaxConcurrency:
|
|
oldVal = (ULONG_PTR)g_cConcurrency;
|
|
g_cConcurrency = (DWORD)Data;
|
|
break;
|
|
|
|
case AtqThreadTimeout:
|
|
oldVal = (ULONG_PTR)(g_msThreadTimeout/1000); // convert back to seconds
|
|
g_msThreadTimeout = (DWORD)Data * 1000; // convert value to millisecs
|
|
break;
|
|
|
|
case AtqUseAcceptEx:
|
|
oldVal = (ULONG_PTR)g_fUseAcceptEx;
|
|
if ( !TsIsWindows95() ) {
|
|
g_fUseAcceptEx = (DWORD)Data;
|
|
}
|
|
break;
|
|
|
|
case AtqMinKbSec:
|
|
|
|
//
|
|
// Ignore it if the value is zero
|
|
//
|
|
|
|
if ( Data ) {
|
|
oldVal = (ULONG_PTR)g_cbMinKbSec;
|
|
g_cbMinKbSec = (DWORD)Data;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
return oldVal;
|
|
|
|
} // AtqSetInfo()
|
|
|
|
|
|
|
|
|
|
|
|
ULONG_PTR
|
|
AtqGetInfo(
|
|
IN ATQ_INFO atqInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets various bits of information for the ATQ module
|
|
|
|
Arguments:
|
|
|
|
atqInfo - Data item to set
|
|
|
|
Return Value:
|
|
|
|
The old value of the parameter
|
|
|
|
--*/
|
|
{
|
|
ULONG_PTR dwVal = 0;
|
|
|
|
switch ( atqInfo ) {
|
|
|
|
case AtqBandwidthThrottle:
|
|
ATQ_ASSERT( g_pBandwidthInfo != NULL );
|
|
dwVal = (ULONG_PTR ) g_pBandwidthInfo->QueryBandwidthLevel();
|
|
break;
|
|
|
|
case AtqExitThreadCallback:
|
|
|
|
dwVal = (ULONG_PTR ) g_pfnExitThreadCallback;
|
|
break;
|
|
|
|
case AtqMaxPoolThreads:
|
|
dwVal = (ULONG_PTR ) (g_cMaxThreads/g_cCPU);
|
|
break;
|
|
|
|
case AtqMaxConcurrency:
|
|
dwVal = (ULONG_PTR ) g_cConcurrency;
|
|
break;
|
|
|
|
case AtqThreadTimeout:
|
|
dwVal = (ULONG_PTR ) (g_msThreadTimeout/1000); // convert back to seconds
|
|
break;
|
|
|
|
case AtqUseAcceptEx:
|
|
dwVal = (ULONG_PTR ) g_fUseAcceptEx;
|
|
break;
|
|
|
|
case AtqMinKbSec:
|
|
dwVal = (ULONG_PTR ) g_cbMinKbSec;
|
|
break;
|
|
|
|
case AtqMaxThreadLimit:
|
|
dwVal = (ULONG_PTR ) g_cMaxThreadLimit;
|
|
break;
|
|
|
|
case AtqAvailableThreads:
|
|
dwVal = (ULONG_PTR) g_cAvailableThreads;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE );
|
|
break;
|
|
} // switch
|
|
|
|
return dwVal;
|
|
} // AtqGetInfo()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqGetStatistics(IN OUT ATQ_STATISTICS * pAtqStats)
|
|
{
|
|
if ( pAtqStats != NULL) {
|
|
|
|
return g_pBandwidthInfo->GetStatistics( pAtqStats );
|
|
|
|
} else {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
return (FALSE);
|
|
}
|
|
} // AtqGetStatistics()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqClearStatistics( VOID)
|
|
{
|
|
return g_pBandwidthInfo->ClearStatistics();
|
|
|
|
} // AtqClearStatistics()
|
|
|
|
|
|
|
|
|
|
|
|
ULONG_PTR
|
|
AtqContextSetInfo(
|
|
PATQ_CONTEXT patqContext,
|
|
enum ATQ_CONTEXT_INFO atqInfo,
|
|
ULONG_PTR Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets various bits of information for this context
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
atqInfo - Data item to set
|
|
data - New value for item
|
|
|
|
Return Value:
|
|
|
|
The old value of the parameter
|
|
|
|
--*/
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT) patqContext;
|
|
ULONG_PTR OldVal = 0;
|
|
|
|
ATQ_ASSERT( pContext );
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
|
|
if ( pContext && pContext->Signature == ATQ_CONTEXT_SIGNATURE )
|
|
{
|
|
switch ( atqInfo ) {
|
|
|
|
case ATQ_INFO_TIMEOUT:
|
|
OldVal = (ULONG_PTR)pContext->TimeOut;
|
|
pContext->TimeOut = CanonTimeout( (DWORD)Data );
|
|
break;
|
|
|
|
case ATQ_INFO_RESUME_IO:
|
|
|
|
//
|
|
// set back the max timeout from pContext->TimeOut
|
|
// This will ensure that timeout processing can go on
|
|
// peacefully.
|
|
//
|
|
|
|
{
|
|
DWORD currentTime = AtqGetCurrentTick( );
|
|
DWORD timeout;
|
|
OldVal = (ULONG_PTR)pContext->NextTimeout;
|
|
timeout = pContext->TimeOut;
|
|
|
|
//
|
|
// Set the new timeout
|
|
//
|
|
|
|
I_SetNextTimeout(pContext);
|
|
|
|
//
|
|
// Return the old
|
|
//
|
|
|
|
if ( currentTime >= (DWORD)OldVal ) {
|
|
ATQ_ASSERT((OldVal & ATQ_INFINITE) == 0);
|
|
OldVal = 0;
|
|
} else if ( (OldVal & ATQ_INFINITE) == 0 ) {
|
|
OldVal -= currentTime;
|
|
}
|
|
|
|
// return correct units
|
|
OldVal = (ULONG_PTR)UndoCanonTimeout( (DWORD)OldVal );
|
|
}
|
|
break;
|
|
|
|
case ATQ_INFO_COMPLETION:
|
|
OldVal = (ULONG_PTR)pContext->pfnCompletion;
|
|
pContext->pfnCompletion = (ATQ_COMPLETION) Data;
|
|
break;
|
|
|
|
case ATQ_INFO_COMPLETION_CONTEXT:
|
|
|
|
ATQ_ASSERT( Data != 0 ); // NULL context not allowed
|
|
|
|
OldVal = (ULONG_PTR)pContext->ClientContext;
|
|
pContext->ClientContext = (void *) Data;
|
|
break;
|
|
|
|
case ATQ_INFO_BANDWIDTH_INFO:
|
|
{
|
|
ATQ_ASSERT( Data != 0 );
|
|
|
|
PBANDWIDTH_INFO pBandwidthInfo = (PBANDWIDTH_INFO) Data;
|
|
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() ==
|
|
ATQ_BW_INFO_SIGNATURE );
|
|
|
|
if ( !pBandwidthInfo->IsFreed() )
|
|
{
|
|
pContext->m_pBandwidthInfo = (PBANDWIDTH_INFO) Data;
|
|
pContext->m_pBandwidthInfo->Reference();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ATQ_INFO_ABORTIVE_CLOSE:
|
|
OldVal = (ULONG_PTR)pContext->IsFlag( ACF_ABORTIVE_CLOSE );
|
|
if ( Data )
|
|
{
|
|
pContext->SetFlag( ACF_ABORTIVE_CLOSE );
|
|
}
|
|
else
|
|
{
|
|
pContext->ResetFlag( ACF_ABORTIVE_CLOSE );
|
|
}
|
|
break;
|
|
|
|
case ATQ_INFO_FORCE_CLOSE:
|
|
OldVal = (ULONG_PTR)pContext->ForceClose();
|
|
pContext->SetForceClose( Data ? TRUE : FALSE );
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE );
|
|
}
|
|
}
|
|
|
|
return OldVal;
|
|
|
|
} // AtqContextSetInfo()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqAddAsyncHandle(
|
|
PATQ_CONTEXT * ppatqContext,
|
|
PVOID EndpointObject,
|
|
PVOID ClientContext,
|
|
ATQ_COMPLETION pfnCompletion,
|
|
DWORD TimeOut,
|
|
HANDLE hAsyncIO
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a handle to the thread queue
|
|
|
|
The client should call this after the IO handle is opened
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
ppatqContext - Receives allocated ATQ Context
|
|
Context - Context to call client with
|
|
pfnCompletion - Completion to call when IO completes
|
|
TimeOut - Time to wait (sec) for IO completion (INFINITE is valid)
|
|
hAsyncIO - Handle with pending read or write
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
return ( I_AtqAddAsyncHandle( (PATQ_CONT *) ppatqContext,
|
|
(PATQ_ENDPOINT) EndpointObject,
|
|
ClientContext,
|
|
pfnCompletion,
|
|
TimeOut,
|
|
hAsyncIO)
|
|
&&
|
|
I_AddAtqContextToPort( *((PATQ_CONT *) ppatqContext))
|
|
);
|
|
|
|
} // AtqAddAsyncHandle()
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AtqGetAcceptExAddrs(
|
|
IN PATQ_CONTEXT patqContext,
|
|
OUT SOCKET * pSock,
|
|
OUT PVOID * ppvBuff,
|
|
OUT PVOID * pEndpointContext,
|
|
OUT SOCKADDR * * ppsockaddrLocal,
|
|
OUT SOCKADDR * * ppsockaddrRemote
|
|
)
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
INT cbsockaddrLocal;
|
|
INT cbsockaddrRemote;
|
|
DWORD cb;
|
|
|
|
ATQ_ASSERT( g_fUseAcceptEx);
|
|
ATQ_ASSERT( pContext->pEndpoint);
|
|
|
|
*pSock = HANDLE_TO_SOCKET(pContext->hAsyncIO);
|
|
*pEndpointContext = pContext->pEndpoint->Context;
|
|
|
|
//
|
|
// The buffer not only receives the initial received data, it also
|
|
// gets the sock addrs, which must be at least sockaddr_in + 16 bytes
|
|
// large
|
|
//
|
|
|
|
g_pfnGetAcceptExSockaddrs( pContext->pvBuff,
|
|
(cb = pContext->pEndpoint->InitialRecvSize),
|
|
MIN_SOCKADDR_SIZE,
|
|
MIN_SOCKADDR_SIZE,
|
|
ppsockaddrLocal,
|
|
&cbsockaddrLocal,
|
|
ppsockaddrRemote,
|
|
&cbsockaddrRemote );
|
|
|
|
*ppvBuff = ( ( cb == 0) ? NULL : pContext->pvBuff);
|
|
|
|
return;
|
|
} // AtqGetAcceptExAddrs()
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqCloseSocket(
|
|
PATQ_CONTEXT patqContext,
|
|
BOOL fShutdown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the socket in this atq structure if it wasn't
|
|
closed by transmitfile. This function should be called only
|
|
if the embedded handle in AtqContext is a Socket.
|
|
|
|
Arguments:
|
|
|
|
patqContext - Context whose socket should be closed.
|
|
fShutdown - If TRUE, means we call shutdown and always close the socket.
|
|
Note that if TransmitFile closed the socket, it will have done the
|
|
shutdown for us
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
--*/
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
|
|
if ( pContext ) {
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
|
|
BOOL fAbortiveClose;
|
|
|
|
fAbortiveClose = pContext->IsFlag( ACF_ABORTIVE_CLOSE );
|
|
pContext->ResetFlag( ACF_ABORTIVE_CLOSE );
|
|
|
|
//
|
|
// Don't delete the socket if we don't have to
|
|
//
|
|
|
|
if ( pContext->IsState( ACS_SOCK_UNCONNECTED |
|
|
ACS_SOCK_CLOSED) &&
|
|
!pContext->ForceClose()
|
|
) {
|
|
|
|
//
|
|
// Do nothing
|
|
//
|
|
|
|
} else {
|
|
|
|
// default:
|
|
// case ACS_SOCK_LISTENING:
|
|
// case ACS_SOCK_CONNECTED: {
|
|
|
|
HANDLE hIO;
|
|
PATQ_ENDPOINT pEndpoint;
|
|
|
|
pEndpoint = pContext->pEndpoint;
|
|
|
|
pContext->MoveState( ACS_SOCK_CLOSED);
|
|
|
|
//
|
|
// During shutdown, the socket may be closed while this thread
|
|
// is doing processing, so only give a warning if any of the
|
|
// following fail
|
|
//
|
|
|
|
hIO = (HANDLE )InterlockedExchangePointer(
|
|
(PVOID *)&pContext->hAsyncIO,
|
|
NULL
|
|
);
|
|
|
|
if ( hIO == NULL ) {
|
|
|
|
//
|
|
// No socket - it is already closed - do nothing.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (fAbortiveClose || fShutdown ) {
|
|
|
|
//
|
|
// If this is an AcceptEx socket, we must first force a
|
|
// user mode context update before we can call shutdown
|
|
//
|
|
|
|
if ( (pEndpoint != NULL) && (pEndpoint->UseAcceptEx) ) {
|
|
|
|
if ( setsockopt( HANDLE_TO_SOCKET(hIO),
|
|
SOL_SOCKET,
|
|
SO_UPDATE_ACCEPT_CONTEXT,
|
|
(char *) &pEndpoint->ListenSocket,
|
|
sizeof(SOCKET) ) == SOCKET_ERROR ) {
|
|
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"[AtqCloseSocket] Warning- setsockopt "
|
|
"failed, error %d, socket = %x,"
|
|
" Context= %08x, Listen = %lx\n",
|
|
GetLastError(),
|
|
hIO,
|
|
pContext,
|
|
pEndpoint->ListenSocket ));
|
|
}
|
|
}
|
|
} // setsock-opt call
|
|
|
|
if ( fAbortiveClose ) {
|
|
LINGER linger;
|
|
|
|
linger.l_onoff = TRUE;
|
|
linger.l_linger = 0;
|
|
|
|
if ( setsockopt( HANDLE_TO_SOCKET(hIO),
|
|
SOL_SOCKET,
|
|
SO_LINGER,
|
|
(char *) &linger,
|
|
sizeof(linger) ) == SOCKET_ERROR
|
|
) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"[AtqCloseSocket] Warning- setsockopt "
|
|
"failed, error %d, socket = %x,"
|
|
" Context= %08x, Listen = %lx\n",
|
|
GetLastError(),
|
|
hIO,
|
|
pContext,
|
|
pEndpoint->ListenSocket ));
|
|
}
|
|
else {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"[AtqCloseSocket(%08x)] requested"
|
|
" abortive close\n",
|
|
pContext));
|
|
}
|
|
} // set up linger
|
|
|
|
if ( fShutdown ) {
|
|
|
|
//
|
|
// Note that shutdown can fail in instances where the
|
|
// client aborts in the middle of a TransmitFile.
|
|
// This is an acceptable failure case
|
|
//
|
|
|
|
shutdown( HANDLE_TO_SOCKET(hIO), 1 );
|
|
}
|
|
|
|
DBG_ASSERT( hIO != NULL);
|
|
|
|
if ( closesocket( HANDLE_TO_SOCKET(hIO) ) ) {
|
|
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"[AtqCloseSocket] Warning- closesocket "
|
|
" failed, Context = %08x, error %d,"
|
|
" socket = %x\n",
|
|
pContext,
|
|
GetLastError(),
|
|
hIO ));
|
|
}
|
|
} // if (hIO != NULL)
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "[AtqCloseSocket] Warning - NULL Atq context\n"));
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
} // AtqCloseSocket()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqCloseFileHandle(
|
|
PATQ_CONTEXT patqContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the file handle in this atq structure.
|
|
This function should be called only if the embedded handle
|
|
in AtqContext is a file handle.
|
|
|
|
Arguments:
|
|
|
|
patqContext - Context whose file handle should be closed.
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
Note:
|
|
THIS FUNCTIONALITY IS ADDED TO SERVE A SPECIAL REQUEST!!!
|
|
Most of the ATQ code thinks that the handle here is a socket.
|
|
Except of course this function...
|
|
--*/
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
|
|
if ( pContext != NULL ) {
|
|
|
|
HANDLE hIO;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( !pContext->IsAcceptExRootContext());
|
|
ATQ_ASSERT( !TsIsWindows95() ); // NYI
|
|
|
|
hIO =
|
|
(HANDLE ) InterlockedExchangePointer(
|
|
(PVOID *)&pContext->hAsyncIO,
|
|
NULL
|
|
);
|
|
|
|
if ( (hIO == NULL) || !CloseHandle( hIO ) ) {
|
|
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"[AtqCloseFileHandle] Warning- CloseHandle failed, "
|
|
" Context = %08x, error %d, handle = %x\n",
|
|
pContext,
|
|
GetLastError(),
|
|
hIO ));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "[AtqCloseSocket] Warning - NULL Atq context\n"));
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
} // AtqCloseFileHandle()
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AtqFreeContext(
|
|
PATQ_CONTEXT patqContext,
|
|
BOOL fReuseContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the context created in AtqAddAsyncHandle.
|
|
Call this after the async handle has been closed and all outstanding
|
|
IO operations have been completed. The context is invalid after this call.
|
|
Call AtqFreeContext() for same context only ONCE.
|
|
|
|
Arguments:
|
|
|
|
patqContext - Context to free
|
|
fReuseContext - TRUE if this can context can be reused in the context of
|
|
the calling thread. Should be FALSE if the calling thread will exit
|
|
soon (i.e., isn't an AtqPoolThread).
|
|
|
|
--*/
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT)patqContext;
|
|
|
|
ATQ_ASSERT( pContext != NULL );
|
|
|
|
IF_DEBUG( API_ENTRY) {
|
|
|
|
ATQ_PRINTF(( DBG_CONTEXT, "AtqFreeContext( %08x (handle=%08x,"
|
|
" nIOs = %d), fReuse=%d)\n",
|
|
patqContext, patqContext->hAsyncIO,
|
|
pContext->m_nIO,
|
|
fReuseContext));
|
|
}
|
|
|
|
if ( pContext ) {
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
|
|
if ( fReuseContext ) {
|
|
pContext->SetFlag( ACF_REUSE_CONTEXT);
|
|
} else {
|
|
pContext->ResetFlag( ACF_REUSE_CONTEXT);
|
|
}
|
|
|
|
if ( InterlockedDecrement( &pContext->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
|
|
//
|
|
|
|
DBG_ASSERT( pContext->lSyncTimeout == 0);
|
|
AtqpReuseOrFreeContext( pContext, fReuseContext);
|
|
}
|
|
}
|
|
|
|
return;
|
|
} // AtqFreeContext()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqReadFile(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD BytesToRead,
|
|
IN OVERLAPPED * lpo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does an async read using the handle defined in the context.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
lpBuffer - Buffer to put read data in
|
|
BytesToRead - number of bytes to read
|
|
lpo - Overlapped structure to use
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
DWORD cbRead; // discarded after usage ( since this is Async)
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( !TsIsWindows95() ); // NYI
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
I_SetNextTimeout(pContext);
|
|
pContext->BytesSent = 0;
|
|
|
|
if ( !lpo ) {
|
|
lpo = &pContext->Overlapped;
|
|
}
|
|
|
|
switch ( pBandwidthInfo->QueryStatus( AtqIoRead ) ) {
|
|
|
|
case StatusAllowOperation:
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
fRes = ( ReadFile( pContext->hAsyncIO,
|
|
lpBuffer,
|
|
BytesToRead,
|
|
&cbRead,
|
|
lpo ) ||
|
|
GetLastError() == ERROR_IO_PENDING);
|
|
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
|
|
break;
|
|
|
|
case StatusBlockOperation:
|
|
|
|
// store data for restarting the operation.
|
|
pContext->arInfo.atqOp = AtqIoRead;
|
|
pContext->arInfo.lpOverlapped = lpo;
|
|
pContext->arInfo.uop.opReadWrite.buf1.len = BytesToRead;
|
|
pContext->arInfo.uop.opReadWrite.buf1.buf = (CHAR * ) lpBuffer;
|
|
pContext->arInfo.uop.opReadWrite.dwBufferCount = 1;
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
|
|
|
// Put this request in queue of blocked requests.
|
|
fRes = pBandwidthInfo->BlockRequest( pContext );
|
|
if ( fRes )
|
|
{
|
|
pBandwidthInfo->IncTotalBlockedRequests();
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case StatusRejectOperation:
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
pBandwidthInfo->IncTotalRejectedRequests();
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE);
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
} // switch()
|
|
|
|
return fRes;
|
|
} // AtqReadFile()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqReadSocket(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN LPWSABUF pwsaBuffers,
|
|
IN DWORD dwBufferCount,
|
|
IN OVERLAPPED * lpo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does an async recv using the handle defined in the context
|
|
as a socket.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
lpBuffer - Buffer to put read data in
|
|
BytesToRead - number of bytes to read
|
|
lpo - Overlapped structure to use
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
DWORD cbRead; // discarded after usage ( since this is Async)
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
IF_DEBUG(API_ENTRY) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqReadSocket(%08lx) called.\n", pContext));
|
|
}
|
|
|
|
if (pContext->IsFlag( ACF_RECV_ISSUED)) {
|
|
IF_DEBUG( SPUD ) {
|
|
ATQ_PRINTF(( DBG_CONTEXT,
|
|
"AtqReadSocket -> WSARecv bypassed.\n"));
|
|
}
|
|
pContext->BytesSent = 0;
|
|
pContext->SetFlag( ACF_RECV_CALLED);
|
|
|
|
#if CC_REF_TRACKING
|
|
//
|
|
// ATQ notification trace
|
|
//
|
|
// Notify client context of all non-oplock notification.
|
|
// This is for debugging purpose only.
|
|
//
|
|
// Code 0xf9f9f9f9 indicates a ACF_RECV_CALLED is set in the flags field
|
|
//
|
|
|
|
pContext->NotifyIOCompletion( (ULONG_PTR)pContext, pContext->m_acFlags, 0xf9f9f9f9 );
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
I_SetNextTimeout(pContext);
|
|
|
|
|
|
// count the number of bytes
|
|
DBG_ASSERT( dwBufferCount >= 1);
|
|
pContext->BytesSent = 0;
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
if ( !lpo ) {
|
|
lpo = &pContext->Overlapped;
|
|
}
|
|
|
|
//
|
|
// NYI: Create an optimal function table
|
|
//
|
|
|
|
if ( !g_fUseFakeCompletionPort &&
|
|
(StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoRead ) ) ) {
|
|
|
|
DWORD lpFlags = 0;
|
|
|
|
fRes = ( (WSARecv( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
pwsaBuffers,
|
|
dwBufferCount,
|
|
&cbRead,
|
|
&lpFlags, // no flags
|
|
lpo,
|
|
NULL // no completion routine
|
|
) == 0) ||
|
|
(WSAGetLastError() == WSA_IO_PENDING));
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
} else {
|
|
|
|
switch ( pBandwidthInfo->QueryStatus( AtqIoRead ) ) {
|
|
|
|
case StatusAllowOperation:
|
|
|
|
DBG_ASSERT(g_fUseFakeCompletionPort);
|
|
|
|
fRes = SIOWSARecv(
|
|
pContext,
|
|
pwsaBuffers,
|
|
dwBufferCount,
|
|
lpo
|
|
);
|
|
|
|
break;
|
|
|
|
case StatusBlockOperation:
|
|
|
|
// store data for restarting the operation.
|
|
pContext->arInfo.atqOp = AtqIoRead;
|
|
pContext->arInfo.lpOverlapped = lpo;
|
|
|
|
pContext->arInfo.uop.opReadWrite.dwBufferCount = dwBufferCount;
|
|
if ( dwBufferCount == 1) {
|
|
pContext->arInfo.uop.opReadWrite.buf1.len = pwsaBuffers->len;
|
|
pContext->arInfo.uop.opReadWrite.buf1.buf = pwsaBuffers->buf;
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
|
} else {
|
|
DBG_ASSERT( dwBufferCount > 1);
|
|
|
|
//
|
|
// Inefficient: But we will burn CPU for b/w throttling.
|
|
//
|
|
WSABUF * pBuf = (WSABUF *)
|
|
::LocalAlloc( LPTR, dwBufferCount * sizeof (WSABUF));
|
|
if ( NULL != pBuf) {
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = pBuf;
|
|
CopyMemory( pBuf, pwsaBuffers,
|
|
dwBufferCount * sizeof(WSABUF));
|
|
} else {
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
fRes = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Put this request in queue of blocked requests.
|
|
fRes = pBandwidthInfo->BlockRequest( pContext );
|
|
if ( fRes )
|
|
{
|
|
pBandwidthInfo->IncTotalBlockedRequests();
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case StatusRejectOperation:
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
pBandwidthInfo->IncTotalRejectedRequests();
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE);
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
fRes = FALSE;
|
|
break;
|
|
} // switch()
|
|
}
|
|
|
|
return fRes;
|
|
} // AtqReadSocket()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqWriteFile(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN LPCVOID lpBuffer,
|
|
IN DWORD BytesToWrite,
|
|
IN OVERLAPPED * lpo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does an async write using the handle defined in the context.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
lpBuffer - Buffer to write
|
|
BytesToWrite - number of bytes to write
|
|
lpo - Overlapped structure to use
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
DWORD cbWritten; // discarded after usage ( since this is Async)
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( !TsIsWindows95() ); // NYI
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
I_SetNextTimeout(pContext);
|
|
pContext->BytesSent = BytesToWrite;
|
|
|
|
if ( !lpo ) {
|
|
lpo = &pContext->Overlapped;
|
|
}
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
switch ( pBandwidthInfo->QueryStatus( AtqIoWrite) ) {
|
|
|
|
case StatusAllowOperation:
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
fRes = ( WriteFile( pContext->hAsyncIO,
|
|
lpBuffer,
|
|
BytesToWrite,
|
|
&cbWritten,
|
|
lpo ) ||
|
|
GetLastError() == ERROR_IO_PENDING);
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
|
|
break;
|
|
|
|
case StatusBlockOperation:
|
|
|
|
// store data for restarting the operation.
|
|
pContext->arInfo.atqOp = AtqIoWrite;
|
|
pContext->arInfo.lpOverlapped = lpo;
|
|
|
|
pContext->arInfo.uop.opReadWrite.buf1.len = BytesToWrite;
|
|
pContext->arInfo.uop.opReadWrite.buf1.buf = (CHAR * ) lpBuffer;
|
|
pContext->arInfo.uop.opReadWrite.dwBufferCount = 1;
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
|
|
|
// Put this request in queue of blocked requests.
|
|
fRes = pBandwidthInfo->BlockRequest( pContext );
|
|
if ( fRes )
|
|
{
|
|
pBandwidthInfo->IncTotalBlockedRequests();
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case StatusRejectOperation:
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
pBandwidthInfo->IncTotalRejectedRequests();
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE);
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
} // switch()
|
|
|
|
return fRes;
|
|
} // AtqWriteFile()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqWriteSocket(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN LPWSABUF pwsaBuffers,
|
|
IN DWORD dwBufferCount,
|
|
IN OVERLAPPED * lpo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does an async write using the handle defined in the context as a socket.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
pwsaBuffer - pointer to Winsock Buffers for scatter/gather
|
|
dwBufferCount - DWORD containing the count of buffers pointed
|
|
to by pwsaBuffer
|
|
lpo - Overlapped structure to use
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
DWORD cbWritten; // discarded after usage ( since this is Async)
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
I_SetNextTimeout(pContext);
|
|
|
|
//
|
|
// count the number of bytes
|
|
//
|
|
|
|
DBG_ASSERT( dwBufferCount >= 1);
|
|
pContext->BytesSent = pwsaBuffers->len;
|
|
if ( dwBufferCount > 1) {
|
|
LPWSABUF pWsaBuf;
|
|
for ( pWsaBuf = pwsaBuffers + 1;
|
|
pWsaBuf < (pwsaBuffers + dwBufferCount);
|
|
pWsaBuf++) {
|
|
pContext->BytesSent += pWsaBuf->len;
|
|
}
|
|
}
|
|
|
|
if ( lpo == NULL ) {
|
|
lpo = &pContext->Overlapped;
|
|
}
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
if ( !g_fUseFakeCompletionPort &&
|
|
(StatusAllowOperation ==
|
|
pBandwidthInfo->QueryStatus( AtqIoWrite ) ) ) {
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
fRes = ( (WSASend( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
pwsaBuffers,
|
|
dwBufferCount,
|
|
&cbWritten,
|
|
0, // no flags
|
|
lpo,
|
|
NULL // no completion routine
|
|
) == 0) ||
|
|
(WSAGetLastError() == WSA_IO_PENDING));
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
} else {
|
|
switch ( pBandwidthInfo->QueryStatus( AtqIoWrite ) ) {
|
|
|
|
case StatusAllowOperation:
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
DBG_ASSERT(g_fUseFakeCompletionPort);
|
|
|
|
fRes = SIOWSASend(
|
|
pContext,
|
|
pwsaBuffers,
|
|
dwBufferCount,
|
|
lpo
|
|
);
|
|
break;
|
|
|
|
case StatusBlockOperation:
|
|
|
|
// store data for restarting the operation.
|
|
pContext->arInfo.atqOp = AtqIoWrite;
|
|
pContext->arInfo.lpOverlapped = lpo;
|
|
|
|
pContext->arInfo.uop.opReadWrite.dwBufferCount = dwBufferCount;
|
|
if ( dwBufferCount == 1) {
|
|
pContext->arInfo.uop.opReadWrite.buf1.len = pwsaBuffers->len;
|
|
pContext->arInfo.uop.opReadWrite.buf1.buf = pwsaBuffers->buf;
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
|
} else {
|
|
DBG_ASSERT( dwBufferCount > 1);
|
|
|
|
//
|
|
// Inefficient: But we will burn CPU for b/w throttling.
|
|
//
|
|
WSABUF * pBuf = (WSABUF *)
|
|
::LocalAlloc( LPTR, dwBufferCount * sizeof (WSABUF));
|
|
if ( NULL != pBuf) {
|
|
pContext->arInfo.uop.opReadWrite.pBufAll = pBuf;
|
|
CopyMemory( pBuf, pwsaBuffers,
|
|
dwBufferCount * sizeof(WSABUF));
|
|
} else {
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
fRes = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Put this request in queue of blocked requests.
|
|
fRes = pBandwidthInfo->BlockRequest( pContext );
|
|
if ( fRes )
|
|
{
|
|
pBandwidthInfo->IncTotalBlockedRequests();
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case StatusRejectOperation:
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
pBandwidthInfo->IncTotalRejectedRequests();
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE);
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
} // switch()
|
|
}
|
|
|
|
return fRes;
|
|
} // AtqWriteSocket()
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqSyncWsaSend(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN LPWSABUF pwsaBuffers,
|
|
IN DWORD dwBufferCount,
|
|
OUT LPDWORD pcbWritten
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a sync write of an array of wsa buffers using WSASend.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
pwsaBuffer - pointer to Winsock Buffers for scatter/gather
|
|
dwBufferCount - DWORD containing the count of buffers pointed
|
|
to by pwsaBuffer
|
|
pcbWritten - ptr to count of bytes written
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL fRes = FALSE;
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
|
|
fRes = ( WSASend( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
pwsaBuffers,
|
|
dwBufferCount,
|
|
pcbWritten,
|
|
0, // no flags
|
|
NULL, // lpo == NULL for sync write
|
|
NULL // no completion routine
|
|
) == 0);
|
|
|
|
return fRes;
|
|
|
|
} // AtqSyncWsaSend()
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
AtqTransmitFile(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN HANDLE hFile,
|
|
IN DWORD dwBytesInFile,
|
|
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
|
|
IN DWORD dwFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a TransmitFile using the handle defined in the context.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
hFile - handle of file to read from
|
|
dwBytesInFile - Bytes to transmit
|
|
lpTransmitBuffers - transmit buffer structure
|
|
dwFlags - Transmit file flags
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
PATQ_CONT pContext = (PATQ_CONT) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
//
|
|
// For large file sends, the client's default timeout may not be
|
|
// adequte for slow links. Scale based on bytes being sent
|
|
//
|
|
|
|
I_SetNextTimeout(pContext);
|
|
pContext->BytesSent = dwBytesInFile;
|
|
|
|
if ( dwFlags == 0 ) {
|
|
|
|
//
|
|
// If no flags are set, then we can attempt to use the special
|
|
// write-behind flag. This flag can cause the TransmitFile to
|
|
// complete immediately, before the send actually completes.
|
|
// This can be a significant performance improvement inside the
|
|
// system.
|
|
//
|
|
|
|
dwFlags = TF_WRITE_BEHIND;
|
|
|
|
} else if ( dwFlags & TF_DISCONNECT ) {
|
|
|
|
//
|
|
// If the socket is getting disconnected, mark it appropriately
|
|
//
|
|
|
|
pContext->MoveState( ( ( dwFlags & TF_REUSE_SOCKET )?
|
|
ACS_SOCK_UNCONNECTED:
|
|
ACS_SOCK_CLOSED
|
|
)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Use kernel apc flag unless configured not to
|
|
//
|
|
if ( g_fUseKernelApc ) {
|
|
dwFlags |= TF_USE_KERNEL_APC;
|
|
}
|
|
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
if ( !g_fUseFakeCompletionPort &&
|
|
(StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) ) {
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
fRes = (g_pfnTransmitFile( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
hFile,
|
|
dwBytesInFile,
|
|
0,
|
|
&pContext->Overlapped,
|
|
lpTransmitBuffers,
|
|
dwFlags ) ||
|
|
(GetLastError() == ERROR_IO_PENDING));
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
} else {
|
|
switch ( pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) {
|
|
|
|
case StatusAllowOperation:
|
|
|
|
DBG_ASSERT(g_fUseFakeCompletionPort);
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
|
|
fRes = SIOTransmitFile(
|
|
pContext,
|
|
hFile,
|
|
dwBytesInFile,
|
|
lpTransmitBuffers
|
|
);
|
|
break;
|
|
|
|
case StatusBlockOperation:
|
|
|
|
// store data for restarting the operation.
|
|
pContext->arInfo.atqOp = AtqIoXmitFile;
|
|
pContext->arInfo.lpOverlapped = &pContext->Overlapped;
|
|
pContext->arInfo.uop.opXmit.hFile = hFile;
|
|
pContext->arInfo.uop.opXmit.dwBytesInFile = dwBytesInFile;
|
|
pContext->arInfo.uop.opXmit.lpXmitBuffers = lpTransmitBuffers;
|
|
pContext->arInfo.uop.opXmit.dwFlags = dwFlags;
|
|
|
|
// Put this request in queue of blocked requests.
|
|
fRes = pBandwidthInfo->BlockRequest( pContext);
|
|
if ( fRes )
|
|
{
|
|
pBandwidthInfo->IncTotalBlockedRequests();
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case StatusRejectOperation:
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
pBandwidthInfo->IncTotalRejectedRequests();
|
|
SetLastError( ERROR_NETWORK_BUSY);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ATQ_ASSERT( FALSE);
|
|
InterlockedDecrement( &pContext->m_nIO);
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
fRes = FALSE;
|
|
break;
|
|
|
|
} // switch()
|
|
}
|
|
|
|
//
|
|
// Restore the socket state if we failed so that the handle gets freed
|
|
//
|
|
|
|
if ( !fRes )
|
|
{
|
|
pContext->MoveState( ACS_SOCK_CONNECTED);
|
|
}
|
|
|
|
return fRes;
|
|
|
|
} // AtqTransmitFile()
|
|
|
|
|
|
BOOL
|
|
AtqTransmitFileEx(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN HANDLE hFile,
|
|
IN DWORD dwBytesInFile,
|
|
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
|
|
IN DWORD dwFlags,
|
|
IN OVERLAPPED * lpo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a TransmitFile using the handle defined in the context.
|
|
Uses the parameter lpo instead of the structure in the context.
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
hFile - handle of file to read from
|
|
dwBytesInFile - Bytes to transmit
|
|
lpTransmitBuffers - transmit buffer structure
|
|
dwFlags - Transmit file flags
|
|
lpo - overlapped structure
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fRes = TRUE;
|
|
PATQ_CONT pContext = (PATQ_CONT) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
//
|
|
// For large file sends, the client's default timeout may not be
|
|
// adequte for slow links. Scale based on bytes being sent
|
|
//
|
|
|
|
I_SetNextTimeout(pContext);
|
|
pContext->BytesSent = dwBytesInFile;
|
|
|
|
if ( dwFlags == 0 ) {
|
|
|
|
//
|
|
// If no flags are set, then we can attempt to use the special
|
|
// write-behind flag. This flag can cause the TransmitFile to
|
|
// complete immediately, before the send actually completes.
|
|
// This can be a significant performance improvement inside the
|
|
// system.
|
|
//
|
|
|
|
dwFlags = TF_WRITE_BEHIND;
|
|
|
|
} else if ( dwFlags & TF_DISCONNECT ) {
|
|
|
|
//
|
|
// If the socket is getting disconnected, mark it appropriately
|
|
//
|
|
|
|
pContext->MoveState( ( ( dwFlags & TF_REUSE_SOCKET )?
|
|
ACS_SOCK_UNCONNECTED:
|
|
ACS_SOCK_CLOSED
|
|
)
|
|
);
|
|
}
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
|
|
if ( !g_fUseFakeCompletionPort &&
|
|
(StatusAllowOperation == pBandwidthInfo->QueryStatus( AtqIoXmitFile ) ) ) {
|
|
|
|
pBandwidthInfo->IncTotalAllowedRequests();
|
|
fRes = (g_pfnTransmitFile( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
hFile,
|
|
dwBytesInFile,
|
|
0,
|
|
(lpo == NULL) ? &pContext->Overlapped : lpo,
|
|
lpTransmitBuffers,
|
|
dwFlags ) ||
|
|
(GetLastError() == ERROR_IO_PENDING));
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
} else {
|
|
ASSERT(FALSE);
|
|
fRes = FALSE;
|
|
}
|
|
|
|
//
|
|
// Restore the socket state if we failed so that the handle gets freed
|
|
//
|
|
|
|
if ( !fRes )
|
|
{
|
|
pContext->MoveState( ACS_SOCK_CONNECTED);
|
|
}
|
|
|
|
return fRes;
|
|
|
|
} // AtqTransmitFileEx()
|
|
|
|
|
|
BOOL
|
|
AtqReadDirChanges(IN PATQ_CONTEXT patqContext,
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD BytesToRead,
|
|
IN BOOL fWatchSubDir,
|
|
IN DWORD dwNotifyFilter,
|
|
IN OVERLAPPED * lpo
|
|
)
|
|
/*++
|
|
AtqReadDirChanges()
|
|
|
|
Description:
|
|
This function submits an Async ReadDirectoryChanges() call for
|
|
the Async handle in the ATQ context supplied.
|
|
It always requires a non-NULL overlapped pointer for processing
|
|
this call.
|
|
|
|
Arguments:
|
|
patqContext - pointer to ATQ Context
|
|
lpBuffer - buffer for the data to be read from ReadDirectoryChanges()
|
|
BytesToRead - count of bytes to read into buffer
|
|
fWatchSubDir - should we watch for sub directory changes
|
|
dwNotifyFilter - DWORD containing the flags for Notification
|
|
lpo - pointer to overlapped structure.
|
|
|
|
Returns:
|
|
TRUE if ReadDirectoryChanges() is successfully submitted.
|
|
FALSE if there is any failure in submitting IO.
|
|
--*/
|
|
{
|
|
BOOL fRes;
|
|
DWORD cbRead; // discarded after usage ( since this is Async)
|
|
PATQ_CONT pContext = (PATQ_CONT ) patqContext;
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone);
|
|
|
|
if ( g_pfnReadDirChangesW == NULL ) {
|
|
ATQ_PRINTF((DBG_CONTEXT,"ReadDirChanges entry point NULL\n"));
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return(FALSE);
|
|
}
|
|
|
|
if ( lpo == NULL ) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
I_SetNextTimeout(pContext);
|
|
pContext->BytesSent = 0;
|
|
|
|
InterlockedIncrement( &pContext->m_nIO);
|
|
fRes = g_pfnReadDirChangesW( pContext->hAsyncIO,
|
|
lpBuffer,
|
|
BytesToRead,
|
|
fWatchSubDir,
|
|
dwNotifyFilter,
|
|
&cbRead,
|
|
lpo,
|
|
NULL);
|
|
if (!fRes) { InterlockedDecrement( &pContext->m_nIO); };
|
|
return fRes;
|
|
} // AtqReadDirChanges()
|
|
|
|
|
|
|
|
BOOL
|
|
AtqPostCompletionStatus(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN DWORD BytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Posts a completion status on the completion port queue
|
|
|
|
An IO pending error code is treated as a success error code
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
Everything else as in the Win32 API
|
|
|
|
NOTES:
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error (call GetLastError)
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fRes;
|
|
PATQ_CONT pAtqContext = (PATQ_CONT ) patqContext;
|
|
PBANDWIDTH_INFO pBandwidthInfo = pAtqContext->m_pBandwidthInfo;
|
|
|
|
ATQ_ASSERT( (pAtqContext)->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
ATQ_ASSERT( pBandwidthInfo != NULL );
|
|
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
|
|
|
if ( !pAtqContext->IsBlocked()) {
|
|
|
|
InterlockedIncrement( &pAtqContext->m_nIO);
|
|
|
|
fRes = ( g_pfnPostCompletionStatus( g_hCompPort,
|
|
BytesTransferred,
|
|
(ULONG_PTR)patqContext,
|
|
&pAtqContext->Overlapped ) ||
|
|
(GetLastError() == ERROR_IO_PENDING));
|
|
if (!fRes) { InterlockedDecrement( &pAtqContext->m_nIO); };
|
|
} else {
|
|
|
|
//
|
|
// Forcibly remove the context from blocking list.
|
|
//
|
|
|
|
fRes = pBandwidthInfo->RemoveFromBlockedList(pAtqContext);
|
|
|
|
// There is a possibility of race conditions!
|
|
// If we cant remove an item from blocking list before
|
|
// its IO operation is scheduled.
|
|
// there wont be any call back generated for this case!
|
|
}
|
|
|
|
return fRes;
|
|
|
|
} // AtqPostCompletionStatus
|
|
|
|
|
|
|
|
DWORD
|
|
I_AtqGetGlobalConfiguration(VOID)
|
|
/*++
|
|
Description:
|
|
This function sets several global config params for the ATQ package.
|
|
It also reads the global configuration from registry for ATQ.
|
|
The values if present will override the defaults
|
|
|
|
Returns:
|
|
Win32 Errorcode - NO_ERROR on success and anything else for error
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
DWORD dwDefaultThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT;
|
|
|
|
//
|
|
// If this is a NTW, do the right thing
|
|
//
|
|
|
|
if ( !TsIsNtServer() ) {
|
|
g_cMaxThreadLimit = ATQ_REG_MIN_POOL_THREAD_LIMIT;
|
|
|
|
//
|
|
// chicago does not have transmitfile/acceptex support
|
|
//
|
|
|
|
if ( TsIsWindows95() ) {
|
|
|
|
g_fUseAcceptEx = FALSE;
|
|
g_fUseFakeCompletionPort = TRUE;
|
|
g_dwNumContextLists = ATQ_NUM_CONTEXT_LIST_W95;
|
|
dwDefaultThreadTimeout = ATQ_REG_DEF_THREAD_TIMEOUT_PWS;
|
|
g_cMaxThreadLimit = ATQ_MAX_THREAD_LIMIT_W95;
|
|
g_cMaxThreads = ATQ_MAX_THREAD_LIMIT_W95;
|
|
}
|
|
|
|
} else {
|
|
|
|
MEMORYSTATUS ms;
|
|
|
|
//
|
|
// get the memory size
|
|
//
|
|
|
|
ms.dwLength = sizeof(MEMORYSTATUS);
|
|
GlobalMemoryStatus( &ms );
|
|
|
|
//
|
|
// attempt to use driver
|
|
//
|
|
|
|
g_fUseDriver = TRUE;
|
|
|
|
//
|
|
// Alloc two threads per MB of memory.
|
|
//
|
|
|
|
g_cMaxThreadLimit = (LONG)((ms.dwTotalPhys >> 19) + 2);
|
|
|
|
if ( g_cMaxThreadLimit < ATQ_REG_MIN_POOL_THREAD_LIMIT ) {
|
|
g_cMaxThreadLimit = ATQ_REG_MIN_POOL_THREAD_LIMIT;
|
|
} else if ( g_cMaxThreadLimit > ATQ_REG_MAX_POOL_THREAD_LIMIT ) {
|
|
g_cMaxThreadLimit = ATQ_REG_MAX_POOL_THREAD_LIMIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get entry points for NT
|
|
//
|
|
|
|
if ( !TsIsWindows95() ) {
|
|
|
|
if ( !I_AtqInitializeNtEntryPoints( ) ) {
|
|
dwError = ERROR_MOD_NOT_FOUND;
|
|
return ( dwError);
|
|
}
|
|
|
|
g_pfnCreateCompletionPort = CreateIoCompletionPort;
|
|
g_pfnGetQueuedCompletionStatus = GetQueuedCompletionStatus;
|
|
g_pfnCloseCompletionPort = CloseHandle;
|
|
g_pfnPostCompletionStatus = PostQueuedCompletionStatus;
|
|
|
|
} else {
|
|
|
|
//
|
|
// win95 entry points
|
|
//
|
|
|
|
g_pfnCreateCompletionPort = SIOCreateCompletionPort;
|
|
g_pfnGetQueuedCompletionStatus = SIOGetQueuedCompletionStatus;
|
|
g_pfnCloseCompletionPort = SIODestroyCompletionPort;
|
|
g_pfnPostCompletionStatus = SIOPostCompletionStatus;
|
|
}
|
|
|
|
if ( !TsIsWindows95() ) {
|
|
|
|
HKEY hkey = NULL;
|
|
DWORD dwVal;
|
|
|
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
g_PSZ_ATQ_CONFIG_PARAMS_REG_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkey);
|
|
|
|
if ( dwError == NO_ERROR ) {
|
|
|
|
//
|
|
// Read the Concurrency factor per processor
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_PER_PROCESSOR_CONCURRENCY,
|
|
ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY);
|
|
|
|
AtqSetInfo( AtqMaxConcurrency, (ULONG_PTR)dwVal);
|
|
|
|
|
|
//
|
|
// Read the count of threads to be allowed per processor
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_PER_PROCESSOR_ATQ_THREADS,
|
|
ATQ_REG_DEF_PER_PROCESSOR_ATQ_THREADS
|
|
);
|
|
|
|
if ( dwVal != 0 ) {
|
|
AtqSetInfo( AtqMaxPoolThreads, (ULONG_PTR)dwVal);
|
|
}
|
|
|
|
|
|
//
|
|
// Read the Data transfer rate value for our calculations
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_MIN_KB_SEC,
|
|
ATQ_REG_DEF_MIN_KB_SEC );
|
|
|
|
AtqSetInfo( AtqMinKbSec, (ULONG_PTR)dwVal);
|
|
|
|
|
|
//
|
|
// read the max thread limit
|
|
//
|
|
|
|
g_cMaxThreadLimit = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_POOL_THREAD_LIMIT,
|
|
g_cMaxThreadLimit);
|
|
|
|
//
|
|
// read the listen backlog
|
|
//
|
|
|
|
g_cListenBacklog = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_LISTEN_BACKLOG,
|
|
g_cListenBacklog);
|
|
|
|
|
|
//
|
|
// Read the time (in seconds) of how long the threads
|
|
// can stay alive when there is no IO operation happening on
|
|
// that thread.
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_THREAD_TIMEOUT,
|
|
dwDefaultThreadTimeout
|
|
);
|
|
|
|
AtqSetInfo( AtqThreadTimeout, (ULONG_PTR)dwVal);
|
|
|
|
//
|
|
// See if we need to turn off TF_USE_KERNEL_APC flag for
|
|
// TransmitFile
|
|
//
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_USE_KERNEL_APC,
|
|
ATQ_REG_DEF_USE_KERNEL_APC );
|
|
g_fUseKernelApc = dwVal;
|
|
|
|
//
|
|
// Whether or not to enable debug thread creator
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_ENABLE_DEBUG_THREADS,
|
|
g_fEnableDebugThreads );
|
|
g_fEnableDebugThreads = !!dwVal;
|
|
|
|
//
|
|
// Do we want to run this backlog monitor at all?
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_DISABLE_BACKLOG_MONITOR,
|
|
g_fDisableBacklogMonitor );
|
|
g_fDisableBacklogMonitor = !!dwVal;
|
|
|
|
//
|
|
// Read timeout for backlog monitor
|
|
//
|
|
|
|
dwVal = I_AtqReadRegDword( hkey,
|
|
ATQ_REG_FORCE_TIMEOUT,
|
|
g_cForceTimeout );
|
|
g_cForceTimeout = dwVal;
|
|
|
|
|
|
ATQ_REQUIRE( !RegCloseKey( hkey ) );
|
|
hkey = NULL;
|
|
}
|
|
|
|
DBG_ASSERT( NULL == hkey);
|
|
|
|
} else {
|
|
|
|
g_cListenBacklog = 5;
|
|
AtqSetInfo( AtqMaxConcurrency, ATQ_REG_DEF_PER_PROCESSOR_CONCURRENCY);
|
|
AtqSetInfo( AtqUseAcceptEx, ATQ_REG_DEF_USE_ACCEPTEX);
|
|
}
|
|
|
|
return ( dwError);
|
|
} // I_AtqGetGlobalConfiguration()
|
|
|
|
|
|
|
|
DWORD
|
|
I_NumAtqEndpointsOpen(VOID)
|
|
/*++
|
|
Description:
|
|
This function counts the number of Enpoints that remain open.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
DWORD containing the number of endpoints that are open.
|
|
--*/
|
|
{
|
|
DWORD nEPOpen = 0;
|
|
AcquireLock( &AtqEndpointLock);
|
|
|
|
PLIST_ENTRY plEP;
|
|
for( plEP = AtqEndpointList.Flink;
|
|
plEP != &AtqEndpointList;
|
|
plEP = plEP->Flink ) {
|
|
|
|
nEPOpen++;
|
|
} // for
|
|
|
|
ReleaseLock( &AtqEndpointLock);
|
|
return ( nEPOpen);
|
|
} // I_NumAtqEndpointsOpen()
|
|
|
|
|
|
|
|
//
|
|
// Global functions
|
|
//
|
|
|
|
|
|
DWORD
|
|
I_AtqReadRegDword(
|
|
IN HKEY hkey,
|
|
IN LPCSTR pszValueName,
|
|
IN DWORD dwDefaultValue )
|
|
/*++
|
|
|
|
NAME: I_AtqReadRegDword
|
|
|
|
SYNOPSIS: Reads a DWORD value from the registry.
|
|
|
|
ENTRY: hkey - Openned registry key to read
|
|
|
|
pszValueName - The name of the value.
|
|
|
|
dwDefaultValue - The default value to use if the
|
|
value cannot be read.
|
|
|
|
RETURNS DWORD - The value from the registry, or dwDefaultValue.
|
|
|
|
--*/
|
|
{
|
|
DWORD err;
|
|
DWORD dwBuffer;
|
|
|
|
DWORD cbBuffer = sizeof(dwBuffer);
|
|
DWORD dwType;
|
|
|
|
if( hkey != NULL ) {
|
|
err = RegQueryValueExA( hkey,
|
|
pszValueName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwBuffer,
|
|
&cbBuffer );
|
|
|
|
if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) ) {
|
|
dwDefaultValue = dwBuffer;
|
|
}
|
|
}
|
|
|
|
return dwDefaultValue;
|
|
|
|
} // I_AtqReadRegDword()
|
|
|
|
BOOL
|
|
AtqSetSocketOption(
|
|
IN PATQ_CONTEXT patqContext,
|
|
IN INT optName,
|
|
IN INT optValue
|
|
)
|
|
/*++
|
|
|
|
AtqSetSocketOption()
|
|
|
|
Routine Description:
|
|
|
|
Set socket options. Presently only handles TCP_NODELAY
|
|
|
|
Arguments:
|
|
|
|
patqContext - pointer to ATQ context
|
|
optName - name of property to change
|
|
optValue - value of property to set
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else FALSE
|
|
|
|
--*/
|
|
{
|
|
if (TCP_NODELAY != optName)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PATQ_CONT pContext = (PATQ_CONT)patqContext;
|
|
|
|
ATQ_ASSERT( pContext != NULL );
|
|
|
|
IF_DEBUG( API_ENTRY) {
|
|
|
|
ATQ_PRINTF(( DBG_CONTEXT, "AtqSetSocketOption( %08x (handle=%08x,"
|
|
" nIOs = %d), optName=%d, optValue=%d)\n",
|
|
patqContext, patqContext->hAsyncIO,
|
|
pContext->m_nIO, optName, optValue));
|
|
}
|
|
|
|
if ( pContext ) {
|
|
|
|
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
|
|
|
if ( ((BOOL)pContext->IsFlag(ACF_TCP_NODELAY)) == ((BOOL)optValue))
|
|
{
|
|
//
|
|
// The flag is already enabled. Return success.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (NO_ERROR == setsockopt( HANDLE_TO_SOCKET(pContext->hAsyncIO),
|
|
IPPROTO_TCP,
|
|
TCP_NODELAY,
|
|
(char *)&optValue,
|
|
sizeof(INT)))
|
|
{
|
|
if ( optValue)
|
|
{
|
|
pContext->SetFlag(ACF_TCP_NODELAY);
|
|
}
|
|
else
|
|
{
|
|
pContext->ResetFlag(ACF_TCP_NODELAY);
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PIIS_CAP_TRACE_INFO
|
|
AtqGetCapTraceInfo(
|
|
IN PATQ_CONTEXT patqContext
|
|
)
|
|
{
|
|
PATQ_CONT pContext = (PATQ_CONT)patqContext;
|
|
|
|
ATQ_ASSERT( pContext != NULL );
|
|
|
|
return pContext->GetCapTraceInfo();
|
|
|
|
}
|
|
|
|
DWORD
|
|
AtqDebugCreatorThread(
|
|
LPDWORD param
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For debugging purpose.
|
|
|
|
This function will cause another IO thread to be created (regardless of
|
|
the max thread limit). This IO thread will only serve new connections.
|
|
|
|
To trigger the creation of a new thread, set isatq!g_fCreateDebugThread=1
|
|
|
|
Arguments:
|
|
|
|
param - Unused
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
for ( ; !g_fShutdown ; )
|
|
{
|
|
Sleep( 5000 );
|
|
|
|
if ( g_fCreateDebugThread )
|
|
{
|
|
OutputDebugString( "Creating DEBUG thread." \
|
|
"Reseting isatq!g_fCreateDebugThread\n" );
|
|
|
|
I_AtqCheckThreadStatus( (VOID*) ATQ_DEBUG_THREAD );
|
|
|
|
g_fCreateDebugThread = FALSE;
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/************************ End of File ***********************/
|