//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: E V E N T Q . C P P // // Contents: Event Queue for managing synchonization of external events. // // Notes: // // Author: ckotze 29 Nov 2000 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "ncmisc.h" #include "eventq.h" #include "conman.h" //+--------------------------------------------------------------------------- // // Function: Constructor for CEventQueue // // Purpose: Creates the various synchronization objects required for the // Queue // Arguments: // HANDLE hServiceShutdown [in] // Event to set when shutting down queue. // // // Returns: nothing. // // Author: ckotze 30 Nov 2000 // // Notes: // // // CEventQueue::CEventQueue(HANDLE hServiceShutdown) : m_hServiceShutdown(0), m_pFireEvents(NULL), m_hWait(0), m_fRefreshAllInQueue(FALSE) { TraceFileFunc(ttidEvents); NTSTATUS Status; try { if (!DuplicateHandle(GetCurrentProcess(), hServiceShutdown, GetCurrentProcess(), &m_hServiceShutdown, NULL, FALSE, DUPLICATE_SAME_ACCESS)) { TraceTag(ttidEvents, "Couldn't Duplicate handle!"); throw E_OUTOFMEMORY; } HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_pFireEvents = new CEvent(hEvent); Status = RtlRegisterWait(&m_hWait, hEvent, (WAITORTIMERCALLBACKFUNC) DispatchEvents, NULL, INFINITE, WT_EXECUTEDEFAULT); if (!NT_SUCCESS(Status)) { throw E_OUTOFMEMORY; } TraceTag(ttidEvents, "RtlRegisterWait Succeeded"); InitializeCriticalSection(&m_csQueue); } catch (...) { if (m_hWait && NT_SUCCESS(Status)) { RtlDeregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE); } // ISSUE: If CreateEvent succeeds and new CEvent fails, we're not freeing the hEvent. if (m_pFireEvents) { delete m_pFireEvents; } if (m_hServiceShutdown) { CloseHandle(m_hServiceShutdown); } throw; } } //+--------------------------------------------------------------------------- // // Function: Destructor for CEventQueue // // Purpose: Empties the queue and frees all existing items in the queue. // // Arguments: // // // // // Returns: nothing. // // Author: ckotze 30 Nov 2000 // // Notes: // // // CEventQueue::~CEventQueue() { NTSTATUS Status; while(!m_eqWorkItems.empty()) { USERWORKITEM UserWorkItem; UserWorkItem = m_eqWorkItems.front(); m_eqWorkItems.pop_front(); if (UserWorkItem.EventMgr == EVENTMGR_CONMAN) { CONMAN_EVENT* pConmanEvent = reinterpret_cast(UserWorkItem.Event); FreeConmanEvent(pConmanEvent); } } DeleteCriticalSection(&m_csQueue); Status = RtlDeregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE); delete m_pFireEvents; CloseHandle(m_hServiceShutdown); } //+--------------------------------------------------------------------------- // // Function: EnqueueEvent // // Purpose: Stores the new event in the Event Queue // // Arguments: // Function - The pointer to the function to be called when firing the // event // Event - The Event information // Flags - Unused, just for compatibility with QueueUserWorkItem calls // // Returns: HRESULT // S_OK - Event has been added and Event code is // already dispatching events // S_FALSE - Event has been added to Queue, but a // thread needs to be scheduled to fire // the events // E_OUTOFMEMORY - Unable to add the event to the Queue. // // Author: ckotze 30 Nov 2000 // // Notes: Locks and Unlocks the critical section only while working // with the queue // // HRESULT CEventQueue::EnqueueEvent(IN LPTHREAD_START_ROUTINE Function, IN PVOID pEvent, IN const EVENT_MANAGER EventMgr) { TraceFileFunc(ttidEvents); CExceptionSafeLock esLock(&m_csQueue); USERWORKITEM UserWorkItem; HRESULT hr = S_OK; if (!Function) { return E_POINTER; } if (!pEvent) { return E_POINTER; } UserWorkItem.Function = Function; UserWorkItem.Event = pEvent; UserWorkItem.EventMgr = EventMgr; if (EVENTMGR_CONMAN == EventMgr) { CONMAN_EVENT* pConmanEvent = reinterpret_cast(pEvent); if (REFRESH_ALL == pConmanEvent->Type) { if (!m_fRefreshAllInQueue) { m_fRefreshAllInQueue = TRUE; } else { FreeConmanEvent(pConmanEvent); return S_OK; } } } #ifdef DBG char pchErrorText[MAX_PATH]; Assert(UserWorkItem.EventMgr); if (EVENTMGR_CONMAN == UserWorkItem.EventMgr) { CONMAN_EVENT* pConmanEvent = reinterpret_cast(pEvent); TraceTag(ttidEvents, "EnqueueEvent received Event: %s (currently %d in queue). Event Manager: CONMAN", DbgEvents(pConmanEvent->Type), m_eqWorkItems.size()); sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\n", pConmanEvent->Type); AssertSz(IsValidEventType(UserWorkItem.EventMgr, pConmanEvent->Type), pchErrorText); } else { sprintf(pchErrorText, "Invalid Event Manager %d specified in Event structure\r\n", EventMgr); AssertSz(FALSE, pchErrorText); } #endif try { m_eqWorkItems.push_back(UserWorkItem); m_pFireEvents->SetEvent(); } catch (...) { hr = E_OUTOFMEMORY; } return hr; } //+--------------------------------------------------------------------------- // // Function: DequeueEvent // // Purpose: Retrieves the next event in the Event Queue // // Arguments: // Function - The pointer to the function to be called when firing the // event // Event - The Event information // Flags - Unused, just for compatibility with QueueUserWorkItem calls // // Returns: HRESULT // // Author: ckotze 30 Nov 2000 // // Notes: Locks and Unlocks the critical section only while working // with the queue // // HRESULT CEventQueue::DequeueEvent(OUT LPTHREAD_START_ROUTINE& Function, OUT PVOID& pEvent, OUT EVENT_MANAGER& EventMgr) { TraceFileFunc(ttidEvents); CExceptionSafeLock esLock(&m_csQueue); USERWORKITEM UserWorkItem; DWORD dwSize = m_eqWorkItems.size(); if (!dwSize) { AssertSz(FALSE, "Calling DequeueEvent with 0 items in Queue!!!"); return E_UNEXPECTED; } UserWorkItem = m_eqWorkItems.front(); m_eqWorkItems.pop_front(); Function = UserWorkItem.Function; pEvent = UserWorkItem.Event; EventMgr = UserWorkItem.EventMgr; if (EVENTMGR_CONMAN == EventMgr) { CONMAN_EVENT* pConmanEvent = reinterpret_cast(pEvent); if (REFRESH_ALL == pConmanEvent->Type) { m_fRefreshAllInQueue = FALSE; } } #ifdef DBG char pchErrorText[MAX_PATH]; Assert(EventMgr); if (EVENTMGR_CONMAN == EventMgr) { CONMAN_EVENT* pConmanEvent = reinterpret_cast(pEvent); TraceTag(ttidEvents, "DequeueEvent retrieved Event: %s (%d left in queue). Event Manager: CONMAN", DbgEvents(pConmanEvent->Type), m_eqWorkItems.size()); sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\nItems in Queue: %d\r\n", pConmanEvent->Type, dwSize); AssertSz(IsValidEventType(EventMgr, pConmanEvent->Type), pchErrorText); } else { sprintf(pchErrorText, "Invalid Event Manager %d specified in Event structure\r\n", EventMgr); AssertSz(FALSE, pchErrorText); } #endif return S_OK; } //+--------------------------------------------------------------------------- // // Function: WaitForExit // // Purpose: Waits for the queue to exit. // // Arguments: // (none) // // Returns: WAIT_OBJECT_0 or failure code. // // Author: ckotze 28 Apr 2001 // // Notes: // // DWORD CEventQueue::WaitForExit() { return WaitForSingleObject(m_hServiceShutdown, INFINITE); } //+--------------------------------------------------------------------------- // // Function: size // // Purpose: Returns the Number of items in the queue // // Arguments: // (none) // // Returns: Number of items in the queue // // Author: ckotze 30 Nov 2000 // // Notes: // // size_t CEventQueue::size() { CExceptionSafeLock esLock(&m_csQueue); TraceFileFunc(ttidEvents); size_t tempsize; tempsize = m_eqWorkItems.size(); return tempsize; } //+--------------------------------------------------------------------------- // // Function: AtomCheckSizeAndResetEvent // // Purpose: Make sure we know when we're supposed to exit, lock during the // operation. // Arguments: // (none) // // Returns: TRUE if should exit thread. FALSE if more events in queue, or // service is not shutting down. // Author: ckotze 04 March 2001 // // Notes: // // BOOL CEventQueue::AtomCheckSizeAndResetEvent(const BOOL fDispatchEvents) { CExceptionSafeLock esLock(&m_csQueue); BOOL fRet = TRUE; TraceTag(ttidEvents, "Checking for Exit Conditions, Events in queue: %d, Service Shutting Down: %s", size(), (fDispatchEvents) ? "FALSE" : "TRUE"); if (m_eqWorkItems.empty() || !fDispatchEvents) { fRet = FALSE; if (fDispatchEvents) { m_pFireEvents->ResetEvent(); } else { SetEvent(m_hServiceShutdown); } } return fRet; } // CEvent is a Hybrid between Automatic and Manual reset events. // It is automatically reset, but we control when it is set so it // doesn't spawn threads while set, except for the first one. CEvent::CEvent(HANDLE hEvent) { m_hEvent = hEvent; m_bSignaled = FALSE; } CEvent::~CEvent() { CloseHandle(m_hEvent); } HRESULT CEvent::SetEvent() { HRESULT hr = S_OK; if (!m_bSignaled) { if (!::SetEvent(m_hEvent)) { hr = HrFromLastWin32Error(); } else { m_bSignaled = TRUE; } } return hr; } HRESULT CEvent::ResetEvent() { HRESULT hr = S_OK; Assert(m_bSignaled); m_bSignaled = FALSE; return hr; }