[lldb] Use WSAEventSelect for MainLoop polling on windows

This patch switches the MainLoop class to use the WSAEventSelect
mechanism to wait for multiple sockets to become readable. The
motivation for doing that is that this allows us to wait for other kinds
of events as well (as long as they can be converted to WSAEvents). This
will allow us to avoid (abstract away) pipe-based multiplexing
mechanisms in the generic code, since pipes cannot be combined with
sockets on windows.

Since the windows implementation will now look completely different than
the posix (file descriptor-based) implementations, I have split the
MainLoop class into two (MainLoopPosix and MainLoopWindows), with the
common code going into MainLoopBase.

Differential Revision: https://reviews.llvm.org/D131159
This commit is contained in:
Pavel Labath 2022-08-04 11:26:36 +02:00
parent 1bd2b2dce2
commit c74c17f37a
8 changed files with 342 additions and 210 deletions

View File

@ -9,114 +9,16 @@
#ifndef LLDB_HOST_MAINLOOP_H
#define LLDB_HOST_MAINLOOP_H
#include "lldb/Host/Config.h"
#include "lldb/Host/MainLoopBase.h"
#include "llvm/ADT/DenseMap.h"
#include <csignal>
#include <list>
#include <vector>
#if !HAVE_PPOLL && !HAVE_SYS_EVENT_H && !defined(__ANDROID__)
#define SIGNAL_POLLING_UNSUPPORTED 1
#endif
#ifdef _WIN32
#include "lldb/Host/windows/MainLoopWindows.h"
namespace lldb_private {
// Implementation of the MainLoopBase class. It can monitor file descriptors
// for readability using ppoll, kqueue, poll or WSAPoll. On Windows it only
// supports polling sockets, and will not work on generic file handles or
// pipes. On systems without kqueue or ppoll handling singnals is not
// supported. In addition to the common base, this class provides the ability
// to invoke a given handler when a signal is received.
//
// Since this class is primarily intended to be used for single-threaded
// processing, it does not attempt to perform any internal synchronisation and
// any concurrent accesses must be protected externally. However, it is
// perfectly legitimate to have more than one instance of this class running on
// separate threads, or even a single thread (with some limitations on signal
// monitoring).
// TODO: Add locking if this class is to be used in a multi-threaded context.
class MainLoop : public MainLoopBase {
private:
class SignalHandle;
public:
typedef std::unique_ptr<SignalHandle> SignalHandleUP;
MainLoop();
~MainLoop() override;
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp,
const Callback &callback,
Status &error) override;
// Listening for signals from multiple MainLoop instances is perfectly safe
// as long as they don't try to listen for the same signal. The callback
// function is invoked when the control returns to the Run() function, not
// when the hander is executed. This mean that you can treat the callback as
// a normal function and perform things which would not be safe in a signal
// handler. However, since the callback is not invoked synchronously, you
// cannot use this mechanism to handle SIGSEGV and the like.
SignalHandleUP RegisterSignal(int signo, const Callback &callback,
Status &error);
// Add a pending callback that will be executed once after all the pending
// events are processed. The callback will be executed even if termination
// was requested.
void AddPendingCallback(const Callback &callback) override;
Status Run() override;
// This should only be performed from a callback. Do not attempt to terminate
// the processing from another thread.
// TODO: Add synchronization if we want to be terminated from another thread.
void RequestTermination() override { m_terminate_request = true; }
protected:
void UnregisterReadObject(IOObject::WaitableHandle handle) override;
void UnregisterSignal(int signo, std::list<Callback>::iterator callback_it);
private:
void ProcessReadObject(IOObject::WaitableHandle handle);
void ProcessSignal(int signo);
class SignalHandle {
public:
~SignalHandle() { m_mainloop.UnregisterSignal(m_signo, m_callback_it); }
private:
SignalHandle(MainLoop &mainloop, int signo,
std::list<Callback>::iterator callback_it)
: m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {}
MainLoop &m_mainloop;
int m_signo;
std::list<Callback>::iterator m_callback_it;
friend class MainLoop;
SignalHandle(const SignalHandle &) = delete;
const SignalHandle &operator=(const SignalHandle &) = delete;
};
struct SignalInfo {
std::list<Callback> callbacks;
#ifndef SIGNAL_POLLING_UNSUPPORTED
struct sigaction old_action;
using MainLoop = MainLoopWindows;
}
#else
#include "lldb/Host/posix/MainLoopPosix.h"
namespace lldb_private {
using MainLoop = MainLoopPosix;
}
#endif
bool was_blocked : 1;
};
class RunImpl;
llvm::DenseMap<IOObject::WaitableHandle, Callback> m_read_fds;
llvm::DenseMap<int, SignalInfo> m_signals;
std::vector<Callback> m_pending_callbacks;
#if HAVE_SYS_EVENT_H
int m_kqueue;
#endif
bool m_terminate_request : 1;
};
} // namespace lldb_private
#endif // LLDB_HOST_MAINLOOP_H

