| //===-- MainLoopWindows.cpp -----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Host/windows/MainLoopWindows.h" |
| #include "lldb/Host/Config.h" |
| #include "lldb/Utility/Status.h" |
| #include "llvm/Config/llvm-config.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <cerrno> |
| #include <csignal> |
| #include <ctime> |
| #include <vector> |
| #include <winsock2.h> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static DWORD ToTimeout(std::optional<MainLoopWindows::TimePoint> point) { |
| using namespace std::chrono; |
| |
| if (!point) |
| return WSA_INFINITE; |
| |
| nanoseconds dur = (std::max)(*point - steady_clock::now(), nanoseconds(0)); |
| return ceil<milliseconds>(dur).count(); |
| } |
| |
| MainLoopWindows::MainLoopWindows() { |
| m_interrupt_event = WSACreateEvent(); |
| assert(m_interrupt_event != WSA_INVALID_EVENT); |
| } |
| |
| MainLoopWindows::~MainLoopWindows() { |
| assert(m_read_fds.empty()); |
| BOOL result = WSACloseEvent(m_interrupt_event); |
| assert(result == TRUE); |
| UNUSED_IF_ASSERT_DISABLED(result); |
| } |
| |
| llvm::Expected<size_t> MainLoopWindows::Poll() { |
| std::vector<WSAEVENT> events; |
| events.reserve(m_read_fds.size() + 1); |
| for (auto &[fd, info] : m_read_fds) { |
| int result = WSAEventSelect(fd, info.event, FD_READ | FD_ACCEPT | FD_CLOSE); |
| assert(result == 0); |
| UNUSED_IF_ASSERT_DISABLED(result); |
| |
| events.push_back(info.event); |
| } |
| events.push_back(m_interrupt_event); |
| |
| DWORD result = |
| WSAWaitForMultipleEvents(events.size(), events.data(), FALSE, |
| ToTimeout(GetNextWakeupTime()), FALSE); |
| |
| for (auto &fd : m_read_fds) { |
| int result = WSAEventSelect(fd.first, WSA_INVALID_EVENT, 0); |
| assert(result == 0); |
| UNUSED_IF_ASSERT_DISABLED(result); |
| } |
| |
| if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + events.size()) |
| return result - WSA_WAIT_EVENT_0; |
| |
| // A timeout is treated as a (premature) signalization of the interrupt event. |
| if (result == WSA_WAIT_TIMEOUT) |
| return events.size() - 1; |
| |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), |
| "WSAWaitForMultipleEvents failed"); |
| } |
| |
| MainLoopWindows::ReadHandleUP |
| MainLoopWindows::RegisterReadObject(const IOObjectSP &object_sp, |
| const Callback &callback, Status &error) { |
| if (!object_sp || !object_sp->IsValid()) { |
| error = Status::FromErrorString("IO object is not valid."); |
| return nullptr; |
| } |
| if (object_sp->GetFdType() != IOObject::eFDTypeSocket) { |
| error = Status::FromErrorString( |
| "MainLoopWindows: non-socket types unsupported on Windows"); |
| return nullptr; |
| } |
| |
| WSAEVENT event = WSACreateEvent(); |
| if (event == WSA_INVALID_EVENT) { |
| error = |
| Status::FromErrorStringWithFormat("Cannot create monitoring event."); |
| return nullptr; |
| } |
| |
| const bool inserted = |
| m_read_fds |
| .try_emplace(object_sp->GetWaitableHandle(), FdInfo{event, callback}) |
| .second; |
| if (!inserted) { |
| WSACloseEvent(event); |
| error = Status::FromErrorStringWithFormat( |
| "File descriptor %d already monitored.", |
| object_sp->GetWaitableHandle()); |
| return nullptr; |
| } |
| |
| return CreateReadHandle(object_sp); |
| } |
| |
| void MainLoopWindows::UnregisterReadObject(IOObject::WaitableHandle handle) { |
| auto it = m_read_fds.find(handle); |
| assert(it != m_read_fds.end()); |
| BOOL result = WSACloseEvent(it->second.event); |
| assert(result == TRUE); |
| UNUSED_IF_ASSERT_DISABLED(result); |
| m_read_fds.erase(it); |
| } |
| |
| void MainLoopWindows::ProcessReadObject(IOObject::WaitableHandle handle) { |
| auto it = m_read_fds.find(handle); |
| if (it != m_read_fds.end()) |
| it->second.callback(*this); // Do the work |
| } |
| |
| Status MainLoopWindows::Run() { |
| m_terminate_request = false; |
| |
| Status error; |
| |
| while (!m_terminate_request) { |
| llvm::Expected<size_t> signaled_event = Poll(); |
| if (!signaled_event) |
| return Status::FromError(signaled_event.takeError()); |
| |
| if (*signaled_event < m_read_fds.size()) { |
| auto &KV = *std::next(m_read_fds.begin(), *signaled_event); |
| WSAResetEvent(KV.second.event); |
| ProcessReadObject(KV.first); |
| } else { |
| assert(*signaled_event == m_read_fds.size()); |
| WSAResetEvent(m_interrupt_event); |
| } |
| ProcessCallbacks(); |
| } |
| return Status(); |
| } |
| |
| void MainLoopWindows::Interrupt() { WSASetEvent(m_interrupt_event); } |