|
|
/*++
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_
|