View File

@ -11,6 +11,7 @@
#include "lldb/Utility/IOObject.h"
#include "lldb/Utility/Status.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/ErrorHandling.h"
#include <functional>
@ -26,14 +27,17 @@ namespace lldb_private {
// of the monitoring. When this handle is destroyed, the callback is
// deregistered.
//
// This class simply defines the interface common for all platforms, actual
// implementations are platform-specific.
// Since this class is primarily intended to be used for single-threaded
// processing, it does not attempt to perform any internal synchronisation and
// any concurrent accesses must be protected externally. However, it is
// perfectly legitimate to have more than one instance of this class running on
// separate threads, or even a single thread.
class MainLoopBase {
private:
class ReadHandle;
public:
MainLoopBase() = default;
MainLoopBase() : m_terminate_request(false) {}
virtual ~MainLoopBase() = default;
typedef std::unique_ptr<ReadHandle> ReadHandleUP;
@ -42,32 +46,34 @@ public:
virtual ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp,
const Callback &callback,
Status &error) {
llvm_unreachable("Not implemented");
}
Status &error) = 0;
// Add a pending callback that will be executed once after all the pending
// events are processed. The callback will be executed even if termination
// was requested.
virtual void AddPendingCallback(const Callback &callback) {
llvm_unreachable("Not implemented");
m_pending_callbacks.push_back(callback);
}
// Waits for registered events and invoke the proper callbacks. Returns when
// all callbacks deregister themselves or when someone requests termination.
virtual Status Run() { llvm_unreachable("Not implemented"); }
// Requests the exit of the Run() function.
virtual void RequestTermination() { llvm_unreachable("Not implemented"); }
// This should only be performed from a callback. Do not attempt to terminate
// the processing from another thread.
virtual void RequestTermination() { m_terminate_request = true; }
protected:
ReadHandleUP CreateReadHandle(const lldb::IOObjectSP &object_sp) {
return ReadHandleUP(new ReadHandle(*this, object_sp->GetWaitableHandle()));
}
virtual void UnregisterReadObject(IOObject::WaitableHandle handle) {
llvm_unreachable("Not implemented");
}
virtual void UnregisterReadObject(IOObject::WaitableHandle handle) = 0;
void ProcessPendingCallbacks();
std::vector<Callback> m_pending_callbacks;
bool m_terminate_request : 1;
private:
class ReadHandle {

View File

@ -0,0 +1,93 @@
//===-- MainLoopPosix.h -----------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_HOST_POSIX_MAINLOOPPOSIX_H
#define LLDB_HOST_POSIX_MAINLOOPPOSIX_H
#include "lldb/Host/Config.h"
#include "lldb/Host/MainLoopBase.h"
#include "llvm/ADT/DenseMap.h"
#include <csignal>
#include <list>
#include <vector>
namespace lldb_private {
// Implementation of the MainLoopBase class. It can monitor file descriptors for
// readability using ppoll, kqueue, or pselect. In addition to the common base,
// this class provides the ability to invoke a given handler when a signal is
// received.
class MainLoopPosix : public MainLoopBase {
private:
class SignalHandle;
public:
typedef std::unique_ptr<SignalHandle> SignalHandleUP;
MainLoopPosix();
~MainLoopPosix() override;
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp,
const Callback &callback,
Status &error) override;
// Listening for signals from multiple MainLoop instances is perfectly safe
// as long as they don't try to listen for the same signal. The callback
// function is invoked when the control returns to the Run() function, not
// when the hander is executed. This mean that you can treat the callback as
// a normal function and perform things which would not be safe in a signal
// handler. However, since the callback is not invoked synchronously, you
// cannot use this mechanism to handle SIGSEGV and the like.
SignalHandleUP RegisterSignal(int signo, const Callback &callback,
Status &error);
Status Run() override;
protected:
void UnregisterReadObject(IOObject::WaitableHandle handle) override;
void UnregisterSignal(int signo, std::list<Callback>::iterator callback_it);
private:
void ProcessReadObject(IOObject::WaitableHandle handle);
void ProcessSignal(int signo);
class SignalHandle {
public:
~SignalHandle() { m_mainloop.UnregisterSignal(m_signo, m_callback_it); }
private:
SignalHandle(MainLoopPosix &mainloop, int signo,
std::list<Callback>::iterator callback_it)
: m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {}
MainLoopPosix &m_mainloop;
int m_signo;
std::list<Callback>::iterator m_callback_it;
friend class MainLoopPosix;
SignalHandle(const SignalHandle &) = delete;
const SignalHandle &operator=(const SignalHandle &) = delete;
};
struct SignalInfo {
std::list<Callback> callbacks;
struct sigaction old_action;
bool was_blocked : 1;
};
class RunImpl;
llvm::DenseMap<IOObject::WaitableHandle, Callback> m_read_fds;
llvm::DenseMap<int, SignalInfo> m_signals;
#if HAVE_SYS_EVENT_H
int m_kqueue;
#endif
};
} // namespace lldb_private
#endif // LLDB_HOST_POSIX_MAINLOOPPOSIX_H

