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 | MainLoopWindows::MainLoopWindows() { |
25 | m_trigger_event = WSACreateEvent(); |
26 | assert(m_trigger_event != WSA_INVALID_EVENT); |
27 | } |
28 | |
29 | MainLoopWindows::~MainLoopWindows() { |
30 | assert(m_read_fds.empty()); |
31 | BOOL result = WSACloseEvent(m_trigger_event); |
32 | assert(result == TRUE); |
33 | UNUSED_IF_ASSERT_DISABLED(result); |
34 | } |
35 | |
36 | llvm::Expected<size_t> MainLoopWindows::Poll() { |
37 | std::vector<WSAEVENT> events; |
38 | events.reserve(m_read_fds.size() + 1); |
39 | for (auto &[fd, info] : m_read_fds) { |
40 | int result = WSAEventSelect(fd, info.event, FD_READ | FD_ACCEPT | FD_CLOSE); |
41 | assert(result == 0); |
42 | UNUSED_IF_ASSERT_DISABLED(result); |
43 | |
44 | events.push_back(info.event); |
45 | } |
46 | events.push_back(m_trigger_event); |
47 | |
48 | DWORD result = WSAWaitForMultipleEvents(events.size(), events.data(), FALSE, |
49 | WSA_INFINITE, FALSE); |
50 | |
51 | for (auto &fd : m_read_fds) { |
52 | int result = WSAEventSelect(fd.first, WSA_INVALID_EVENT, 0); |
53 | assert(result == 0); |
54 | UNUSED_IF_ASSERT_DISABLED(result); |
55 | } |
56 | |
57 | if (result >= WSA_WAIT_EVENT_0 && result <= WSA_WAIT_EVENT_0 + events.size()) |
58 | return result - WSA_WAIT_EVENT_0; |
59 | |
60 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
61 | Msg: "WSAWaitForMultipleEvents failed" ); |
62 | } |
63 | |
64 | MainLoopWindows::ReadHandleUP |
65 | MainLoopWindows::RegisterReadObject(const IOObjectSP &object_sp, |
66 | const Callback &callback, Status &error) { |
67 | if (!object_sp || !object_sp->IsValid()) { |
68 | error.SetErrorString("IO object is not valid." ); |
69 | return nullptr; |
70 | } |
71 | if (object_sp->GetFdType() != IOObject::eFDTypeSocket) { |
72 | error.SetErrorString( |
73 | "MainLoopWindows: non-socket types unsupported on Windows" ); |
74 | return nullptr; |
75 | } |
76 | |
77 | WSAEVENT event = WSACreateEvent(); |
78 | if (event == WSA_INVALID_EVENT) { |
79 | error.SetErrorStringWithFormat("Cannot create monitoring event." ); |
80 | return nullptr; |
81 | } |
82 | |
83 | const bool inserted = |
84 | m_read_fds |
85 | .try_emplace(Key: object_sp->GetWaitableHandle(), Args: FdInfo{event, callback}) |
86 | .second; |
87 | if (!inserted) { |
88 | WSACloseEvent(event); |
89 | error.SetErrorStringWithFormat("File descriptor %d already monitored." , |
90 | object_sp->GetWaitableHandle()); |
91 | return nullptr; |
92 | } |
93 | |
94 | return CreateReadHandle(object_sp); |
95 | } |
96 | |
97 | void MainLoopWindows::UnregisterReadObject(IOObject::WaitableHandle handle) { |
98 | auto it = m_read_fds.find(Val: handle); |
99 | assert(it != m_read_fds.end()); |
100 | BOOL result = WSACloseEvent(it->second.event); |
101 | assert(result == TRUE); |
102 | UNUSED_IF_ASSERT_DISABLED(result); |
103 | m_read_fds.erase(I: it); |
104 | } |
105 | |
106 | void MainLoopWindows::ProcessReadObject(IOObject::WaitableHandle handle) { |
107 | auto it = m_read_fds.find(Val: handle); |
108 | if (it != m_read_fds.end()) |
109 | it->second.callback(*this); // Do the work |
110 | } |
111 | |
112 | Status MainLoopWindows::Run() { |
113 | m_terminate_request = false; |
114 | |
115 | Status error; |
116 | |
117 | // run until termination or until we run out of things to listen to |
118 | while (!m_terminate_request && !m_read_fds.empty()) { |
119 | |
120 | llvm::Expected<size_t> signaled_event = Poll(); |
121 | if (!signaled_event) |
122 | return Status(signaled_event.takeError()); |
123 | |
124 | if (*signaled_event < m_read_fds.size()) { |
125 | auto &KV = *std::next(x: m_read_fds.begin(), n: *signaled_event); |
126 | ProcessReadObject(handle: KV.first); |
127 | } else { |
128 | assert(*signaled_event == m_read_fds.size()); |
129 | WSAResetEvent(m_trigger_event); |
130 | } |
131 | ProcessPendingCallbacks(); |
132 | } |
133 | return Status(); |
134 | } |
135 | |
136 | void MainLoopWindows::TriggerPendingCallbacks() { |
137 | WSASetEvent(m_trigger_event); |
138 | } |
139 | |