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