/ src / common / utils / EventWaiter.h
EventWaiter.h
  1  #pragma once
  2  
  3  #include <functional>
  4  #include <thread>
  5  #include <string>
  6  #include <atomic>
  7  #include <windows.h>
  8  
  9  /// <summary>
 10  /// A reusable utility class that listens for a named Windows event and invokes a callback when triggered.
 11  /// Provides RAII-based resource management for event handles and the listener thread.
 12  /// The thread is properly joined on destruction to ensure clean shutdown.
 13  /// </summary>
 14  class EventWaiter
 15  {
 16  public:
 17      EventWaiter() = default;
 18  
 19      EventWaiter(const EventWaiter&) = delete;
 20      EventWaiter& operator=(const EventWaiter&) = delete;
 21      EventWaiter(EventWaiter&&) = delete;
 22      EventWaiter& operator=(EventWaiter&&) = delete;
 23  
 24      ~EventWaiter()
 25      {
 26          stop();
 27      }
 28  
 29      /// <summary>
 30      /// Starts listening for the specified named event. When the event is signaled, the callback is invoked.
 31      /// </summary>
 32      /// <param name="name">The name of the Windows event to listen for.</param>
 33      /// <param name="callback">The callback function to invoke when the event is triggered. Receives ERROR_SUCCESS on success.</param>
 34      /// <returns>true if listening started successfully, false otherwise.</returns>
 35      bool start(const std::wstring& name, std::function<void(DWORD)> callback)
 36      {
 37          if (m_listening)
 38          {
 39              return false;
 40          }
 41  
 42          m_exitThreadEvent = CreateEventW(nullptr, false, false, nullptr);
 43          m_waitingEvent = CreateEventW(nullptr, false, false, name.c_str());
 44  
 45          if (!m_exitThreadEvent || !m_waitingEvent)
 46          {
 47              cleanup();
 48              return false;
 49          }
 50  
 51          m_listening = true;
 52          m_eventThread = std::thread([this, cb = std::move(callback)]() {
 53              HANDLE events[2] = { m_waitingEvent, m_exitThreadEvent };
 54              while (m_listening)
 55              {
 56                  auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE);
 57                  if (!m_listening)
 58                  {
 59                      break;
 60                  }
 61  
 62                  if (waitResult == WAIT_OBJECT_0 + 1)
 63                  {
 64                      // Exit event signaled
 65                      break;
 66                  }
 67  
 68                  if (waitResult == WAIT_FAILED)
 69                  {
 70                      cb(GetLastError());
 71                      continue;
 72                  }
 73  
 74                  if (waitResult == WAIT_OBJECT_0)
 75                  {
 76                      cb(ERROR_SUCCESS);
 77                  }
 78              }
 79          });
 80  
 81          return true;
 82      }
 83  
 84      /// <summary>
 85      /// Stops listening for the event and cleans up resources.
 86      /// Waits for the listener thread to finish before returning.
 87      /// Safe to call multiple times.
 88      /// </summary>
 89      void stop()
 90      {
 91          m_listening = false;
 92          if (m_exitThreadEvent)
 93          {
 94              SetEvent(m_exitThreadEvent);
 95          }
 96          if (m_eventThread.joinable())
 97          {
 98              m_eventThread.join();
 99          }
100          cleanup();
101      }
102  
103      /// <summary>
104      /// Returns whether the listener is currently active.
105      /// </summary>
106      bool is_listening() const
107      {
108          return m_listening;
109      }
110  
111  private:
112      void cleanup()
113      {
114          if (m_exitThreadEvent)
115          {
116              CloseHandle(m_exitThreadEvent);
117              m_exitThreadEvent = nullptr;
118          }
119          if (m_waitingEvent)
120          {
121              CloseHandle(m_waitingEvent);
122              m_waitingEvent = nullptr;
123          }
124      }
125  
126      HANDLE m_exitThreadEvent = nullptr;
127      HANDLE m_waitingEvent = nullptr;
128      std::thread m_eventThread;
129      std::atomic_bool m_listening{ false };
130  };