xref: /llvm-project/lldb/source/Host/windows/MainLoopWindows.cpp (revision 17b87853c3b07b8e1c7f000c3818efab7fdd8883)
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(llvm::inconvertibleErrorCode(),
76                                  "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("IO object is not valid.");
84     return nullptr;
85   }
86   if (object_sp->GetFdType() != IOObject::eFDTypeSocket) {
87     error = Status::FromErrorString(
88         "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("Cannot create monitoring event.");
96     return nullptr;
97   }
98 
99   const bool inserted =
100       m_read_fds
101           .try_emplace(object_sp->GetWaitableHandle(), FdInfo{event, callback})
102           .second;
103   if (!inserted) {
104     WSACloseEvent(event);
105     error = Status::FromErrorStringWithFormat(
106         "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(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(it);
121 }
122 
123 void MainLoopWindows::ProcessReadObject(IOObject::WaitableHandle handle) {
124   auto it = m_read_fds.find(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(signaled_event.takeError());
138 
139     if (*signaled_event < m_read_fds.size()) {
140       auto &KV = *std::next(m_read_fds.begin(), *signaled_event);
141       WSAResetEvent(KV.second.event);
142       ProcessReadObject(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