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 | |
9 | #include "lldb/Host/windows/MainLoopWindows.h" |
10 | #include "lldb/Host/Config.h" |
11 | #include "lldb/Utility/Status.h" |
12 | #include "llvm/Config/llvm-config.h" |
13 | #include <algorithm> |
14 | #include <cassert> |
15 | #include <cerrno> |
16 | #include <csignal> |
17 | #include <ctime> |
18 | #include <vector> |
19 | #include <winsock2.h> |
20 | |
21 | using namespace lldb; |
22 | using namespace lldb_private; |
23 | |
24 | static DWORD ToTimeout(std::optional<MainLoopWindows::TimePoint> point) { |
25 | using namespace std::chrono; |
26 | |
27 | if (!point) |
28 | return WSA_INFINITE; |
29 | |
30 | nanoseconds dur = (std::max)(*point - steady_clock::now(), nanoseconds(0)); |
31 | return ceil<milliseconds>(dur).count(); |
32 | } |
33 | |
34 | MainLoopWindows::MainLoopWindows() { |
35 | m_interrupt_event = WSACreateEvent(); |
36 | assert(m_interrupt_event != WSA_INVALID_EVENT); |
37 | } |
38 | |
39 | MainLoopWindows::~MainLoopWindows() { |
40 | assert(m_read_fds.empty()); |
41 | BOOL result = WSACloseEvent(m_interrupt_event); |
42 | assert(result == TRUE); |
43 | UNUSED_IF_ASSERT_DISABLED(result); |
44 | } |
45 | |
46 | llvm::Expected<size_t> MainLoopWindows::Poll() { |
47 | std::vector<WSAEVENT> events; |
48 | events.reserve(m_read_fds.size() + 1); |
49 | for (auto &[fd, info] : m_read_fds) { |
50 | int result = WSAEventSelect(fd, info.event, FD_READ | FD_ACCEPT | FD_CLOSE); |
51 | assert(result == 0); |
52 | UNUSED_IF_ASSERT_DISABLED(result); |
53 | |
54 | events.push_back(info.event); |
55 | } |
56 | events.push_back(m_interrupt_event); |
57 | |
58 | DWORD result = |
59 | WSAWaitForMultipleEvents(events.size(), events.data(), FALSE, |
60 | ToTimeout(GetNextWakeupTime()), FALSE); |
61 | |
62 | for (auto &fd : m_read_fds) { |
63 | int result = WSAEventSelect(fd.first, WSA_INVALID_EVENT, 0); |
64 | assert(result == 0); |
65 | UNUSED_IF_ASSERT_DISABLED(result); |
66 | } |
67 | |
68 | if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + events.size()) |
69 | return result - WSA_WAIT_EVENT_0; |
70 | |
71 | // A timeout is treated as a (premature) signalization of the interrupt event. |
72 | if (result == WSA_WAIT_TIMEOUT) |
73 | return events.size() - 1; |
74 | |
75 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
76 | S: "WSAWaitForMultipleEvents failed" ); |
77 | } |
78 | |
79 | MainLoopWindows::ReadHandleUP |
80 | MainLoopWindows::RegisterReadObject(const IOObjectSP &object_sp, |
81 | const Callback &callback, Status &error) { |
82 | if (!object_sp || !object_sp->IsValid()) { |
83 | error = Status::FromErrorString(str: "IO object is not valid." ); |
84 | return nullptr; |
85 | } |
86 | if (object_sp->GetFdType() != IOObject::eFDTypeSocket) { |
87 | error = Status::FromErrorString( |
88 | str: "MainLoopWindows: non-socket types unsupported on Windows" ); |
89 | return nullptr; |
90 | } |
91 | |
92 | WSAEVENT event = WSACreateEvent(); |
93 | if (event == WSA_INVALID_EVENT) { |
94 | error = |
95 | Status::FromErrorStringWithFormat(format: "Cannot create monitoring event." ); |
96 | return nullptr; |
97 | } |
98 | |
99 | const bool inserted = |
100 | m_read_fds |
101 | .try_emplace(Key: object_sp->GetWaitableHandle(), Args: FdInfo{event, callback}) |
102 | .second; |
103 | if (!inserted) { |
104 | WSACloseEvent(event); |
105 | error = Status::FromErrorStringWithFormat( |
106 | format: "File descriptor %d already monitored." , |
107 | object_sp->GetWaitableHandle()); |
108 | return nullptr; |
109 | } |
110 | |
111 | return CreateReadHandle(object_sp); |
112 | } |
113 | |
114 | void MainLoopWindows::UnregisterReadObject(IOObject::WaitableHandle handle) { |
115 | auto it = m_read_fds.find(Val: handle); |
116 | assert(it != m_read_fds.end()); |
117 | BOOL result = WSACloseEvent(it->second.event); |
118 | assert(result == TRUE); |
119 | UNUSED_IF_ASSERT_DISABLED(result); |
120 | m_read_fds.erase(I: it); |
121 | } |
122 | |
123 | void MainLoopWindows::ProcessReadObject(IOObject::WaitableHandle handle) { |
124 | auto it = m_read_fds.find(Val: handle); |
125 | if (it != m_read_fds.end()) |
126 | it->second.callback(*this); // Do the work |
127 | } |
128 | |
129 | Status MainLoopWindows::Run() { |
130 | m_terminate_request = false; |
131 | |
132 | Status error; |
133 | |
134 | while (!m_terminate_request) { |
135 | llvm::Expected<size_t> signaled_event = Poll(); |
136 | if (!signaled_event) |
137 | return Status::FromError(error: signaled_event.takeError()); |
138 | |
139 | if (*signaled_event < m_read_fds.size()) { |
140 | auto &KV = *std::next(x: m_read_fds.begin(), n: *signaled_event); |
141 | WSAResetEvent(KV.second.event); |
142 | ProcessReadObject(handle: KV.first); |
143 | } else { |
144 | assert(*signaled_event == m_read_fds.size()); |
145 | WSAResetEvent(m_interrupt_event); |
146 | } |
147 | ProcessCallbacks(); |
148 | } |
149 | return Status(); |
150 | } |
151 | |
152 | void MainLoopWindows::Interrupt() { WSASetEvent(m_interrupt_event); } |
153 | |