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

1390 lines
29 KiB
C++
Raw Permalink 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) 1996 Microsoft Corporation
Module Name:
iisendp.cxx
Abstract:
This module defines the IIS_ENDPOINT class
Author:
Johnson Apacible (JohnsonA) June-04-1996
--*/
#include "tcpdllp.hxx"
#include <rpc.h>
#include <tsunami.hxx>
#include <iistypes.hxx>
#include <iisendp.hxx>
#include <iisassoc.hxx>
#include "inetreg.h"
#include <tcpcons.h>
#include <apiutil.h>
#include <issched.hxx>
#include "..\atq\atqtypes.hxx"
#if IE_REF_TRACKING
//
// Ref count trace log size
//
#define C_IIS_ENDP_REFTRACES 4000
#define C_LOCAL_ENDP_REFTRACES 40
//
// Ref trace log for IIS_ENDPOINT objects
// NOTE we make this global so other classes can get at it
//
PTRACE_LOG g_pDbgIERefTraceLog = NULL;
#endif
/*******************************************************************
Macro support for IIS_ENDPOINT::Reference/Dereference
HISTORY:
MCourage 31-Oct-1997 Added ref trace logging
********************************************************************/
#if IE_REF_TRACKING
#define IE_LOG_REF_COUNT( cRefs ) \
\
IE_SHARED_LOG_REF_COUNT( \
cRefs \
, (PVOID) this \
, m_state \
, m_atqEndpoint \
, 0xffffffff \
); \
IE_LOCAL_LOG_REF_COUNT( \
cRefs \
, (PVOID) this \
, m_state \
, m_atqEndpoint \
, 0xffffffff \
);
#else
#define IE_LOG_REF_COUNT( cRefs )
#endif
PVOID
I_IISAddListenEndpoint(
IN PATQ_ENDPOINT_CONFIGURATION Configuration,
IN PVOID EndpointContext
);
BOOL
IIS_SERVICE::AssociateInstance(
IN PIIS_SERVER_INSTANCE pInstance
)
/*++
Routine Description:
Associates an instance with an endpoint. It also activates the endpoint.
Arguments:
pInstance - instance to associate.
Return Value:
TRUE - if successful, FALSE otherwise
--*/
{
DWORD err = NO_ERROR;
BOOL shouldStart = FALSE;
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"AssociateInstance %p called\n",
pInstance));
}
//
// Lock the service.
//
AcquireServiceLock( TRUE );
//
// if service is closing, abort
//
if ( !IsActive() ) {
err = ERROR_NOT_READY;
goto exit;
}
if ( pInstance->QueryServerState( ) != MD_SERVER_STATE_STOPPED ) {
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"Server not in stopped (%d) state\n",
pInstance->QueryServerState()));
}
err = ERROR_INVALID_FUNCTION;
goto exit;
}
//
// Start the server instance.
//
shouldStart = TRUE;
err = pInstance->DoStartInstance();
if( err != NO_ERROR ) {
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT, "BindInstance() failed, %lu\n", err));
}
}
exit:
if( shouldStart ) {
//
// We're up and running. Note that the StartInstance() method will
// set the instance state appropriately if successful, so we only
// need to set it if the start failed.
//
if( err != NO_ERROR ) {
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, err );
}
ReleaseServiceLock( TRUE );
return TRUE;
} else {
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"AssociateInstace: Error %d\n",
err));
}
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, err );
ReleaseServiceLock( TRUE );
SetLastError(err);
return FALSE;
}
} // IIS_SERVICE::AssociateInstance
BOOL
IIS_SERVICE::DisassociateInstance(
IN PIIS_SERVER_INSTANCE pInstance
)
/*++
Routine Description:
Removes an instance from an endpoint.
Arguments:
pInstance - instance to associate.
Return Value:
TRUE - if successful, FALSE otherwise
--*/
{
//
// if it's running, stop it
//
AcquireServiceLock( TRUE );
if ( pInstance->QueryServerState( ) == MD_SERVER_STATE_STOPPED ) {
ReleaseServiceLock( TRUE );
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"Cannot disassociate stopped server %p\n",pInstance));
}
return(TRUE);
}
pInstance->SetServerState( MD_SERVER_STATE_STOPPED, NO_ERROR );
ReleaseServiceLock( TRUE );
//
// Blow away any users still clinging to this instance,
// then unbind the instance.
//
pInstance->Reference();
DisconnectUsersByInstance( pInstance );
pInstance->UnbindInstance();
pInstance->Dereference();
return TRUE;
} // IIS_SERVICE::DisassociateInstance
BOOL
IIS_SERVICE::ShutdownService(
VOID
)
/*++
Routine Description:
Shuts down all endpoints.
Arguments:
None.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
//
// Walk the list and close the instances
//
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT, "ShutdownService called\n"));
}
//
// Update the service state.
//
AcquireServiceLock( );
m_state = BlockStateClosed;
ReleaseServiceLock( );
//
// Blow away all instances.
//
DestroyAllServerInstances();
return TRUE;
} // IIS_SERVICE::ShutdownService
IIS_ENDPOINT::IIS_ENDPOINT(
IN PIIS_SERVICE pService,
IN USHORT Port,
IN DWORD IpAddress,
IN BOOL fIsSecure
)
:
m_signature ( IIS_ENDPOINT_SIGNATURE),
m_state ( BlockStateIdle),
m_atqEndpoint ( NULL),
m_isSecure ( fIsSecure),
m_fAtqEpStopped ( FALSE),
m_service ( NULL),
m_reference ( 1),
m_NumQualifiedInstances ( 0),
m_WildcardInstance ( NULL),
m_nAcceptExOutstanding ( 0),
m_AcceptExTimeout ( 0),
m_nInstances ( 0)
{
//
// initialize the lock
//
INITIALIZE_CRITICAL_SECTION(&m_endpointLock);
//
// initialize the association info
//
ZeroMemory(
m_QualifiedInstances,
sizeof(m_QualifiedInstances)
);
//
// reference the service
//
if ( !pService->CheckAndReference( ) ) {
m_state = BlockStateInvalid;
return;
}
m_service = pService;
//
// use the service name as the advertised name
//
m_Port = Port;
m_IpAddress = IpAddress;
#if IE_REF_TRACKING
_pDbgIERefTraceLog = CreateRefTraceLog( C_LOCAL_ENDP_REFTRACES, 0 );
#endif
} // IIS_ENDPOINT::IIS_ENDPOINT
IIS_ENDPOINT::~IIS_ENDPOINT(
VOID
)
{
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"IIS Endpoint %p freed\n",this));
}
DBG_ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
//
// Delete the instance association objects.
//
for( INT qualifier = (INT)FullyQualified ;
qualifier < (INT)NumInstanceQualifiers ;
qualifier++ ) {
delete m_QualifiedInstances[qualifier];
}
//
// Dereference the owning service.
//
if ( m_service != NULL ) {
m_service->Dereference();
m_service = NULL;
}
m_signature = IIS_ENDPOINT_SIGNATURE_FREE;
DeleteCriticalSection(&m_endpointLock);
#if IE_REF_TRACKING
DestroyRefTraceLog( _pDbgIERefTraceLog );
#endif
} // IIS_ENDPOINT::~IIS_ENDPOINT
BOOL
IIS_ENDPOINT::AddInstance(
IN PIIS_SERVER_INSTANCE pInstance,
IN DWORD IpAddress,
IN const CHAR * HostName
)
/*++
Routine Description:
Adds an instance to an existing endpoint.
Arguments:
pInstance - instance to add.
IpAddress - The IP address for this instance; may be INADDR_ANY;.
HostName - The host name for this instance; may be empty ("").
Return Value:
TRUE - if successful, FALSE otherwise
--*/
{
INSTANCE_QUALIFIER qualifier;
DWORD status;
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"IIS_ENDPOINT::AddInstance %p called\n", pInstance));
}
//
// Determine the proper qualifier based on the presence of the
// IP address and host name.
//
qualifier = CalcQualifier( IpAddress, HostName );
LockEndpoint();
//
// Put instance into proper association.
//
if( qualifier == WildcardInstance ) {
if( m_WildcardInstance == NULL ) {
m_WildcardInstance = pInstance;
} else {
DBGPRINTF((
DBG_CONTEXT,
"AddInstance: endpoint %p already has a wildcard instance\n",
this
));
status = ERROR_INVALID_PARAMETER;
goto unlock_and_fail;
}
} else {
PIIS_ASSOCIATION association;
//
// Create a new instance association object if necessary.
//
association = m_QualifiedInstances[qualifier];
if( association == NULL ) {
association = new IIS_ASSOCIATION(
( qualifier == FullyQualified ) ||
( qualifier == QualifiedByIpAddress ),
( qualifier == FullyQualified ) ||
( qualifier == QualifiedByHostName )
);
if( association == NULL ) {
DBGPRINTF((
DBG_CONTEXT,
"AddInstance: cannot create new association\n"
));
status = ERROR_NOT_ENOUGH_MEMORY;
goto unlock_and_fail;
}
m_QualifiedInstances[qualifier] = association;
}
//
// Add the instance to the association.
//
status = association->AddDescriptor(
IpAddress,
HostName,
(LPVOID)pInstance
);
if( status != NO_ERROR ) {
goto unlock_and_fail;
}
//
// Update the number of qualified instances on this endpoint.
// We use this to "short circuit" the instance lookup in the
// common case of a single wildcard instance per endpoint.
//
m_NumQualifiedInstances++;
}
//
// Setup the necessary references, update the server state.
//
Reference();
pInstance->Reference();
InterlockedIncrement( (LPLONG)&m_nInstances );
//
// Aggregate the AcceptEx outstanding parameter.
//
m_nAcceptExOutstanding += pInstance->QueryAcceptExOutstanding();
m_nMaximumAcceptExOutstanding = pInstance->QueryMaxEndpointConnections();
//
// Activate the endpoint if necessary.
//
if ( !ActivateEndpoint() ) {
status = GetLastError();
RemoveInstance( pInstance, IpAddress, HostName );
goto unlock_and_fail;
}
UnlockEndpoint();
return TRUE;
unlock_and_fail:
DBG_ASSERT( status != NO_ERROR );
UnlockEndpoint();
SetLastError( status );
return FALSE;
} // IIS_ENDPOINT::AddInstance
BOOL
IIS_ENDPOINT::RemoveInstance(
IN PIIS_SERVER_INSTANCE pInstance,
IN DWORD IpAddress,
IN const CHAR * HostName
)
/*++
Routine Description:
Removes an instance to an existing endpoint.
Arguments:
pInstance - instance to remove
IpAddress - The IP address for this instance; may be INADDR_ANY;.
HostName - The host name for this instance; may be empty ("").
Return Value:
TRUE - if successful, FALSE otherwise
--*/
{
INSTANCE_QUALIFIER qualifier;
DWORD status;
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"RemoveInstance called endpoint %p instance %p\n",
this, pInstance ));
}
DBG_ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
//
// Determine the proper qualifier based on the presence of the
// IP address and host name.
//
qualifier = CalcQualifier( IpAddress, HostName );
LockEndpoint();
m_nAcceptExOutstanding -= pInstance->QueryAcceptExOutstanding();
if( qualifier == WildcardInstance ) {
DBG_ASSERT( m_WildcardInstance == pInstance );
m_WildcardInstance->Dereference();
m_WildcardInstance = NULL;
} else {
LPVOID Context;
DBG_ASSERT( m_QualifiedInstances[qualifier] != NULL );
status = m_QualifiedInstances[qualifier]->RemoveDescriptor(
IpAddress,
HostName,
&Context
);
if( status == NO_ERROR ) {
DBG_ASSERT( Context == (LPVOID)pInstance );
pInstance->Dereference();
m_NumQualifiedInstances--;
}
}
UnlockEndpoint();
//
// If this was the last instance, then remove ourselves from
// the service's endpoint list and initiate shutdown.
//
if ( InterlockedDecrement( (LPLONG ) &m_nInstances) == 0 ) {
RemoveEntryList( &m_EndpointListEntry );
ShutdownEndpoint( );
}
//
// Remove the reference added in AddInstance().
//
Dereference();
return TRUE;
} // IIS_ENDPOINT::RemoveInstance
BOOL
IIS_ENDPOINT::ActivateEndpoint(
VOID
)
/*++
Routine Description:
Starts an idle endpoint.
Arguments:
None.
Return Value:
TRUE - if successful, FALSE otherwise
--*/
{
PVOID atqEndpoint = NULL;
ATQ_ENDPOINT_CONFIGURATION config;
//
// Make sure this is idle
//
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"Calling activate on %p\n",this));
}
LockEndpoint( );
if ( m_state == BlockStateActive ) {
IF_DEBUG(ENDPOINT) {
DBGPRINTF(( DBG_CONTEXT,
"Activate called on %p is not in idle state(%d)\n",
this, (DWORD)m_state ));
}
DBG_ASSERT(m_atqEndpoint != NULL);
AtqEndpointSetInfo(
m_atqEndpoint,
EndpointInfoAcceptExOutstanding,
min(
m_nAcceptExOutstanding,
m_nMaximumAcceptExOutstanding
)
);
UnlockEndpoint();
return(TRUE);
}
config.cbAcceptExRecvBuffer = m_service->m_cbRecvBuffer;
config.pfnConnect = m_service->m_pfnConnect;
config.pfnConnectEx = m_service->m_pfnConnectEx;
config.pfnIoCompletion = m_service->m_pfnIoCompletion;
config.ListenPort = m_Port;
config.IpAddress = m_IpAddress;
config.nAcceptExOutstanding = min( m_nAcceptExOutstanding,
m_nMaximumAcceptExOutstanding );
config.AcceptExTimeout = m_AcceptExTimeout;
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"%d %d %d\n",
config.ListenPort,
config.nAcceptExOutstanding, config.AcceptExTimeout ));
}
atqEndpoint = I_IISAddListenEndpoint(
&config,
(PVOID)this
);
if ( atqEndpoint == NULL ) {
UnlockEndpoint();
DBGPRINTF(( DBG_CONTEXT,
"Activate failed, error %d\n",GetLastError()));
return(FALSE);
}
//
// Update the state
//
m_state = BlockStateActive;
m_atqEndpoint = atqEndpoint;
Reference( );
UnlockEndpoint();
return(TRUE);
} // IIS_ENDPOINT::ActivateEndpoint
BOOL
IIS_ENDPOINT::StopEndpoint( VOID)
/*++
Routine Description:
Stops the ATQ endpoint structure stored inside the IIS_ENDPOINT
This will prevent us from accepting new connection. This function
should be called only when are preparing ourselves to shut this
endpoint down entirely.
Arguments:
None.
Return Value:
None.
History:
MuraliK 7/8/97
--*/
{
BOOL fReturn = TRUE;
//
// lock, check the state and
// then, stop any Atq Endpoint, if any.
//
LockEndpoint( );
//
// NYI: We mayhave to use an intermediate state for STopped endpoint.
// Unfortunately the state machine usage in IIS_SERVICE/INSTANCE/ENDPOINT
// is not setup for doing so. For now I will just assert that this
// object is in an Active State
// - muralik 7/8/97
//
DBG_ASSERT( m_state == BlockStateActive);
if ( m_atqEndpoint != NULL ) {
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"IIS_ENDPOINT(%08p) Stopping ATQ Endpoint %p\n",
this, m_atqEndpoint));
}
if ( !m_fAtqEpStopped ) {
fReturn = AtqStopEndpoint( m_atqEndpoint);
if ( fReturn ) {
m_fAtqEpStopped = TRUE;
}
}
}
UnlockEndpoint();
return (fReturn);
} // IIS_ENDPOINT::StopEndpoint()
VOID
IIS_ENDPOINT::ShutdownEndpoint(
VOID
)
/*++
Routine Description:
Shuts down an endpoint.
Arguments:
None.
Return Value:
None.
--*/
{
//
// lock, check the state and mark it closed.
// then, shutdown any Atq Endpoint, if any.
//
LockEndpoint( );
DBG_ASSERT(m_nInstances == 0);
if ( m_state == BlockStateActive ) {
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,"Shutting down endpoint %p\n",this));
}
m_state = BlockStateClosed;
UnlockEndpoint();
IF_DEBUG( INSTANCE ) {
DBGPRINTF(( DBG_CONTEXT,
"IIS_ENDPOINT(%08p)::ShutdownEndpoint() for "
" AtqEndpoint %08p\n",
this, m_atqEndpoint ));
}
if ( m_atqEndpoint != NULL ) {
// I replaced AtqStopAndCloseEndpoint() here
if ( !m_fAtqEpStopped ) {
DBG_REQUIRE( AtqStopEndpoint( m_atqEndpoint));
m_fAtqEpStopped = TRUE;
}
/*
* Moved this to IIS_ENDPOINT::Dereference for shutdown thread hack
*
AtqCloseEndpoint( m_atqEndpoint);
m_atqEndpoint = NULL;
*/
}
Dereference( );
} else {
UnlockEndpoint();
}
return;
} // IIS_ENDPOINT::ShutdownEndpoint
PIIS_SERVER_INSTANCE
IIS_ENDPOINT::FindAndReferenceInstance(
IN LPCSTR pszDomainName,
IN const DWORD IPAddress,
OUT LPBOOL pbMaxConnExceeded
)
/*++
Routine Description:
Finds the appropriate instance given domain name and
socket information. The instance is referenced if found.
Arguments:
pszDomainName - Domain name of request.
IPAddress - Local IP Address of the connection.
pbMaxConnExceeded - Receives TRUE if the maximum number of connections
for this endpoint has been exceeded. It is still the caller's
responsibility to properly dispose of the instance.
Return Value:
if successful, returns the pointer to the instance.
NULL, otherwise.
--*/
{
PIIS_SERVER_INSTANCE instance;
DWORD status;
INT qualifier;
IIS_ASSOCIATION::HASH_CONTEXT context;
IF_DEBUG(ENDPOINT) {
LPCSTR tmp = pszDomainName;
if ( tmp == NULL ) {
tmp = "";
}
DBGPRINTF((DBG_CONTEXT,"Finding %s %x\n", tmp, IPAddress));
}
LockEndpoint( );
DBG_CODE( instance = NULL );
if( m_NumQualifiedInstances == 0 ) {
//
// Fast path: only the wildcard instance.
//
instance = m_WildcardInstance;
status = NO_ERROR;
} else {
//
// Less-fast path: we'll need to go hunt for it.
//
if( pszDomainName == NULL ) {
pszDomainName = "";
}
IIS_ASSOCIATION::InitializeHashContext( &context );
for( qualifier = FullyQualified ;
qualifier < NumInstanceQualifiers ;
qualifier++ ) {
if( m_QualifiedInstances[qualifier] != NULL ) {
status = m_QualifiedInstances[qualifier]->LookupDescriptor(
IPAddress,
pszDomainName,
(LPVOID *)&instance,
&context
);
if( status == NO_ERROR ) {
goto FoundInstance;
}
DBG_ASSERT( instance == NULL );
}
}
//
// If we made it this far, then no qualified instances will
// take the request, so use the wildcard (if available).
//
instance = m_WildcardInstance;
//
// Reset the status so that we may continue.
//
status = NO_ERROR;
}
if( instance == NULL ) {
status = ERROR_BAD_NET_NAME;
}
FoundInstance:
if( status == NO_ERROR ) {
DBG_ASSERT( instance != NULL );
if( instance->QueryServerState() != MD_SERVER_STATE_STARTED ) {
status = ERROR_FILE_NOT_FOUND;
}
}
if( status == NO_ERROR ) {
//
// Reference this
//
instance->Reference( );
UnlockEndpoint( );
//
// Make sure that we have not exceeded the max
//
instance->IncrementCurrentConnections();
*pbMaxConnExceeded = ( instance->QueryCurrentConnections() >
instance->QueryMaxConnections() );
if( *pbMaxConnExceeded ) {
IF_DEBUG(ERROR) {
DBGPRINTF((DBG_CONTEXT,
"Too many connected users (%d) max %d, refusing connection\n",
instance->QueryCurrentConnections(),
instance->QueryMaxConnections() ));
}
}
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT,
"Found and referenced instance %p\n",instance));
}
return instance;
}
UnlockEndpoint();
SetLastError( status );
return NULL;
} // IIS_ENDPOINT::FindInstance
PVOID
I_IISAddListenEndpoint(
IN PATQ_ENDPOINT_CONFIGURATION Configuration,
IN PVOID EndpointContext
)
/*++
Description:
Adds a TCPIP ATQ endpoint.
Arguments:
Configuration - contains the endpoint configuration
EndpointContext - context to return during completion
Returns:
TRUE if successful,
FALSE, otherwise
--*/
{
PVOID atqEndpoint;
BOOL fReturn = FALSE;
IF_DEBUG( INSTANCE ) {
DBGPRINTF(( DBG_CONTEXT, "I_IISAddListenEndpoint called\n"));
}
//
// Create the endpoint
//
atqEndpoint = AtqCreateEndpoint(
Configuration,
EndpointContext
);
if ( atqEndpoint == NULL ) {
goto error_exit;
}
//
// Activate the endpoint
//
if ( !AtqStartEndpoint(atqEndpoint) ) {
goto error_exit;
}
return (atqEndpoint);
error_exit:
DWORD dwError = GetLastError();
DBG_ASSERT( NO_ERROR != dwError );
if ( atqEndpoint != NULL ) {
AtqCloseEndpoint( atqEndpoint);
atqEndpoint = NULL;
}
SetLastError(dwError);
return(NULL);
} // I_IISAddListenEndpoint()
VOID
IIS_ENDPOINT::Reference( VOID )
/*++
Routine Description:
Increments the reference count for the endpoint
Arguments:
None
Return Value:
None
--*/
{
InterlockedIncrement( &m_reference );
IE_LOG_REF_COUNT( m_reference );
}
typedef struct _ENDPOINT_HACK_PARAM {
PIIS_ENDPOINT piisEndpoint;
PVOID patqEndpoint;
} ENDPOINT_HACK_PARAM, *PENDPOINT_HACK_PARAM;
VOID
WINAPI
EndpointHackFunc( PVOID pv );
VOID
IIS_ENDPOINT::Dereference( )
/*++
Routine Description:
Decrements the reference count for the endpoint and cleans up if the refcount
reaches zero.
Arguments:
None
Return Value:
None
--*/
{
ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
ASSERT( m_reference > 0 );
//
// Write the trace log BEFORE the decrement operation :(
// If we write it after the decrement, we will run into potential
// race conditions in this object getting freed up accidentally
// by another thread
//
// NOTE we write (_cRef - 1) == ref count AFTER decrement happens
//
LONG cRefsAfter = (m_reference - 1);
IE_LOG_REF_COUNT( cRefsAfter );
if ( InterlockedDecrement( &m_reference ) == 0 ) {
DWORD dwCookie;
PENDPOINT_HACK_PARAM pParam;
DBGPRINTF((DBG_CONTEXT,"deleting endpoint %p\n",this));
if ( m_atqEndpoint != NULL ) {
ASSERT( ((PATQ_ENDPOINT)m_atqEndpoint)->Signature == ATQ_ENDPOINT_SIGNATURE );
//
// Because the ATQ endpoint has an uncounted reference to this object, we can't
// go away until the ATQ endpoint is closed. Since it may be bad to block our
// own thread while waiting for the ATQ endpoint, we create a new thread to do
// it.
//
pParam = new ENDPOINT_HACK_PARAM;
if (pParam == NULL) {
goto threadfail;
}
pParam->piisEndpoint = this;
pParam->patqEndpoint = m_atqEndpoint;
dwCookie = ScheduleWorkItem( EndpointHackFunc, pParam, 0 );
if ( dwCookie == 0 ) {
goto threadfail;
}
} else {
//
// If we couldn't activate the endpoint we will not have an ATQ_ENDPOINT
// to close. In this case we can clean up immediately
//
delete this;
}
} else {
//DBGPRINTF((DBG_CONTEXT,"endpoint deref count %d\n",m_reference));
}
return;
threadfail:
if ( AtqCloseEndpoint( m_atqEndpoint ) ) {
ASSERT( m_signature == IIS_ENDPOINT_SIGNATURE );
delete this;
} else {
//
// There could still be some connections to us, so we can't free the memory.
// However we have to dereference the IIS_SERVICE, which is waiting for us
// during shutdown. Normally this deref occurs during the IIS_ENDPOINT destructor.
//
m_service->Dereference();
m_service = NULL;
DBGPRINTF((DBG_CONTEXT,
"AtqCloseEndpoint returned FALSE! "
"Leaking endpoints: iisEndpoint = %p, atqEndpoint = %p\n",
this, m_atqEndpoint));
}
}
VOID
WINAPI
EndpointHackFunc(
PVOID pv
)
/*++
Routine Description:
This function is a work item scheduled by IIS_ENDPOINT::Dereference. When the IIS_ENDPOINT
refcount hits zero, there is still a reference to the structure from the ATQ_ENDPOINT.
We call AtqCloseEndpoint, which returns when the reference is gone, and then clean up
when it is safe to do so.
Arguments:
pv - a parameter block containing pointers to the IIS_ENDPOINT and it's ATQ_ENDPOINT.
Return Value:
None
--*/
{
PENDPOINT_HACK_PARAM pParam = (PENDPOINT_HACK_PARAM) pv;
ASSERT( pParam->piisEndpoint->CheckSignature(IIS_ENDPOINT_SIGNATURE) );
ASSERT( ((PATQ_ENDPOINT)pParam->patqEndpoint)->Signature == ATQ_ENDPOINT_SIGNATURE );
IF_DEBUG(ENDPOINT) {
DBGPRINTF((DBG_CONTEXT, "EndpointHackFunc: iis=%p atq=%p\n",
pParam->piisEndpoint, pParam->patqEndpoint));
}
//
// if AtqCloseEndpoint fails we can't clean up, because someone could
// still have a pointer to us!
//
if ( AtqCloseEndpoint( pParam->patqEndpoint ) ) {
ASSERT( pParam->piisEndpoint->CheckSignature(IIS_ENDPOINT_SIGNATURE) );
delete pParam->piisEndpoint;
} else {
//
// There could still be some connections to us, so we can't free the memory.
// However we have to dereference the IIS_SERVICE, which is waiting for us
// during shutdown. Normally this deref occurs during the IIS_ENDPOINT destructor.
//
pParam->piisEndpoint->QueryService()->Dereference();
pParam->piisEndpoint->SetService( NULL );
DBGPRINTF((DBG_CONTEXT,
"AtqCloseEndpoint returned FALSE! "
"Leaking endpoints: iisEndpoint = %p, atqEndpoint = %p\n",
pParam->piisEndpoint, pParam->patqEndpoint));
}
delete pParam;
}
BOOL
InitializeEndpointUtilities(
VOID
)
/*++
Routine Description:
Called during infocomm initialization. This sets up a global debug trace
log object
Arguments:
None
Return Value:
TRUE if successful
--*/
{
#if IE_REF_TRACKING
if ( g_pDbgIERefTraceLog == NULL )
{
g_pDbgIERefTraceLog = CreateRefTraceLog( C_IIS_ENDP_REFTRACES, 0 );
return g_pDbgIERefTraceLog != NULL;
}
#endif
return TRUE;
}
BOOL
TerminateEndpointUtilities(
VOID
)
/*++
Routine Description:
Called during infocomm cleanup. This cleans up the global debug trace
log object
Arguments:
None
Return Value:
TRUE if successful
--*/
{
#if IE_REF_TRACKING
if ( g_pDbgIERefTraceLog )
{
DestroyRefTraceLog( g_pDbgIERefTraceLog );
g_pDbgIERefTraceLog = NULL;
}
#endif
return TRUE;
}