[Lldb-commits] [lldb] c74c17f - [lldb] Use WSAEventSelect for MainLoop polling on windows

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Fri Aug 19 04:25:44 PDT 2022


Author: Pavel Labath
Date: 2022-08-19T13:25:25+02:00
New Revision: c74c17f37aa9360492675e2aae3de99539eaf465

URL: https://github.com/llvm/llvm-project/commit/c74c17f37aa9360492675e2aae3de99539eaf465
DIFF: https://github.com/llvm/llvm-project/commit/c74c17f37aa9360492675e2aae3de99539eaf465.diff

LOG: [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

Added: 
    lldb/include/lldb/Host/posix/MainLoopPosix.h
    lldb/include/lldb/Host/windows/MainLoopWindows.h
    lldb/source/Host/common/MainLoopBase.cpp
    lldb/source/Host/posix/MainLoopPosix.cpp
    lldb/source/Host/windows/MainLoopWindows.cpp

Modified: 
    lldb/include/lldb/Host/MainLoop.h
    lldb/include/lldb/Host/MainLoopBase.h
    lldb/source/Host/CMakeLists.txt

Removed: 
    lldb/source/Host/common/MainLoop.cpp


################################################################################
diff  --git a/lldb/include/lldb/Host/MainLoop.h b/lldb/include/lldb/Host/MainLoop.h
index f04ac7359cc18..c88b9f8920ec4 100644
--- 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 <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;
-#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;
+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
index 2a2899341a51b..8daa52cf9eaa9 100644
--- 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 <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 @@ class MainLoopBase {
 
   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 {

diff  --git a/lldb/include/lldb/Host/posix/MainLoopPosix.h b/lldb/include/lldb/Host/posix/MainLoopPosix.h
new file mode 100644
index 0000000000000..7b36771bf0bbd
--- /dev/null
+++ b/lldb/include/lldb/Host/posix/MainLoopPosix.h
@@ -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

diff  --git a/lldb/include/lldb/Host/windows/MainLoopWindows.h b/lldb/include/lldb/Host/windows/MainLoopWindows.h
new file mode 100644
index 0000000000000..fb0f05d7a306e
--- /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 <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

diff  --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt
index 64d9bb00ffab3..4a5ceeb60b7b9 100644
--- a/lldb/source/Host/CMakeLists.txt
+++ b/lldb/source/Host/CMakeLists.txt
@@ -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
     )

diff  --git a/lldb/source/Host/common/MainLoopBase.cpp b/lldb/source/Host/common/MainLoopBase.cpp
new file mode 100644
index 0000000000000..0bcf6078a28fc
--- /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
similarity index 78%
rename from lldb/source/Host/common/MainLoop.cpp
rename to lldb/source/Host/posix/MainLoopPosix.cpp
index 473a3c776436a..c0fee59bc942e 100644
--- 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 <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 @@ class MainLoop::RunImpl {
 };
 
 #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
-}

diff  --git a/lldb/source/Host/windows/MainLoopWindows.cpp b/lldb/source/Host/windows/MainLoopWindows.cpp
new file mode 100644
index 0000000000000..aca8ef21125c7
--- /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 <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();
+}


        


More information about the lldb-commits mailing list