I have an auto-reset event object created like this:
handle = CreateEvent(NULL, true, false, NULL);
...and (for some unit testing purposes) I want to check if it's signalled at a certain point. I'm aware of the 'correct' way of using events - this is purely for a diagnostic harness.
For a manual reset event I can just use...
bool signalled = WaitForSingleObjectEx(handle, 0, true) != WAIT_TIMEOUT;
...but for auto-reset events that has the side-effect of resetting them. I guess I could try this, but I have a feeling that there should be a less dangerous way...?
bool isSignalled(HANDLE handle)
{
bool signalled = WaitForSingleObjectEx(handle, 0, true) != WAIT_TIMEOUT;
// warning -开发者_如何学Python event is now reset. Maybe need to wrap this in a critical section or similar?
if (signalled)
SetEvent(handle);
return signalled;
}
Update:
The Coffee has kicked in, and I was wrong!
Using WaitForSingleObject with a timeout of zero to determine if an event has been signaled WILL cause the signal to be cleared if the Event is signaled (and its an AutoReset event). You can verify this by simply calling WaitForSingleObject twice in a row.
A waiting thread has to be released before an Event that is intialized for AutoReset will reset.
http://msdn.microsoft.com/en-us/library/ms682396(VS.85).aspx
When you call WaitForSingleObjectEx with a timeout of zero, your thread does not become a waiter.
http://msdn.microsoft.com/en-us/library/ms687036(VS.85).aspx
If dwMilliseconds is zero, the function does not enter a wait state if the criteria is not met; it always returns immediately.
I don't see a simple way to do it. You could "mock" the event for testing purposes.
Wrap the event in an C++ object, and change all the code to use its methods.
class MockEvent {
public:
MockEvent () : m_handle(::CreateEvent(NULL, TRUE, FALSE, NULL) {}
~MockEvent () { ::CloseHandle(m_handle); }
BOOL Set() { return ::SetEvent(m_handle); }
DWORD Wait(DWORD timeout = INFINITE) {
return ::WaitForSingleObject(m_handle, timeout);
}
private:
HANDLE m_handle;
// Do not implement copy or assignment:
MockEvent(const MockEvent &);
MockEvent &operator=(const MockEvent &);
};
Then you'll want to use some sort of reference counted pointer that can be passed around and copied the way the original HANDLE can be:
typedef std::tr1::shared_ptr<MockEvent> MockEventPtr;
Replace all your code that uses the raw HANDLE with a MockEventPtr.
// Original code:
HANDLE hEvent = CreateEvent(NULL, true, false, NULL);
// Becomes:
MockEventPtr pEvent(new MockEvent);
// Original code:
SetEvent(hEvent);
// Becomes:
pEvent->Set();
And so on.
Now, for your diagnostic harness, you can extend MockEvent to keep track of the state and expose a method to show the current state.
class MockEvent {
public:
MockEvent () :
m_handle(::CreateEvent(NULL, TRUE, FALSE, NULL),
m_signaled(false) {
::InitializeCriticalSection(&m_cs);
}
~MockEvent () {
::DeleteCriticalSection(&m_cs);
::CloseHandle(m_handle);
}
BOOL Set() {
::EnterCriticalSection(&m_cs);
m_signaled = true;
BOOL result = ::SetEvent(m_handle);
::LeaveCriticalSection(&m_cs);
return result;
}
DWORD Wait(DWORD timeout = INFINITE) {
::EnterCriticalSection(&m_cs);
DWORD result = ::WaitForSingleObject(m_handle, timeout);
if (result == WAIT_OBJECT_0) {
m_signaled = false;
}
::LeaveCriticalSection(&m_cs);
return result;
}
// The result of this may be obsolete by the time you get it.
bool IsSignaled() const { return m_signaled; }
private:
HANDLE m_handle;
bool m_signaled;
CRITICAL_SECTION m_cs;
// Do not implement copy or assignment:
MockEvent(const MockEvent &);
MockEvent &operator=(const MockEvent &);
};
This is untested code. In real code, I'd wrap the CRITICAL_SECTION, too. Note that the result of IsSignaled may be obsolete the moment you get it, if another thread changes the state. I'm assuming this is for testing code that will check at a time when the state should be a certain way.
精彩评论