// -------------------------------------------------------------------------- // Module Name: APIDispatcher.cpp // // Copyright (c) 1999-2000, Microsoft Corporation // // A class that handles API requests in the server on a separate thread. Each // thread is dedicated to respond to a single client. This is acceptable for // a lightweight server. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "APIDispatcher.h" #include "APIRequest.h" #include "SingleThreadedExecution.h" #include "StatusCode.h" // -------------------------------------------------------------------------- // CAPIDispatcher::CAPIDispatcher // // Arguments: hClientProcess = HANDLE to the client process. // // Returns: // // Purpose: Constructor for CAPIDispatcher. The handle to the client // process is transferred to this object. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- CAPIDispatcher::CAPIDispatcher (HANDLE hClientProcess) : _hSection(NULL), _pSection(NULL), _hProcessClient(hClientProcess), _hPort(NULL), _fRequestsPending(false), _fConnectionClosed(false), _pAPIDispatchSync(NULL) { } // -------------------------------------------------------------------------- // CAPIDispatcher::~CAPIDispatcher // // Arguments: // // Returns: // // Purpose: Destructor for CAPIDispatcher. Release the port handle if // present. Release the process handle. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- CAPIDispatcher::~CAPIDispatcher (void) { ASSERTMSG(_fConnectionClosed, "Destructor invoked without connection being closed in CAPIDispatcher::~CAPIDispatcher"); if (_pSection != NULL) { TBOOL(UnmapViewOfFile(_pSection)); _pSection = NULL; } ReleaseHandle(_hSection); ReleaseHandle(_hPort); ReleaseHandle(_hProcessClient); } // -------------------------------------------------------------------------- // CAPIDispatcher::GetClientProcess // // Arguments: // // Returns: HANDLE // // Purpose: Returns the handle to the client process. This is not // duplicated. DO NOT CLOSE THIS HANDLE. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- HANDLE CAPIDispatcher::GetClientProcess (void) const { return(_hProcessClient); } // -------------------------------------------------------------------------- // CAPIDispatcher::GetClientSessionID // // Arguments: // // Returns: DWORD // // Purpose: Returns the client session ID. // // History: 2000-11-11 vtan created // -------------------------------------------------------------------------- DWORD CAPIDispatcher::GetClientSessionID (void) const { DWORD dwSessionID; ULONG ulReturnLength; PROCESS_SESSION_INFORMATION processSessionInformation; if (NT_SUCCESS(NtQueryInformationProcess(_hProcessClient, ProcessSessionInformation, &processSessionInformation, sizeof(processSessionInformation), &ulReturnLength))) { dwSessionID = processSessionInformation.SessionId; } else { dwSessionID = 0; } return(dwSessionID); } // -------------------------------------------------------------------------- // CAPIDispatcher::SetPort // // Arguments: hPort = Reply port received from // ntdll!NtAcceptConnectionPort. // // Returns: // // Purpose: Sets the given port handle into this object. The handle // ownership is transferred. Wait until the thread processing // requests is ready before returning. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- void CAPIDispatcher::SetPort (HANDLE hPort) { _hPort = hPort; } // -------------------------------------------------------------------------- // CAPIDispatcher::GetSection // // Arguments: // // Returns: HANDLE // // Purpose: Returns a handle to a section used to communicate large // quantities of data from client to server. If the section has // not been created then create it. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- HANDLE CAPIDispatcher::GetSection (void) { if (_hSection == NULL) { TSTATUS(CreateSection()); } return(_hSection); } // -------------------------------------------------------------------------- // CAPIDispatcher::GetSectionAddress // // Arguments: // // Returns: void* // // Purpose: Returns the mapped address of the section. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- void* CAPIDispatcher::GetSectionAddress (void) const { return(_pSection); } // -------------------------------------------------------------------------- // CAPIDispatcher::CloseConnection // // Arguments: // // Returns: NTSTATUS // // Purpose: Sets the member variable indicating the dispatcher's port has // been closed and that any pending requests are now invalid. // The object is reference counted so if there are any pending // requests they will release their reference when they're done. // The caller of this function releases its reference. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // 2000-11-08 vtan reference counted object // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::CloseConnection (void) { CSingleThreadedExecution requestsPendingLock(_lock); _fConnectionClosed = true; return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CAPIDispatcher::QueueRequest // // Arguments: portMessage = CPortMessage of request. // // Returns: NTSTATUS // // Purpose: Checks if the connection has been closed. If closed then it // rejects the request. Otherwise it queues it. // // History: 2000-12-02 vtan created // 2002-03-21 scotthan Copy APIDispatchSync address, // update count of queued requests // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::QueueRequest (const CPortMessage& portMessage, CAPIDispatchSync* pAPIDispatchSync) { NTSTATUS status; if (_fConnectionClosed) { status = RejectRequest(portMessage, STATUS_PORT_DISCONNECTED); } else { // Note: we should receive one and the same CAPIDispatchSync address for the lifetime // of this CAPIDispatcher instance. We do not own this pointer, but only maintain a copy. #ifdef DEBUG if( NULL == _pAPIDispatchSync ) { #endif DEBUG _pAPIDispatchSync = pAPIDispatchSync; #ifdef DEBUG } else { ASSERTBREAKMSG(pAPIDispatchSync == _pAPIDispatchSync, "CAPIDispatcher::QueueRequest - invalid APIDispatchSync"); } #endif DEBUG // track this request queue. CAPIDispatchSync::DispatchEnter(_pAPIDispatchSync); status = CreateAndQueueRequest(portMessage); // on failure to queue the request, remove counter reference. if( !NT_SUCCESS(status) ) { CAPIDispatchSync::DispatchLeave(_pAPIDispatchSync); } } return(status); } // -------------------------------------------------------------------------- // CAPIDispatcher::ExecuteRequest // // Arguments: portMessage = CPortMessage of request. // // Returns: NTSTATUS // // Purpose: Checks if the connection has been closed. If closed then it // rejects the request. Otherwise it executes it. // // History: 2000-12-02 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::ExecuteRequest (const CPortMessage& portMessage) { NTSTATUS status; if (_fConnectionClosed) { status = RejectRequest(portMessage, STATUS_PORT_DISCONNECTED); } else { status = CreateAndExecuteRequest(portMessage); } return(status); } // -------------------------------------------------------------------------- // CAPIDispatcher::RejectRequest // // Arguments: portMessage = CPortMessage of request. // // Returns: NTSTATUS // // Purpose: Sends back a reply to the caller STATUS_PORT_DISCONNECTED to // reject the request. // // History: 2000-12-02 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::RejectRequest (const CPortMessage& portMessage, NTSTATUS status) const { CPortMessage portMessageOut(portMessage); // Send the message back to the client. portMessageOut.SetDataLength(sizeof(NTSTATUS)); portMessageOut.SetReturnCode(status); return(SendReply(portMessageOut)); } // -------------------------------------------------------------------------- // CAPIDispatcher::Entry // // Arguments: // // Returns: // // Purpose: Main entry point for processing LPC requests. If there are // pending requests in the queue pick them off and process them. // While processing them more items can get queued. Keep // processing until there are no more queued items. There is a // possible overlap where a newly queued item can be missed. In // that case a new work item is queued to execute those requests. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // 2002-03-21 scotthan Add queue synchronization via CAPIDispatchSynch. // -------------------------------------------------------------------------- void CAPIDispatcher::Entry (void) { CAPIRequest *pAPIRequest; // artificial addref our dispatch sync to prevent us from signalling prematurely, // before we can safely allow ourselves to be destroyed by our parent APIConnection. CAPIDispatchSync::DispatchEnter(_pAPIDispatchSync); // Acquire the requests pending lock before fetching the first // request. This will ensure an accurate result. _lock.Acquire(); pAPIRequest = static_cast(_queue.Get()); // If there are more requests in the queue keep looping. while (pAPIRequest != NULL) { // Release the requests pending lock to allow more requests to // get queued to this dispatcher while the dispatch is executing. if (!_fConnectionClosed) { NTSTATUS status; // Before executing the API request release the lock to allow // more requests to get queued while executing this one. _lock.Release(); // Execute the request. status = Execute(pAPIRequest); // Acquire the requests pending lock again before getting // the next available request. If the loop continues the // lock will be released at the top of the loop. If the loop // exits then the lock must be released outside. _lock.Acquire(); // On debug builds ignore STATUS_REPLY_MESSAGE_MISMATCH. // This typically happens on stress machines where timing // causes the thread waiting on the reply to go away before // the service has a chance to reply to the LPC request. #ifdef DEBUG if (!_fConnectionClosed && !ExcludedStatusCodeForDebug(status)) { TSTATUS(status); } #endif /* DEBUG */ } // Remove this processed request. _queue.Remove(); // Decrement dispatch sync object. The matching DispatchEnter() // took place at time of queuing, in CAPIDispatcher::QueueRequest(). CAPIDispatchSync::DispatchLeave(_pAPIDispatchSync); // Get the next request. A request may have been queued while // processing the request just processed. So keep looping until // there really are no requests left. pAPIRequest = static_cast(_queue.Get()); } // Set the state to no longer processing requests so that any // further queued requests will cause the dispatcher to be // re-invoked in a new worker thread. Release the lock. _fRequestsPending = false; _lock.Release(); // remove defensive addref CAPIDispatchSync::DispatchLeave(_pAPIDispatchSync); } // -------------------------------------------------------------------------- // CAPIDispatcher::Execute // // Arguments: pAPIRequest = API request to execute. // // Returns: NTSTATUS // // Purpose: Execute the API request. This can be done from a queued work // item executing on a different thread or execute in the server // port listen thread. // // History: 2000-10-19 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::Execute (CAPIRequest *pAPIRequest) const { NTSTATUS status; // Set the return data size to NTSTATUS by default. Execute the // request. Store the result. If the executed function has more // data to return it will set the size itself. pAPIRequest->SetDataLength(sizeof(NTSTATUS)); // Protect the execution with an exception block. If the code // throws an exception it would normally just kill the worker // thread. However, the CAPIDispatcher would be left in a state // where it was marked as still executing requests even though // the thread died. If an exception is thrown the function is // considered unsuccessful. __try { status = pAPIRequest->Execute(_pAPIDispatchSync); } __except (DispatcherExceptionFilter(GetExceptionInformation())) { status = STATUS_UNSUCCESSFUL; } pAPIRequest->SetReturnCode(status); // Reply to the client with the result. return(SendReply(*pAPIRequest)); } // -------------------------------------------------------------------------- // CAPIDispatcher::CreateSection // // Arguments: // // Returns: NTSTATUS // // Purpose: Overridable function that creates a section object. Because // size is not determinable it can be inheritable. // // The default implementation does nothing. // // History: 2000-10-10 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::CreateSection (void) { return(STATUS_NOT_IMPLEMENTED); } // -------------------------------------------------------------------------- // CAPIDispatcher::SignalRequestPending // // Arguments: // // Returns: NTSTATUS // // Purpose: Signals the event to wake up the thread processing requests. // // History: 1999-11-07 vtan created // 2000-08-25 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::SignalRequestPending (void) { NTSTATUS status; CSingleThreadedExecution requestsPendingLock(_lock); // Only check the validity of _fRequestsPending after acquiring the // lock. This will guarantee that the value of this variable is // 100% correct in a multi worker threaded environment. if (!_fRequestsPending) { _fRequestsPending = true; status = Queue(); } else { status = STATUS_SUCCESS; } return(status); } // -------------------------------------------------------------------------- // CAPIDispatcher::SendReply // // Arguments: portMessage = CPortMessage to send in the reply. // // Returns: NTSTATUS // // Purpose: Sends a reply to the LPC port so the caller can be unblocked. // // History: 2000-10-19 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIDispatcher::SendReply (const CPortMessage& portMessage) const { return(NtReplyPort(_hPort, const_cast(portMessage.GetPortMessage()))); } #ifdef DEBUG // -------------------------------------------------------------------------- // CAPIDispatcher::ExcludedStatusCodeForDebug // // Arguments: status = NTSTATUS code to check. // // Returns: bool // // Purpose: Returns whether this status code should be ignored on asserts. // // History: 2001-03-30 vtan created // -------------------------------------------------------------------------- bool CAPIDispatcher::ExcludedStatusCodeForDebug (NTSTATUS status) { return((status == STATUS_REPLY_MESSAGE_MISMATCH) || (status == STATUS_INVALID_CID)); } #endif /* DEBUG */ // -------------------------------------------------------------------------- // CAPIDispatcher::DispatcherExceptionFilter // // Arguments: // // Returns: LONG // // Purpose: Filters exceptions that occur when dispatching API requests. // // History: 2000-10-13 vtan created // -------------------------------------------------------------------------- LONG WINAPI CAPIDispatcher::DispatcherExceptionFilter (struct _EXCEPTION_POINTERS *pExceptionInfo) { (LONG)RtlUnhandledExceptionFilter(pExceptionInfo); return(EXCEPTION_EXECUTE_HANDLER); } // -------------------------------------------------------------------------- // CAPIDispatchSync impl // History: 2002-03-18 scotthan created. // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- CAPIDispatchSync::CAPIDispatchSync() : _cDispatches(0) , _hServiceStopping(NULL) , _hZeroDispatches(NULL) , _hPortShutdown(NULL) , _hServiceControlStop(NULL) { if( !InitializeCriticalSectionAndSpinCount(&_cs, 0) ) { ZeroMemory(&_cs, sizeof(_cs)); } _hServiceStopping = CreateEvent(NULL, TRUE /* manual-rest */, FALSE /* unsignalled */, NULL); _hZeroDispatches = CreateEvent(NULL, TRUE /* manual-rest */, TRUE /* signalled */, NULL); _hPortShutdown = CreateEvent(NULL, FALSE /* auto-reset */, FALSE /* unsignalled */, NULL); _hServiceControlStop = CreateEvent(NULL, FALSE /* auto-reset */, FALSE /* unsignalled */, NULL); } // -------------------------------------------------------------------------- CAPIDispatchSync::~CAPIDispatchSync() { HANDLE h; h = _hServiceStopping; _hServiceStopping = NULL; if( h ) { CloseHandle(h); } h = _hZeroDispatches; _hZeroDispatches = NULL; if( h ) { CloseHandle(h); } h = _hPortShutdown; _hPortShutdown = NULL; if( h ) { CloseHandle(h); } h = _hServiceControlStop; _hServiceControlStop = NULL; if( h ) { CloseHandle(h); } if( _cs.DebugInfo ) { DeleteCriticalSection(&_cs); } } // -------------------------------------------------------------------------- void CAPIDispatchSync::SignalServiceStopping(CAPIDispatchSync* pds) { if( pds ) { pds->Lock(); if( pds->_hServiceStopping ) { SetEvent(pds->_hServiceStopping); } pds->Unlock(); } } // -------------------------------------------------------------------------- BOOL CAPIDispatchSync::IsServiceStopping(CAPIDispatchSync* pds) { BOOL fRet = FALSE; if( pds ) { if( pds->_hServiceStopping ) { fRet = (WaitForSingleObject(pds->_hPortShutdown, 0) != WAIT_TIMEOUT); } } return fRet; } // -------------------------------------------------------------------------- HANDLE CAPIDispatchSync::GetServiceStoppingEvent(CAPIDispatchSync* pds) { return pds ? pds->_hServiceStopping : NULL; } // -------------------------------------------------------------------------- void CAPIDispatchSync::DispatchEnter(CAPIDispatchSync* pds) { if( pds ) { pds->Lock(); if( (++(pds->_cDispatches) > 0) && pds->_hZeroDispatches ) { ResetEvent(pds->_hZeroDispatches); } pds->Unlock(); } } // -------------------------------------------------------------------------- void CAPIDispatchSync::DispatchLeave(CAPIDispatchSync* pds) { if( pds ) { pds->Lock(); if( (--(pds->_cDispatches) == 0) && pds->_hZeroDispatches ) { SetEvent(pds->_hZeroDispatches); } ASSERTMSG(pds->_cDispatches >= 0, "CAPIDispatchSync::Leave - refcount < 0: Mismatched Enter/Leave"); pds->Unlock(); } } // -------------------------------------------------------------------------- DWORD CAPIDispatchSync::WaitForZeroDispatches(CAPIDispatchSync* pds, DWORD dwTimeout) { if( pds ) { if( pds->_hZeroDispatches ) { return WaitForSingleObject(pds->_hZeroDispatches, dwTimeout); } } return WAIT_ABANDONED; } // -------------------------------------------------------------------------- void CAPIDispatchSync::SignalPortShutdown(CAPIDispatchSync* pds) { if( pds ) { pds->Lock(); if( pds->_hPortShutdown ) { SetEvent(pds->_hPortShutdown); } pds->Unlock(); } } // -------------------------------------------------------------------------- DWORD CAPIDispatchSync::WaitForPortShutdown(CAPIDispatchSync* pds, DWORD dwTimeout) { if( pds ) { if( pds->_hPortShutdown ) { return WaitForSingleObject(pds->_hPortShutdown, dwTimeout); } } return WAIT_ABANDONED; } // -------------------------------------------------------------------------- void CAPIDispatchSync::SignalServiceControlStop(CAPIDispatchSync* pds) { if( pds ) { pds->Lock(); if( pds->_hServiceControlStop ) { SetEvent(pds->_hServiceControlStop); } pds->Unlock(); } } // -------------------------------------------------------------------------- DWORD CAPIDispatchSync::WaitForServiceControlStop(CAPIDispatchSync* pds, DWORD dwTimeout) { if( pds ) { if( pds->_hServiceControlStop ) { return WaitForSingleObject(pds->_hServiceControlStop, dwTimeout); } } return WAIT_ABANDONED; } // -------------------------------------------------------------------------- void CAPIDispatchSync::Lock() { if( _cs.DebugInfo ) { EnterCriticalSection(&_cs); } } // -------------------------------------------------------------------------- void CAPIDispatchSync::Unlock() { if( _cs.DebugInfo ) { LeaveCriticalSection(&_cs); } }