LLDB mainline
MainLoopWindows.cpp
Go to the documentation of this file.
1//===-- MainLoopWindows.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "lldb/Host/Config.h"
11#include "lldb/Host/Socket.h"
13#include "lldb/Utility/Status.h"
14#include "llvm/Config/llvm-config.h"
15#include "llvm/Support/WindowsError.h"
16#include <algorithm>
17#include <atomic>
18#include <cassert>
19#include <ctime>
20#include <io.h>
21#include <synchapi.h>
22#include <thread>
23#include <vector>
24#include <winbase.h>
25#include <winerror.h>
26#include <winsock2.h>
27
28using namespace lldb;
29using namespace lldb_private;
30
31static DWORD ToTimeout(std::optional<MainLoopWindows::TimePoint> point) {
32 using namespace std::chrono;
33
34 if (!point)
35 return WSA_INFINITE;
36
37 nanoseconds dur = (std::max)(*point - steady_clock::now(), nanoseconds(0));
38 return ceil<milliseconds>(dur).count();
39}
40
41namespace {
42
43class PipeEvent : public MainLoopWindows::IOEvent {
44public:
45 explicit PipeEvent(HANDLE handle)
46 : IOEvent(CreateEventW(NULL, /*bManualReset=*/TRUE,
47 /*bInitialState=*/FALSE, NULL)),
48 m_handle(handle), m_ready(CreateEventW(NULL, /*bManualReset=*/TRUE,
49 /*bInitialState=*/FALSE, NULL)) {
50 assert(m_event && m_ready);
51 m_monitor_thread = std::thread(&PipeEvent::Monitor, this);
52 }
53
54 ~PipeEvent() override {
55 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);
63 m_monitor_thread.join();
64 }
65 CloseHandle(m_event);
66 CloseHandle(m_ready);
67 }
68
69 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.
77 return;
78 }
79 // Start waiting.
80 SetEvent(m_ready);
81 }
82
83 void Disarm() override { ResetEvent(m_event); }
84
85 /// Monitors the handle performing a zero byte read to determine when data is
86 /// avaiable.
87 void Monitor() {
88 // Wait until the MainLoop tells us to start.
89 WaitForSingleObject(m_ready, INFINITE);
90
91 do {
92 char buf[1];
93 DWORD bytes_read = 0;
94 OVERLAPPED ov;
95 ZeroMemory(&ov, sizeof(ov));
96 // Block on a 0-byte read; this will only resume when data is
97 // available in the pipe. The pipe must be PIPE_WAIT or this thread
98 // will spin.
99 BOOL success =
100 ReadFile(m_handle, buf, /*nNumberOfBytesToRead=*/0, &bytes_read, &ov);
101 DWORD bytes_available = 0;
102 DWORD err = GetLastError();
103 if (!success && err == ERROR_IO_PENDING) {
104 success = GetOverlappedResult(m_handle, &ov, &bytes_read,
105 /*bWait=*/TRUE);
106 err = GetLastError();
107 }
108 if (success) {
109 success =
110 PeekNamedPipe(m_handle, NULL, 0, NULL, &bytes_available, NULL);
111 err = GetLastError();
112 }
113 if (success) {
114 if (bytes_available == 0) {
115 // This can happen with a zero-byte write. Try again.
116 continue;
117 }
118 } else if (err == ERROR_NO_DATA) {
119 // The pipe is nonblocking. Try again.
120 Sleep(0);
121 continue;
122 } else if (err == ERROR_OPERATION_ABORTED) {
123 // Read may have been cancelled, try again.
124 continue;
125 }
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);
132
133 // Wait until the current read is consumed before doing the next read.
134 WaitForSingleObject(m_ready, INFINITE);
135 } while (!m_stopped);
136 }
137
138private:
139 HANDLE m_handle;
140 HANDLE m_ready;
141 std::thread m_monitor_thread;
142 std::atomic<bool> m_stopped = false;
143};
144
145class SocketEvent : public MainLoopWindows::IOEvent {
146public:
147 explicit SocketEvent(SOCKET socket)
148 : IOEvent(WSACreateEvent()), m_socket(socket) {
149 assert(m_event != WSA_INVALID_EVENT);
150 }
151
152 ~SocketEvent() override { WSACloseEvent(m_event); }
153
154 void WillPoll() override {
155 int result =
156 WSAEventSelect(m_socket, m_event, FD_READ | FD_ACCEPT | FD_CLOSE);
157 assert(result == 0);
159 }
160
161 void DidPoll() override {
162 int result = WSAEventSelect(m_socket, WSA_INVALID_EVENT, 0);
163 assert(result == 0);
165 }
166
167 void Disarm() override { WSAResetEvent(m_event); }
168
169 SOCKET m_socket;
170};
171
172} // namespace
173
175 m_interrupt_event = WSACreateEvent();
176 assert(m_interrupt_event != WSA_INVALID_EVENT);
177}
178
180 assert(m_read_fds.empty());
181 BOOL result = WSACloseEvent(m_interrupt_event);
182 assert(result == TRUE);
184}
185
186llvm::Expected<size_t> MainLoopWindows::Poll() {
187 std::vector<HANDLE> events;
188 events.reserve(m_read_fds.size() + 1);
189 for (auto &[_, fd_info] : m_read_fds) {
190 fd_info.event->WillPoll();
191 events.push_back(fd_info.event->GetHandle());
192 }
193 events.push_back(m_interrupt_event);
194
195 DWORD result =
196 WSAWaitForMultipleEvents(events.size(), events.data(), FALSE,
197 ToTimeout(GetNextWakeupTime()), FALSE);
198
199 for (auto &[_, fd_info] : m_read_fds)
200 fd_info.event->DidPoll();
201
202 if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + events.size())
203 return result - WSA_WAIT_EVENT_0;
204
205 // A timeout is treated as a (premature) signalization of the interrupt event.
206 if (result == WSA_WAIT_TIMEOUT)
207 return events.size() - 1;
208
209 return llvm::createStringError(llvm::inconvertibleErrorCode(),
210 "WSAWaitForMultipleEvents failed");
211}
212
215 const Callback &callback, Status &error) {
216 if (!object_sp || !object_sp->IsValid()) {
217 error = Status::FromErrorString("IO object is not valid.");
218 return nullptr;
219 }
220
221 IOObject::WaitableHandle waitable_handle = object_sp->GetWaitableHandle();
222 assert(waitable_handle != IOObject::kInvalidHandleValue);
223
224 if (m_read_fds.find(waitable_handle) != m_read_fds.end()) {
226 "File descriptor %p already monitored.", waitable_handle);
227 return nullptr;
228 }
229
230 if (object_sp->GetFdType() == IOObject::eFDTypeSocket) {
231 m_read_fds[waitable_handle] = {
232 std::make_unique<SocketEvent>(
233 reinterpret_cast<SOCKET>(waitable_handle)),
234 callback};
235 } else {
236 DWORD file_type = GetFileType(waitable_handle);
237 if (file_type != FILE_TYPE_PIPE) {
238 error = Status::FromErrorStringWithFormat("Unsupported file type %ld",
239 file_type);
240 return nullptr;
241 }
242
243 m_read_fds[waitable_handle] = {std::make_unique<PipeEvent>(waitable_handle),
244 callback};
245 }
246
247 return CreateReadHandle(object_sp);
248}
249
251 auto it = m_read_fds.find(handle);
252 assert(it != m_read_fds.end());
253 m_read_fds.erase(it);
254}
255
257 m_terminate_request = false;
258
260
261 while (!m_terminate_request) {
262 llvm::Expected<size_t> signaled_event = Poll();
263 if (!signaled_event)
264 return Status::FromError(signaled_event.takeError());
265
266 if (*signaled_event < m_read_fds.size()) {
267 auto &KV = *std::next(m_read_fds.begin(), *signaled_event);
268 KV.second.event->Disarm();
269 KV.second.callback(*this); // Do the work.
270 } else {
271 assert(*signaled_event == m_read_fds.size());
272 WSAResetEvent(m_interrupt_event);
273 }
275 }
276 return Status();
277}
278
static llvm::raw_ostream & error(Stream &strm)
static DWORD ToTimeout(std::optional< MainLoopWindows::TimePoint > point)
static const WaitableHandle kInvalidHandleValue
Definition IOObject.h:31
lldb::file_t WaitableHandle
Definition IOObject.h:29
std::unique_ptr< ReadHandle > ReadHandleUP
std::optional< TimePoint > GetNextWakeupTime()
ReadHandleUP CreateReadHandle(const lldb::IOObjectSP &object_sp)
std::function< void(MainLoopBase &)> Callback
llvm::Expected< size_t > Poll()
llvm::DenseMap< IOObject::WaitableHandle, FdInfo > m_read_fds
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Status &error) override
void UnregisterReadObject(IOObject::WaitableHandle handle) override
An error handling class.
Definition Status.h:118
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static Status FromErrorString(const char *str)
Definition Status.h:141
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:137
#define UNUSED_IF_ASSERT_DISABLED(x)
A class that represents a running process on the host machine.
std::shared_ptr< lldb_private::IOObject > IOObjectSP