//+--------------------------------------------------------------------------- // // 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 "cmevent.h" #include "eventq.h" #include "ncmisc.h" #include "conman.h" #include "nceh.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) throw(HRESULT) : m_hServiceShutdown(0), m_pFireEvents(NULL), m_hWait(0), m_fRefreshAllInQueue(FALSE) { TraceFileFunc(ttidEvents); NTSTATUS Status; try { Status = DuplicateHandle(GetCurrentProcess(), hServiceShutdown, GetCurrentProcess(), &m_hServiceShutdown, NULL, FALSE, DUPLICATE_SAME_ACCESS); if (!Status) { TraceTag(ttidEvents, "Couldn't Duplicate handle!"); throw HRESULT_FROM_WIN32(Status); } HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_pFireEvents = new CEvent(hEvent); if (!m_pFireEvents) { throw E_OUTOFMEMORY; } Status = RtlRegisterWait(&m_hWait, hEvent, (WAITORTIMERCALLBACKFUNC) DispatchEvents, NULL, INFINITE, WT_EXECUTEDEFAULT); if (!NT_SUCCESS(Status)) { throw HRESULT_FROM_WIN32(Status); } TraceTag(ttidEvents, "RtlRegisterWait Succeeded"); InitializeCriticalSection(&m_csQueue); } catch (HRESULT &hr) { TraceError("Out of memory", hr); 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; } catch (SE_Exception &e) { TraceError("An exception occurred", HRESULT_FROM_WIN32(e.getSeNumber()) ); 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 E_UNEXPECTED; } } //+--------------------------------------------------------------------------- // // 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() throw() { TraceFileFunc(ttidEvents); NTSTATUS Status; // Blocks until all outstanding threads return. Status = RtlDeregisterWaitEx(m_hWait, INVALID_HANDLE_VALUE); TraceError("RtlDeregisterWaitEx", HRESULT_FROM_WIN32(Status)); if (TryEnterCriticalSection(&m_csQueue)) { // This is okay. LeaveCriticalSection(&m_csQueue); } else { AssertSz(FALSE, "Another thread is still holding onto this critical section. This is unexpected at this point."); } DeleteCriticalSection(&m_csQueue); while (!m_eqWorkItems.empty()) { USERWORKITEM UserWorkItem; UserWorkItem = m_eqWorkItems.front(); m_eqWorkItems.pop_front(); if (UserWorkItem.EventMgr == EVENTMGR_CONMAN) { FreeConmanEvent(UserWorkItem.Event); } } 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 // pEvent - The Event information // EventMgr - Which event manager the event should go to. // // 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 PCONMAN_EVENTTHREAD Function, IN TAKEOWNERSHIP CONMAN_EVENT* 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) { if (REFRESH_ALL == pEvent->Type) { if (!m_fRefreshAllInQueue) { m_fRefreshAllInQueue = TRUE; } else { FreeConmanEvent(pEvent); return S_OK; } } } #ifdef DBG char pchErrorText[MAX_PATH]; Assert(UserWorkItem.EventMgr); if (EVENTMGR_CONMAN == UserWorkItem.EventMgr) { TraceTag(ttidEvents, "EnqueueEvent received Event: %s (currently %d in queue). Event Manager: CONMAN", DbgEvents(pEvent->Type), m_eqWorkItems.size()); sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\n", pEvent->Type); AssertSz(IsValidEventType(UserWorkItem.EventMgr, pEvent->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 (bad_alloc) { 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. Free with delete // EventMgr - Which event manager the event should go to. // // Returns: HRESULT // // Author: ckotze 30 Nov 2000 // // Notes: Locks and Unlocks the critical section only while working // with the queue // // HRESULT CEventQueue::DequeueEvent(OUT PCONMAN_EVENTTHREAD& Function, OUT TAKEOWNERSHIP CONMAN_EVENT*& 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) { if (REFRESH_ALL == pEvent->Type) { m_fRefreshAllInQueue = FALSE; } } #ifdef DBG char pchErrorText[MAX_PATH]; Assert(EventMgr); if (EVENTMGR_CONMAN == EventMgr) { TraceTag(ttidEvents, "DequeueEvent retrieved Event: %s (%d left in queue). Event Manager: CONMAN", DbgEvents(pEvent->Type), m_eqWorkItems.size()); sprintf(pchErrorText, "Invalid Type %d specified in Event structure\r\nItems in Queue: %d\r\n", pEvent->Type, dwSize); AssertSz(IsValidEventType(EventMgr, pEvent->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() throw() { TraceFileFunc(ttidEvents); 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() throw() { 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: // fDispatchEvents [in] Should be dispatching more events. // // 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(IN const BOOL fDispatchEvents) throw() { TraceFileFunc(ttidEvents); 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(IN HANDLE hEvent) throw() { m_hEvent = hEvent; m_bSignaled = FALSE; } CEvent::~CEvent() throw() { 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; }