@@ -53,34 +53,38 @@ class PipeEvent : public MainLoopWindows::IOEvent {
5353
5454 ~PipeEvent () override {
5555 if (m_monitor_thread.joinable ()) {
56- m_stopped = true ;
57- SetEvent (m_ready);
58- // Keep trying to cancel ReadFile() until the thread exits.
59- do {
60- CancelIoEx (m_handle, /* lpOverlapped=*/ NULL );
61- } while (WaitForSingleObject (m_monitor_thread.native_handle (), 1 ) ==
62- WAIT_TIMEOUT);
56+ {
57+ std::lock_guard<std::mutex> guard (m_mutex);
58+ m_stopped = true ;
59+ SetEvent (m_ready);
60+ CancelIoEx (m_handle, &m_ov);
61+ }
6362 m_monitor_thread.join ();
6463 }
6564 CloseHandle (m_event);
6665 CloseHandle (m_ready);
6766 }
6867
6968 void WillPoll () override {
70- if (WaitForSingleObject (m_event, /* dwMilliseconds=*/ 0 ) != WAIT_TIMEOUT) {
71- // The thread has already signalled that the data is available. No need
72- // for further polling until we consume that event.
73- return ;
74- }
75- if (WaitForSingleObject (m_ready, /* dwMilliseconds=*/ 0 ) != WAIT_TIMEOUT) {
76- // The thread is already waiting for data to become available.
69+ std::lock_guard<std::mutex> guard (m_mutex);
70+
71+ HANDLE handles[2 ] = {m_event, m_ready};
72+ if (WaitForMultipleObjects (2 , handles, /* bWaitAll=*/ FALSE ,
73+ /* dwMilliseconds=*/ 0 ) != WAIT_TIMEOUT) {
74+ // Either:
75+ // - The thread has already signalled that the data is available. No need
76+ // for further polling until we consume that event.
77+ // - The thread is already waiting for data to become available.
7778 return ;
7879 }
7980 // Start waiting.
8081 SetEvent (m_ready);
8182 }
8283
83- void Disarm () override { ResetEvent (m_event); }
84+ void Disarm () override {
85+ std::lock_guard<std::mutex> guard (m_mutex);
86+ ResetEvent (m_event);
87+ }
8488
8589 // / Monitors the handle performing a zero byte read to determine when data is
8690 // / avaiable.
@@ -91,17 +95,16 @@ class PipeEvent : public MainLoopWindows::IOEvent {
9195 do {
9296 char buf[1 ];
9397 DWORD bytes_read = 0 ;
94- OVERLAPPED ov;
95- ZeroMemory (&ov, sizeof (ov));
98+ ZeroMemory (&m_ov, sizeof (m_ov));
9699 // Block on a 0-byte read; this will only resume when data is
97100 // available in the pipe. The pipe must be PIPE_WAIT or this thread
98101 // will spin.
99- BOOL success =
100- ReadFile (m_handle, buf, /* nNumberOfBytesToRead= */ 0 , &bytes_read, &ov );
102+ BOOL success = ReadFile (m_handle, buf, /* nNumberOfBytesToRead= */ 0 ,
103+ &bytes_read, &m_ov );
101104 DWORD bytes_available = 0 ;
102105 DWORD err = GetLastError ();
103106 if (!success && err == ERROR_IO_PENDING) {
104- success = GetOverlappedResult (m_handle, &ov , &bytes_read,
107+ success = GetOverlappedResult (m_handle, &m_ov , &bytes_read,
105108 /* bWait=*/ TRUE );
106109 err = GetLastError ();
107110 }
@@ -123,12 +126,20 @@ class PipeEvent : public MainLoopWindows::IOEvent {
123126 // Read may have been cancelled, try again.
124127 continue ;
125128 }
126-
127- // Notify that data is available on the pipe. It's important to set this
128- // before clearing m_ready to avoid a race with WillPoll.
129- SetEvent (m_event);
130- // Stop polling until we're told to resume.
131- ResetEvent (m_ready);
129+ {
130+ std::lock_guard<std::mutex> guard (m_mutex);
131+
132+ // Notify that data is available on the pipe.
133+ SetEvent (m_event);
134+ if (m_stopped) {
135+ // The destructor might have called SetEvent(m_ready) before this
136+ // block. If that's the case, ResetEvent(m_ready) will cause
137+ // WaitForSingleObject to wait forever unless we break early.
138+ break ;
139+ }
140+ // Stop polling until we're told to resume.
141+ ResetEvent (m_ready);
142+ }
132143
133144 // Wait until the current read is consumed before doing the next read.
134145 WaitForSingleObject (m_ready, INFINITE);
@@ -138,8 +149,10 @@ class PipeEvent : public MainLoopWindows::IOEvent {
138149private:
139150 HANDLE m_handle;
140151 HANDLE m_ready;
152+ OVERLAPPED m_ov;
141153 std::thread m_monitor_thread;
142154 std::atomic<bool > m_stopped = false ;
155+ std::mutex m_mutex;
143156};
144157
145158class SocketEvent : public MainLoopWindows ::IOEvent {
0 commit comments