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,14 +27,17 @@ // 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 ReadHandleUP; @@ -42,32 +46,34 @@ 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 m_pending_callbacks; + bool m_terminate_request : 1; private: class ReadHandle { diff --git a/lldb/include/lldb/Host/MainLoop.h b/lldb/include/lldb/Host/posix/MainLoopPosix.h copy from lldb/include/lldb/Host/MainLoop.h copy to lldb/include/lldb/Host/posix/MainLoopPosix.h --- a/lldb/include/lldb/Host/MainLoop.h +++ b/lldb/include/lldb/Host/posix/MainLoopPosix.h @@ -1,4 +1,4 @@ -//===-- MainLoop.h ----------------------------------------------*- C++ -*-===// +//===-- 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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_HOST_MAINLOOP_H -#define LLDB_HOST_MAINLOOP_H +#ifndef LLDB_HOST_POSIX_MAINLOOPPOSIX_H +#define LLDB_HOST_POSIX_MAINLOOPPOSIX_H #include "lldb/Host/Config.h" #include "lldb/Host/MainLoopBase.h" @@ -16,35 +16,21 @@ #include #include -#if !HAVE_PPOLL && !HAVE_SYS_EVENT_H && !defined(__ANDROID__) -#define SIGNAL_POLLING_UNSUPPORTED 1 -#endif - 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 { +// 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; - MainLoop(); - ~MainLoop() override; + MainLoopPosix(); + ~MainLoopPosix() override; ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, @@ -60,21 +46,10 @@ 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: @@ -86,37 +61,33 @@ ~SignalHandle() { m_mainloop.UnregisterSignal(m_signo, m_callback_it); } private: - SignalHandle(MainLoop &mainloop, int signo, + SignalHandle(MainLoopPosix &mainloop, int signo, std::list::iterator callback_it) : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {} - MainLoop &m_mainloop; + MainLoopPosix &m_mainloop; int m_signo; std::list::iterator m_callback_it; - friend class MainLoop; + friend class MainLoopPosix; 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; #endif - bool m_terminate_request : 1; }; } // namespace lldb_private -#endif // LLDB_HOST_MAINLOOP_H +#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,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 +#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: + ~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 Poll(); + + struct FdInfo { + void *event; + Callback callback; + }; + llvm::DenseMap m_read_fds; +}; + +} // 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,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(); +} 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,29 +221,24 @@ } #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 @@ // 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 @@ 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::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 +338,9 @@ #endif m_signals.erase(it); -#endif } -Status MainLoop::Run() { +Status MainLoopPosix::Run() { m_terminate_request = false; Status error; @@ -406,14 +355,18 @@ 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 @@ 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,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 +#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, 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 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(); +}