Как определить состояние события Win32?

У меня есть объект события auto- reset, созданный следующим образом:

handle = CreateEvent(NULL, true, false, NULL);

... и (для некоторых целей тестирования), я хочу проверить, сигнализировалось ли оно в определенный момент. Я знаю о "правильном" способе использования событий - это чисто для диагностического жгута.

Для ручного события reset я могу просто использовать...

bool signalled = WaitForSingleObjectEx(handle, 0, true) != WAIT_TIMEOUT;

... но для событий auto- reset, которые имеют побочный эффект их сброса. Думаю, я мог бы попробовать это, но у меня есть ощущение, что должен быть менее опасный путь...?

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; 
}

Ответ 1

Я не вижу простого способа сделать это. Вы можете "высмеять" событие для целей тестирования.

Оберните событие в объект С++ и измените весь код на использование его методов.

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 &);
};

Затем вы захотите использовать какой-то ссылочный подсчитанный указатель, который можно передать и скопировать способ, которым может быть оригинальная РУЧКА:

typedef std::tr1::shared_ptr<MockEvent> MockEventPtr;

Замените весь свой код, который использует необработанную РУЧКУ с помощью MockEventPtr.

// Original code:
HANDLE hEvent = CreateEvent(NULL, true, false, NULL);
// Becomes:
MockEventPtr pEvent(new MockEvent);

// Original code:
SetEvent(hEvent);
// Becomes:
pEvent->Set();

И так далее.

Теперь, для вашей диагностической упряжи, вы можете расширить MockEvent, чтобы отслеживать состояние и выставлять метод для отображения текущего состояния.

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 &);
};

Это непроверенный код. В реальном коде я бы обернул CRITICAL_SECTION. Обратите внимание, что результат IsSignaled может быть устаревшим в тот момент, когда вы его получите, если другой поток изменит состояние. Я предполагаю, что это для тестирования кода, который будет проверяться в то время, когда состояние должно быть определенным образом.

Ответ 2

Обновление:

Кофе зашел, и я ошибся!

Использование WaitForSingleObject с тайм-аутом нуля, чтобы определить, было ли событие сигнализировано, приведет к тому, что сигнал будет очищен, если событие будет сигнализировано (и событие AutoReset). Вы можете проверить это, просто дважды вызовите WaitForSingleObject подряд.


Ожидающий поток должен быть освобожден до того, как событие, которое будет выполнено для AutoReset, будет reset.

http://msdn.microsoft.com/en-us/library/ms682396 (VS.85).aspx

Когда вы вызываете WaitForSingleObjectEx с тайм-аутом нуля, ваш поток не становится официантом.

http://msdn.microsoft.com/en-us/library/ms687036 (VS.85).aspx

Если dwMilliseconds равно нулю, функция не переходит в состояние ожидания если критерии не выполнены; так всегда немедленно возвращается.