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
21using namespace lldb;
22using namespace lldb_private;
23
24static 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
34MainLoopWindows::MainLoopWindows() {
35 m_interrupt_event = WSACreateEvent();
36 assert(m_interrupt_event != WSA_INVALID_EVENT);
37}
38
39MainLoopWindows::~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
46llvm::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
79MainLoopWindows::ReadHandleUP
80MainLoopWindows::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
114void 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
123void 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
129Status 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
152void MainLoopWindows::Interrupt() { WSASetEvent(m_interrupt_event); }
153

source code of lldb/source/Host/windows/MainLoopWindows.cpp