| 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 | |