Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine Signalled state of Win32 Event?

Tags:

c++

events

winapi

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 - event is now reset. Maybe need to wrap this in a critical section or similar?

  if (signalled)  
    SetEvent(handle);

  return signalled; 
}
like image 527
Roddy Avatar asked May 09 '11 12:05

Roddy


People also ask

What is a win32 event?

The win32-event library provides an interface to Windows event objects. An event object is a synchronization object whose state can be explicitly set to a signaled state. Event objects are useful in sending a signal to a thread indicating that a particular event has occurred.

What does createEvent do?

The createEvent() method creates an event object. The event must be of a legal event type, and must be initialized (dipatched) before use.

What does WaitForSingleObject return?

The WaitForSingleObject function returns when one of the following occurs: The specified object is in the signaled state. The time-out interval elapses.

What is the use of WaitForSingleObject?

The WaitForSingleObject function checks the current state of the specified object. If the object's state is nonsignaled, the calling thread enters the wait state until the object is signaled or the time-out interval elapses. The function modifies the state of some types of synchronization objects.


2 Answers

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.

like image 144
Ivan Bohannon Avatar answered Oct 23 '22 04:10

Ivan Bohannon


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.

like image 42
Adrian McCarthy Avatar answered Oct 23 '22 03:10

Adrian McCarthy