View File

@ -0,0 +1,49 @@
//===-- MainLoopWindows.h ---------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_HOST_WINDOWS_MAINLOOPWINDOWS_H
#define LLDB_HOST_WINDOWS_MAINLOOPWINDOWS_H
#include "lldb/Host/Config.h"
#include "lldb/Host/MainLoopBase.h"
#include <csignal>
#include <list>
#include <vector>
namespace lldb_private {
// Windows-specific implementation of the MainLoopBase class. It can monitor
// socket descriptors for readability using WSAEventSelect. Non-socket file
// descriptors are not supported.
class MainLoopWindows : public MainLoopBase {
public:
~MainLoopWindows() override { assert(m_read_fds.empty()); }
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp,
const Callback &callback,
Status &error) override;
Status Run() override;
protected:
void UnregisterReadObject(IOObject::WaitableHandle handle) override;
private:
void ProcessReadObject(IOObject::WaitableHandle handle);
llvm::Expected<size_t> Poll();
struct FdInfo {
void *event;
Callback callback;
};
llvm::DenseMap<IOObject::WaitableHandle, FdInfo> m_read_fds;
};
} // namespace lldb_private
#endif // LLDB_HOST_WINDOWS_MAINLOOPWINDOWS_H

View File

@ -24,7 +24,7 @@ add_host_subdirectory(common
common/HostThread.cpp
common/LockFileBase.cpp
common/LZMA.cpp
common/MainLoop.cpp
common/MainLoopBase.cpp
common/MonitoringProcessLauncher.cpp
common/NativeProcessProtocol.cpp
common/NativeRegisterContext.cpp
@ -63,6 +63,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows")
windows/HostProcessWindows.cpp
windows/HostThreadWindows.cpp
windows/LockFileWindows.cpp
windows/MainLoopWindows.cpp
windows/PipeWindows.cpp
windows/ProcessLauncherWindows.cpp
windows/ProcessRunLock.cpp
@ -75,6 +76,7 @@ else()
posix/HostProcessPosix.cpp
posix/HostThreadPosix.cpp
posix/LockFilePosix.cpp
posix/MainLoopPosix.cpp
posix/PipePosix.cpp
posix/ProcessLauncherPosixFork.cpp
)

