Skip to content

Commit e3ed56a

Browse files
Merge pull request #12013 from charles-zablit/charles-zablit/lldb/windows/fix-race-condition-to-swift-release-6.3
🍒 [lldb][windows] fix a race condition when polling Pipes
2 parents de4b7ff + a2ddda2 commit e3ed56a

File tree

1 file changed

+39
-26
lines changed

1 file changed

+39
-26
lines changed

lldb/source/Host/windows/MainLoopWindows.cpp

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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 {
138149
private:
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

145158
class SocketEvent : public MainLoopWindows::IOEvent {

0 commit comments

Comments
 (0)