You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
12 KiB
478 lines
12 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|