View File

@ -0,0 +1,18 @@
//===-- MainLoopBase.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/MainLoopBase.h"
using namespace lldb;
using namespace lldb_private;
void MainLoopBase::ProcessPendingCallbacks() {
for (const Callback &callback : m_pending_callbacks)
callback(*this);
m_pending_callbacks.clear();
}

View File

@ -1,4 +1,4 @@
//===-- MainLoop.cpp ------------------------------------------------------===//
//===-- MainLoopPosix.cpp -------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -6,12 +6,11 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/Config/llvm-config.h"
#include "lldb/Host/posix/MainLoopPosix.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/PosixApi.h"
#include "lldb/Utility/Status.h"
#include "llvm/Config/llvm-config.h"
#include <algorithm>
#include <cassert>
#include <cerrno>
@ -26,59 +25,32 @@
#if HAVE_SYS_EVENT_H
#include <sys/event.h>
#elif defined(_WIN32)
#include <winsock2.h>
#elif defined(__ANDROID__)
#include <sys/syscall.h>
#else
#include <poll.h>
#endif
#ifdef _WIN32
#define POLL WSAPoll
#else
#define POLL poll
#endif
#if SIGNAL_POLLING_UNSUPPORTED
#ifdef _WIN32
typedef int sigset_t;
typedef int siginfo_t;
#endif
int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout_ts,
const sigset_t *) {
int timeout =
(timeout_ts == nullptr)
? -1
: (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
return POLL(fds, nfds, timeout);
}
#endif
using namespace lldb;
using namespace lldb_private;
static sig_atomic_t g_signal_flags[NSIG];
#ifndef SIGNAL_POLLING_UNSUPPORTED
static void SignalHandler(int signo, siginfo_t *info, void *) {
assert(signo < NSIG);
g_signal_flags[signo] = 1;
}
#endif
class MainLoop::RunImpl {
class MainLoopPosix::RunImpl {
public:
RunImpl(MainLoop &loop);
RunImpl(MainLoopPosix &loop);
~RunImpl() = default;
Status Poll();
void ProcessEvents();
private:
MainLoop &loop;
MainLoopPosix &loop;
#if HAVE_SYS_EVENT_H
std::vector<struct kevent> in_events;
@ -97,11 +69,11 @@ private:
};
#if HAVE_SYS_EVENT_H
MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
in_events.reserve(loop.m_read_fds.size());
}
Status MainLoop::RunImpl::Poll() {
Status MainLoopPosix::RunImpl::Poll() {
in_events.resize(loop.m_read_fds.size());
unsigned i = 0;
for (auto &fd : loop.m_read_fds)
@ -121,7 +93,7 @@ Status MainLoop::RunImpl::Poll() {
return Status();
}
void MainLoop::RunImpl::ProcessEvents() {
void MainLoopPosix::RunImpl::ProcessEvents() {
assert(num_events >= 0);
for (int i = 0; i < num_events; ++i) {
if (loop.m_terminate_request)
@ -139,31 +111,25 @@ void MainLoop::RunImpl::ProcessEvents() {
}
}
#else
MainLoop::RunImpl::RunImpl(MainLoop &loop) : loop(loop) {
MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
#ifndef __ANDROID__
read_fds.reserve(loop.m_read_fds.size());
#endif
}
sigset_t MainLoop::RunImpl::get_sigmask() {
sigset_t MainLoopPosix::RunImpl::get_sigmask() {
sigset_t sigmask;
#if defined(_WIN32)
sigmask = 0;
#elif SIGNAL_POLLING_UNSUPPORTED
sigemptyset(&sigmask);
#else
int ret = pthread_sigmask(SIG_SETMASK, nullptr, &sigmask);
assert(ret == 0);
(void) ret;
(void)ret;
for (const auto &sig : loop.m_signals)
sigdelset(&sigmask, sig.first);
#endif
return sigmask;
}
#ifdef __ANDROID__
Status MainLoop::RunImpl::Poll() {
Status MainLoopPosix::RunImpl::Poll() {
// ppoll(2) is not supported on older all android versions. Also, older
// versions android (API <= 19) implemented pselect in a non-atomic way, as a
// combination of pthread_sigmask and select. This is not sufficient for us,
@ -196,7 +162,7 @@ Status MainLoop::RunImpl::Poll() {
return Status();
}
#else
Status MainLoop::RunImpl::Poll() {
Status MainLoopPosix::RunImpl::Poll() {
read_fds.clear();
sigset_t sigmask = get_sigmask();
@ -217,7 +183,7 @@ Status MainLoop::RunImpl::Poll() {
}
#endif
void MainLoop::RunImpl::ProcessEvents() {
void MainLoopPosix::RunImpl::ProcessEvents() {
#ifdef __ANDROID__
// Collect first all readable file descriptors into a separate vector and
// then iterate over it to invoke callbacks. Iterating directly over
@ -255,29 +221,24 @@ void MainLoop::RunImpl::ProcessEvents() {
}
#endif
MainLoop::MainLoop() : m_terminate_request(false) {
MainLoopPosix::MainLoopPosix() {
#if HAVE_SYS_EVENT_H
m_kqueue = kqueue();
assert(m_kqueue >= 0);
#endif
}
MainLoop::~MainLoop() {
MainLoopPosix::~MainLoopPosix() {
#if HAVE_SYS_EVENT_H
close(m_kqueue);
#endif
assert(m_read_fds.size() == 0);
assert(m_read_fds.size() == 0);
assert(m_signals.size() == 0);
}
MainLoop::ReadHandleUP MainLoop::RegisterReadObject(const IOObjectSP &object_sp,
const Callback &callback,
Status &error) {
#ifdef _WIN32
if (object_sp->GetFdType() != IOObject:: eFDTypeSocket) {
error.SetErrorString("MainLoop: non-socket types unsupported on Windows");
return nullptr;
}
#endif
MainLoopPosix::ReadHandleUP
MainLoopPosix::RegisterReadObject(const IOObjectSP &object_sp,
const Callback &callback, Status &error) {
if (!object_sp || !object_sp->IsValid()) {
error.SetErrorString("IO object is not valid.");
return nullptr;
@ -296,12 +257,9 @@ MainLoop::ReadHandleUP MainLoop::RegisterReadObject(const IOObjectSP &object_sp,
// We shall block the signal, then install the signal handler. The signal will
// be unblocked in the Run() function to check for signal delivery.
MainLoop::SignalHandleUP
MainLoop::RegisterSignal(int signo, const Callback &callback, Status &error) {
#ifdef SIGNAL_POLLING_UNSUPPORTED
error.SetErrorString("Signal polling is not supported on this platform.");
return nullptr;
#else
MainLoopPosix::SignalHandleUP
MainLoopPosix::RegisterSignal(int signo, const Callback &callback,
Status &error) {
auto signal_it = m_signals.find(signo);
if (signal_it != m_signals.end()) {
auto callback_it = signal_it->second.callbacks.insert(
@ -344,24 +302,16 @@ MainLoop::RegisterSignal(int signo, const Callback &callback, Status &error) {
return SignalHandleUP(new SignalHandle(
*this, signo, insert_ret.first->second.callbacks.begin()));
#endif
}
void MainLoop::AddPendingCallback(const Callback &callback) {
m_pending_callbacks.push_back(callback);
}
void MainLoop::UnregisterReadObject(IOObject::WaitableHandle handle) {
void MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) {
bool erased = m_read_fds.erase(handle);
UNUSED_IF_ASSERT_DISABLED(erased);
assert(erased);
}
void MainLoop::UnregisterSignal(int signo,
std::list<Callback>::iterator callback_it) {
#if SIGNAL_POLLING_UNSUPPORTED
Status("Signal polling is not supported on this platform.");
#else
void MainLoopPosix::UnregisterSignal(
int signo, std::list<Callback>::iterator callback_it) {
auto it = m_signals.find(signo);
assert(it != m_signals.end());
@ -388,10 +338,9 @@ void MainLoop::UnregisterSignal(int signo,
#endif
m_signals.erase(it);
#endif
}
Status MainLoop::Run() {
Status MainLoopPosix::Run() {
m_terminate_request = false;
Status error;
@ -406,14 +355,18 @@ Status MainLoop::Run() {
impl.ProcessEvents();
for (const Callback &callback : m_pending_callbacks)
callback(*this);
m_pending_callbacks.clear();
ProcessPendingCallbacks();
}
return Status();
}
void MainLoop::ProcessSignal(int signo) {
void MainLoopPosix::ProcessReadObject(IOObject::WaitableHandle handle) {
auto it = m_read_fds.find(handle);
if (it != m_read_fds.end())
it->second(*this); // Do the work
}
void MainLoopPosix::ProcessSignal(int signo) {
auto it = m_signals.find(signo);
if (it != m_signals.end()) {
// The callback may actually register/unregister signal handlers,
@ -424,9 +377,3 @@ void MainLoop::ProcessSignal(int signo) {
x(*this); // Do the work
}
}
void MainLoop::ProcessReadObject(IOObject::WaitableHandle handle) {
auto it = m_read_fds.find(handle);
if (it != m_read_fds.end())
it->second(*this); // Do the work
}

View File

@ -0,0 +1,115 @@
//===-- 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;
llvm::Expected<size_t> MainLoopWindows::Poll() {
std::vector<WSAEVENT> read_events;
read_events.reserve(m_read_fds.size());
for (auto &[fd, info] : m_read_fds) {
int result = WSAEventSelect(fd, info.event, FD_READ | FD_ACCEPT | FD_CLOSE);
assert(result == 0);
read_events.push_back(info.event);
}
DWORD result = WSAWaitForMultipleEvents(
read_events.size(), read_events.data(), FALSE, WSA_INFINITE, FALSE);
for (auto &fd : m_read_fds) {
int result = WSAEventSelect(fd.first, WSA_INVALID_EVENT, 0);
assert(result == 0);
}
if (result >= WSA_WAIT_EVENT_0 &&
result < WSA_WAIT_EVENT_0 + read_events.size())
return result - WSA_WAIT_EVENT_0;
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.SetErrorString("IO object is not valid.");
return nullptr;
}
if (object_sp->GetFdType() != IOObject::eFDTypeSocket) {
error.SetErrorString(
"MainLoopWindows: non-socket types unsupported on Windows");
return nullptr;
}
WSAEVENT event = WSACreateEvent();
if (event == WSA_INVALID_EVENT) {
error.SetErrorStringWithFormat("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.SetErrorStringWithFormat("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);
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;
// run until termination or until we run out of things to listen to
while (!m_terminate_request && !m_read_fds.empty()) {
llvm::Expected<size_t> signaled_event = Poll();
if (!signaled_event)
return Status(signaled_event.takeError());
auto &KV = *std::next(m_read_fds.begin(), *signaled_event);
ProcessReadObject(KV.first);
ProcessPendingCallbacks();
}
return Status();
}