diff --git a/lldb/include/lldb/Host/MainLoop.h b/lldb/include/lldb/Host/MainLoop.h --- a/lldb/include/lldb/Host/MainLoop.h +++ b/lldb/include/lldb/Host/MainLoop.h @@ -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 -#include -#include - -#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 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::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::iterator callback_it) - : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {} - - MainLoop &m_mainloop; - int m_signo; - std::list::iterator m_callback_it; - - friend class MainLoop; - SignalHandle(const SignalHandle &) = delete; - const SignalHandle &operator=(const SignalHandle &) = delete; - }; - - struct SignalInfo { - std::list callbacks; -#ifndef SIGNAL_POLLING_UNSUPPORTED - struct sigaction old_action; -#endif - bool was_blocked : 1; - }; - class RunImpl; - - llvm::DenseMap m_read_fds; - llvm::DenseMap m_signals; - std::vector m_pending_callbacks; -#if HAVE_SYS_EVENT_H - int m_kqueue; +using MainLoop = MainLoopWindows; +} +#else +#include "lldb/Host/posix/MainLoopPosix.h" +namespace lldb_private { +using MainLoop = MainLoopPosix; +} #endif - bool m_terminate_request : 1; -}; - -} // namespace lldb_private #endif // LLDB_HOST_MAINLOOP_H diff --git a/lldb/include/lldb/Host/MainLoopBase.h b/lldb/include/lldb/Host/MainLoopBase.h --- a/lldb/include/lldb/Host/MainLoopBase.h +++ b/lldb/include/lldb/Host/MainLoopBase.h @@ -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 @@ -26,15 +27,18 @@ // 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; - virtual ~MainLoopBase() = default; + MainLoopBase() : m_terminate_request(false) {} + virtual ~MainLoopBase() { assert(m_read_fds.size() == 0); } typedef std::unique_ptr ReadHandleUP; @@ -42,32 +46,36 @@ virtual ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, - Status &error) { - llvm_unreachable("Not implemented"); - } + 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. 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); + + void ProcessReadObject(IOObject::WaitableHandle handle); + void ProcessPendingCallbacks(); + + llvm::DenseMap m_read_fds; + std::vector m_pending_callbacks; + bool m_terminate_request : 1; private: class ReadHandle { diff --git a/lldb/include/lldb/Host/posix/MainLoopPosix.h b/lldb/include/lldb/Host/posix/MainLoopPosix.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Host/posix/MainLoopPosix.h @@ -0,0 +1,86 @@ +//===-- 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 +#include +#include + +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 SignalHandleUP; + + MainLoopPosix(); + ~MainLoopPosix() 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 UnregisterSignal(int signo, std::list::iterator callback_it); + +private: + void ProcessSignal(int signo); + + class SignalHandle { + public: + ~SignalHandle() { m_mainloop.UnregisterSignal(m_signo, m_callback_it); } + + private: + SignalHandle(MainLoopPosix &mainloop, int signo, + std::list::iterator callback_it) + : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {} + + MainLoopPosix &m_mainloop; + int m_signo; + std::list::iterator m_callback_it; + + friend class MainLoopPosix; + SignalHandle(const SignalHandle &) = delete; + const SignalHandle &operator=(const SignalHandle &) = delete; + }; + + struct SignalInfo { + std::list callbacks; + struct sigaction old_action; + bool was_blocked : 1; + }; + class RunImpl; + + llvm::DenseMap m_signals; +#if HAVE_SYS_EVENT_H + int m_kqueue; +#endif +}; + +} // namespace lldb_private + +#endif // LLDB_HOST_POSIX_MAINLOOPPOSIX_H diff --git a/lldb/include/lldb/Host/windows/MainLoopWindows.h b/lldb/include/lldb/Host/windows/MainLoopWindows.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Host/windows/MainLoopWindows.h @@ -0,0 +1,37 @@ +//===-- 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 +#include +#include + +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: + ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, + const Callback &callback, + Status &error) override; + + Status Run() override; + +private: + llvm::Expected Poll(); +}; + +} // namespace lldb_private + +#endif // LLDB_HOST_WINDOWS_MAINLOOPWINDOWS_H diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -24,7 +24,7 @@ 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 @@ 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 @@ posix/HostProcessPosix.cpp posix/HostThreadPosix.cpp posix/LockFilePosix.cpp + posix/MainLoopPosix.cpp posix/PipePosix.cpp posix/ProcessLauncherPosixFork.cpp ) diff --git a/lldb/source/Host/common/MainLoopBase.cpp b/lldb/source/Host/common/MainLoopBase.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Host/common/MainLoopBase.cpp @@ -0,0 +1,49 @@ +//===-- 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; + +MainLoopBase::ReadHandleUP +MainLoopBase::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; + } + + const bool inserted = + m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second; + if (!inserted) { + error.SetErrorStringWithFormat("File descriptor %d already monitored.", + object_sp->GetWaitableHandle()); + return nullptr; + } + + return CreateReadHandle(object_sp); +} + +void MainLoopBase::UnregisterReadObject(IOObject::WaitableHandle handle) { + bool erased = m_read_fds.erase(handle); + UNUSED_IF_ASSERT_DISABLED(erased); + assert(erased); +} + +void MainLoopBase::ProcessReadObject(IOObject::WaitableHandle handle) { + auto it = m_read_fds.find(handle); + if (it != m_read_fds.end()) + it->second(*this); // Do the work +} + +void MainLoopBase::ProcessPendingCallbacks() { + for (const Callback &callback : m_pending_callbacks) + callback(*this); + m_pending_callbacks.clear(); +} diff --git a/lldb/source/Host/common/MainLoop.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp rename from lldb/source/Host/common/MainLoop.cpp rename to lldb/source/Host/posix/MainLoopPosix.cpp --- a/lldb/source/Host/common/MainLoop.cpp +++ b/lldb/source/Host/posix/MainLoopPosix.cpp @@ -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 #include #include @@ -26,59 +25,32 @@ #if HAVE_SYS_EVENT_H #include -#elif defined(_WIN32) -#include #elif defined(__ANDROID__) #include #else #include #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 in_events; @@ -97,11 +69,11 @@ }; #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 @@ 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 @@ } } #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 @@ return Status(); } #else -Status MainLoop::RunImpl::Poll() { +Status MainLoopPosix::RunImpl::Poll() { read_fds.clear(); sigset_t sigmask = get_sigmask(); @@ -217,7 +183,7 @@ } #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,53 +221,25 @@ } #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_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 - if (!object_sp || !object_sp->IsValid()) { - error.SetErrorString("IO object is not valid."); - return nullptr; - } - - const bool inserted = - m_read_fds.insert({object_sp->GetWaitableHandle(), callback}).second; - if (!inserted) { - error.SetErrorStringWithFormat("File descriptor %d already monitored.", - object_sp->GetWaitableHandle()); - return nullptr; - } - - return CreateReadHandle(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 +282,10 @@ 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) { - bool erased = m_read_fds.erase(handle); - UNUSED_IF_ASSERT_DISABLED(erased); - assert(erased); -} - -void MainLoop::UnregisterSignal(int signo, - std::list::iterator callback_it) { -#if SIGNAL_POLLING_UNSUPPORTED - Status("Signal polling is not supported on this platform."); -#else +void MainLoopPosix::UnregisterSignal( + int signo, std::list::iterator callback_it) { auto it = m_signals.find(signo); assert(it != m_signals.end()); @@ -388,10 +312,9 @@ #endif m_signals.erase(it); -#endif } -Status MainLoop::Run() { +Status MainLoopPosix::Run() { m_terminate_request = false; Status error; @@ -406,14 +329,12 @@ 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::ProcessSignal(int signo) { auto it = m_signals.find(signo); if (it != m_signals.end()) { // The callback may actually register/unregister signal handlers, @@ -424,9 +345,3 @@ 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 -} diff --git a/lldb/source/Host/windows/MainLoopWindows.cpp b/lldb/source/Host/windows/MainLoopWindows.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Host/windows/MainLoopWindows.cpp @@ -0,0 +1,88 @@ +//===-- 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 +#include +#include +#include +#include +#include +#include + +using namespace lldb; +using namespace lldb_private; + +llvm::Expected MainLoopWindows::Poll() { + std::vector read_events; + read_events.reserve(m_read_fds.size()); + for (auto &fd : m_read_fds) { + WSAEVENT event = WSACreateEvent(); + assert(event != WSA_INVALID_EVENT); + + int result = + WSAEventSelect(fd.first, event, FD_READ | FD_ACCEPT | FD_CLOSE); + assert(result == 0); + + read_events.push_back(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); + } + + for (auto &event : read_events) { + BOOL result = WSACloseEvent(event); + assert(result == TRUE); + } + + 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->GetFdType() != IOObject::eFDTypeSocket) { + error.SetErrorString( + "MainLoopWindows: non-socket types unsupported on Windows"); + return nullptr; + } + return MainLoopBase::RegisterReadObject(object_sp, callback, error); +} + +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 signaled_event = Poll(); + if (!signaled_event) + return Status(signaled_event.takeError()); + + auto &fd_info = *std::next(m_read_fds.begin(), *signaled_event); + + ProcessReadObject(fd_info.first); + ProcessPendingCallbacks(); + } + return Status(); +}