windows-nt/Source/XPSP1/NT/inetsrv/iis/staxinc/mtlib.h
2020-09-26 16:20:57 +08:00

797 lines
16 KiB
C++

/*++
This header file exists to provide templates which support
mutlithreaded software servers.
--*/
#include <windows.h>
#include "lockq.h"
#include "smartptr.h"
#ifndef _MTLIB_H_
#define _MTLIB_H_
class CStateStackInterface : public CRefCount {
//
// This class defines a pure virtual interface used
// to allocate memory for CCallState objects.
//
public :
enum STACK_CONSTANTS {
MAX_STACK_RESERVE = 4096
} ;
//
// Initialize the stack !
//
virtual BOOL
Init( DWORD cbReserve ) = 0 ;
//
// Allocate bytes for the object
//
virtual LPVOID
Allocate( DWORD cb ) = 0 ;
//
// Release the bytes associated with the object
//
virtual BOOL
Release( LPVOID lpvState ) = 0 ;
//
// Provide virtual do nothing destructor.
//
virtual
~CStateStackInterface() {
}
} ;
typedef CRefPtr< CStateStackInterface > STACKPTR ;
//
// This is just a definition of this symbol which
// is used to implement these stack objects but otherwise
// is hidden from clients !
//
class CStateStackBase ;
//
// A Smart Pointer TYPE for CStateStackBase objects !
//
typedef CRefPtr< CStateStackBase > STACKBPTR ;
class CStateStack : public CStateStackInterface {
/*++
This is the object that users should instantiate when
they are ready to be allocating memory from these stack objects !
--*/
private :
//
// Signature for recognizing these stacks in memory
//
DWORD m_dwSignature ;
//
// Points to the object we delegate all operations to.
// This is really the top of a stack of such objects !
//
STACKBPTR m_pImp ;
//
// Push a CStateStackBase derived object onto our stack
// that can handle the next allocation !
//
BOOL
NewStack( DWORD cbReserve ) ;
//
// Copy construction not allowed !
//
CStateStack( CStateStack& ) ;
public :
//
// We have do nothing Constructor and Destructors - however
// we declare and define them so that the STACKBPTR type
// is only referenced in the mtserver library.
//
CStateStack() ;
~CStateStack() ;
//
// Prepare the initial stack
//
BOOL
Init( DWORD cbReserve ) ;
//
// Allocate bytes for the object
//
LPVOID
Allocate( DWORD cb ) ;
//
// Release the bytes associated with the object
//
BOOL
Release( LPVOID lpvState ) ;
} ;
class CCall : public CQElement {
//
// This class defines a base class for Call Objects created with
// the TMFnCallAndCompletion objects.
// This base class will provide for memory management of Call Objects.
//
private :
//
// Do Nothing Constructor is private - everybody has
// to provide args to constructor !
//
CCall() ;
//
// Signature for these things !
//
DWORD m_dwSignature ;
//
// This is a reference to the allocator that should be used
// to create all Child CCallState objects of this one !
//
CStateStackInterface& m_Allocator ;
protected :
//
// The only method we provide for retrieving the allocator !
//
CStateStackInterface&
Allocator() {
return m_Allocator ;
}
public:
//
// The only constructor we provide REQUIRES an allocator,
// we don't take a pointer as that would let you pass a NULL!
//
CCall( CStateStackInterface& allocator ) :
m_dwSignature( DWORD('laCC') ),
m_Allocator( allocator ) {
#ifdef DEBUG
LPVOID lpv = this ;
_ASSERT( *((CStateStackInterface**)lpv) = &allocator ) ;
#endif
}
//
// Allocate a CCallState object !
//
void*
operator new( size_t size, CStateStackInterface& stack ) {
size += sizeof( CStateStackInterface* ) ;
LPVOID lpv = stack.Allocate( size ) ;
if( lpv != 0 ) {
*((CStateStackInterface**)lpv) = &stack ;
lpv = (LPVOID)(((CStateStackInterface**)lpv)+1) ;
}
return lpv ;
}
//
// Users are required to not use this version of operator new! -
// The language doesn't let us hide it - so it will DebugBreak()
// at runtime if necessary !
//
void*
operator new( size_t size ) {
DebugBreak() ;
return 0 ;
}
//
// Free a CCallState derived object !
//
void
operator delete( void* lpv ) {
if( lpv != 0 ) {
CStateStackInterface* pStack = ((CStateStackInterface**)lpv)[-1] ;
lpv = (LPVOID)(((CStateStackInterface**)lpv)-1) ;
pStack->Release( lpv ) ;
}
}
} ;
template< class RESULT >
class TCompletion {
//
// This template defines an interface for Completion
// objects which have a Virtual Function taking a
// particular kind of result.
//
public :
//
// One pure virtual function which gets the results !
//
virtual void
Complete( RESULT& result ) = 0 ;
//
// This function is called when the request is unable to be
// completed for whatever reason (most likely, we're in a
// shutdown state.)
//
virtual void
ErrorComplete( DWORD dwReserved ) = 0 ;
void*
operator new( size_t size, CStateStackInterface& stack ) {
size += sizeof( CStateStackInterface* ) ;
LPVOID lpv = stack.Allocate( size ) ;
if( lpv != 0 ) {
*((CStateStackInterface**)lpv) = &stack ;
lpv = (LPVOID)(((CStateStackInterface**)lpv)+1) ;
}
return lpv ;
}
//
// Free a CCallState derived object !
//
void
operator delete( void* lpv ) {
if( lpv != 0 ) {
CStateStackInterface* pStack = ((CStateStackInterface**)lpv)[-1] ;
lpv = (LPVOID)(((CStateStackInterface**)lpv)-1) ;
pStack->Release( lpv ) ;
}
}
} ;
template< class SERVER >
class ICall : public CCall {
//
// This template defines the interface to CCallState objects
// that are to operate against SERVER's of type 'SERVER'.
//
// We define 2 virtual functions - ExecuteCall and CompleteCall.
// This template only exists to provide a base class definition
// for objects which are passed into the TMtService< SERVER >
// QueueRequest function.
//
// Derived Objects must manage their own destruction in a
// fashion which guarantees they are destroyed BEFORE the
// caller is notified of the completion. This is because CCallState
// objects are created with a memory manager that is managed
// by the caller, which won't be in any other threads untill the
// completion is called.
//
protected:
//
// Only derived classes are allowed to construct these things !
//
ICall( CStateStackInterface& allocator ) :
CCall( allocator ) {}
public :
typedef CStateStackInterface INITIALIZER ;
//
// A return value of TRUE means that the CCallState object
// should be kept for a later call to CompleteCall
//
virtual BOOL
ExecuteCall( SERVER& server ) = 0 ;
//
// This function is only called if ExecuteCall() return TRUE -
// indicating that all the thread safe aspects of the Execution
// had been completed, and that the CCallState could be called
// again to notify the original caller of the results.
//
virtual void
CompleteCall( SERVER& server ) = 0 ;
//
// This function is called when we won't be given a change to
// execute our request. This occurs during shutdown for instance.
// dwReserved is for future use (i.e. indicate error conditions.)
//
virtual void
CancelCall( DWORD dwReserved ) = 0 ;
} ;
template< class SERVER,
class RESULT,
class ARGUMENT,
class BASECLASS = ICall<SERVER>
>
class TMFnCall : public BASECLASS {
//
// This defines a call state object which can be passed to a TMtService<SERVER,BASECLASS>
// The call object holds onto the arguments to which should be passed to the
// member function of the server.
//
// This object will call the member function and imediately complete the operation !
//
public :
//
// Define the signature of the member function we will call.
//
// The member function must not have any return value.
//
typedef void (SERVER::*FUNC_SIGNATURE)( ARGUMENT, RESULT& ) ;
//
// Define the signature of the object which will handle the completion
// of the Async Call.
//
typedef TCompletion<RESULT> COMPLETION_OBJECT ;
private :
//
// Pointer to a member function of the server
//
FUNC_SIGNATURE m_pfn ;
//
// An argument for the member function of the server.
//
ARGUMENT m_Arg ;
//
// The object we will notify when the call completes !
//
COMPLETION_OBJECT* m_pCompletion ;
public :
//
// Construct a TMFnCallAndCompletion object.
// We require a pointer to a function and a pointer
// to a Completion object.
//
//
TMFnCall(
BASECLASS::INITIALIZER& baseInit,
FUNC_SIGNATURE pfn,
ARGUMENT arg,
COMPLETION_OBJECT* pCompletion
) :
BASECLASS( baseInit ),
m_pfn( pfn ),
m_Arg( arg ),
m_pCompletion( pCompletion ) {
}
TMFnCall(
FUNC_SIGNATURE pfn,
ARGUMENT arg,
COMPLETION_OBJECT* pCompletion
) :
m_pfn( pfn ),
m_Arg( arg ),
m_pCompletion( pCompletion ) {
}
//
// Execute the call !
//
//
BOOL
ExecuteCall( SERVER& server ) {
RESULT results ;
(server.*m_pfn)( m_Arg, results ) ;
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->Complete( results ) ;
return FALSE ;
}
//
// Do nothing - no delayed completions
//
virtual void
CompleteCall( SERVER& server ) {
}
//
// Notify the caller that the call will not be executed.
//
void
CancelCall( DWORD dwReserved ) {
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->ErrorComplete() ;
}
} ;
template< class SERVER,
class RESULT,
class ARGUMENT,
class BASECLASS = ICall<SERVER>
>
class TMFnDelay : public BASECLASS {
//
// This defines a call state object which can be passed to a TMtService<SERVER>
// The call object holds onto the arguments to which should be passed to the
// member function of the server.
//
// The main property of this object is that it can postpone calling the
// callers completion object until the worker thread is out of its critical
// region !
//
public :
//
// Define the signature of the member function we will call.
// NOTE : the function takes references to the values !!
//
typedef BOOL (SERVER::*FUNC_SIGNATURE)( ARGUMENT&, RESULT& ) ;
//
// Define the signature of the object which will handle the completion
// of the Async Call.
//
typedef TCompletion<RESULT> COMPLETION_OBJECT ;
private :
//
// Pointer to a member function of the server
//
FUNC_SIGNATURE m_pfn ;
//
// An argument for the member function of the server.
//
ARGUMENT m_Arg ;
//
// Temporary to hold the results of the function
//
RESULT m_Result ;
//
// Pointer to the object which gets notified of the async completion.
//
COMPLETION_OBJECT* m_pCompletion ;
public :
TMFnDelay(
BASECLASS::INITIALIZER& baseinit,
FUNC_SIGNATURE pfn,
ARGUMENT arg,
COMPLETION_OBJECT* pCompletion
) :
BASECLASS( baseinit ),
m_pfn( pfn ),
m_Arg( arg ),
m_pCompletion( pCompletion ) {
}
TMFnDelay(
FUNC_SIGNATURE pfn,
ARGUMENT arg,
COMPLETION_OBJECT* pCompletion
) :
m_pfn( pfn ),
m_Arg( arg ),
m_pCompletion( pCompletion ) {
}
//
// Execute the call !
//
// If we return TRUE, then this object will be
// put into a queue for a later call to CompletCall()
//
//
BOOL
ExecuteCall( SERVER& server ) {
if( (server.*m_pfn)( m_Arg, m_Result ) ) {
return TRUE ;
}
RESULT results = m_Result ;
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->Complete( results ) ;
return FALSE ;
}
//
// Destroy ourselves before invoking the completion object !
//
virtual void
CompleteCall( SERVER& server ) {
RESULT results = m_Result ;
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->Complete( results ) ;
}
//
// Notify the caller that the call will not be executed.
//
void
CancelCall( DWORD dwReserved ) {
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->ErrorComplete( dwReserved ) ;
}
} ;
template< class SERVER,
class RESULT,
class BASECLASS = ICall< SERVER >
>
class TMFnNoArgDelay : public BASECLASS {
//
// This defines a call state object which can be passed to a TMtService<SERVER>
// The call object holds onto the arguments to which should be passed to the
// member function of the server.
//
public :
//
// Define the signature of the member function we will call.
// NOTE : the function takes references to the values !!
//
typedef BOOL (SERVER::*FUNC_SIGNATURE)( RESULT& ) ;
//
// Define the signature of the object which will handle the completion
// of the Async Call.
//
typedef TCompletion<RESULT> COMPLETION_OBJECT ;
private :
//
// Pointer to a member function of the server
//
FUNC_SIGNATURE m_pfn ;
//
// Temporary to hold the results of the function
//
RESULT m_Result ;
//
// Pointer to the object which gets notified of the async completion.
//
COMPLETION_OBJECT* m_pCompletion ;
public :
TMFnNoArgDelay(
BASECLASS::INITIALIZER& baseInit,
FUNC_SIGNATURE pfn,
COMPLETION_OBJECT* pCompletion
) :
BASECLASS( baseInit ),
m_pfn( pfn ),
m_pCompletion( pCompletion ) {
}
TMFnNoArgDelay(
FUNC_SIGNATURE pfn,
COMPLETION_OBJECT* pCompletion
) :
BASECLASS( baseInit ),
m_pfn( pfn ),
m_pCompletion( pCompletion ) {
}
//
// Execute the call !
//
// If we return TRUE, then this object will be
// put into a queue for a later call to CompletCall()
//
//
BOOL
ExecuteCall( SERVER& server ) {
if( (server.*m_pfn)( m_Result ) ) {
return TRUE ;
}
RESULT results = m_Result ;
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->Complete( results ) ;
return FALSE ;
}
//
// Destroy ourselves before invoking the completion object !
//
virtual void
CompleteCall( SERVER& server ) {
RESULT results = m_Result ;
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->Complete( results ) ;
}
//
// Notify the caller that the call will not be executed.
//
void
CancelCall( DWORD dwReserved ) {
COMPLETION_OBJECT* pCompletion = m_pCompletion ;
delete this ;
if( pCompletion )
pCompletion->ErrorComplete( dwReserved ) ;
}
} ;
template< class SERVER,
class CALLBASE = ICall< SERVER > >
class TMtService {
//
// This template defines the class that manages the
// multithreading issues for objects of type 'SERVER'
//
public :
typedef CALLBASE CALL_OBJECT ;
private :
//
// Queue of requests from different clients.
//
TLockQueue< CALL_OBJECT > m_PendingCalls ;
///
// Is somebody trying to shut us down ?
//
BOOL m_fShutdown ;
//
// The Server object that will be provided to the call objects !
//
SERVER& m_Server ;
//
// Can't construct us without providing all of our parameters !
//
TMtService() ;
public :
//
// Initialize us !
//
TMtService( SERVER& server ) :
m_Server( server ),
m_fShutdown( FALSE ) {
}
//
// Queue a request to be executed !
//
void
QueueRequest( CALL_OBJECT* pcallobj ) {
//
// Use this to keep a stack of Calls that we did not
// immediately complete !
//
CALL_OBJECT* pDelayedCompletion = 0 ;
//
// If Append returns FALSE then another thread will service the request !
//
if( m_PendingCalls.Append( pcallobj ) ) {
//
// We're the first thread in - still may be no work to do though -
// Try to get a CALL_OBJECT to service.
//
while( (pcallobj = m_PendingCalls.RemoveAndRelease()) != 0 ) {
//
// If we're shutting down then we don't execute requests -
// just fail them.
//
if( m_fShutdown ) {
pcallobj->CancelCall( 0 ) ;
} else {
//
// If Execute Call returns
//
if( pcallobj->ExecuteCall( m_Server ) ) {
pcallobj->m_pNext = pDelayedCompletion ;
pDelayedCompletion = pcallobj ;
}
}
}
//
// At this point, other threads may be executing in the service
// and we are only giving those CALL_OBJECTS who can deal with
// it a chance to notify their invokers.
//
// NOTE : pDelayedCompletion is basically a stack - get better
// value out of the CPU Cache that way.
//
while( pDelayedCompletion != 0 ) {
pcallobj = (CALL_OBJECT*)pDelayedCompletion->m_pNext ;
pDelayedCompletion->m_pNext = 0 ;
pDelayedCompletion->CompleteCall( m_Server ) ;
pDelayedCompletion = pcallobj ;
}
}
}
} ;
#endif // _MTLIB_H_