Index: lldb/include/lldb/Host/MainLoopBase.h =================================================================== --- lldb/include/lldb/Host/MainLoopBase.h +++ lldb/include/lldb/Host/MainLoopBase.h @@ -13,7 +13,10 @@ #include "lldb/Utility/Status.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/ErrorHandling.h" + +#include #include +#include namespace lldb_private { @@ -35,12 +38,14 @@ class MainLoopBase { private: class ReadHandle; + class EventHandle; public: MainLoopBase() : m_terminate_request(false) {} virtual ~MainLoopBase() { assert(m_read_fds.size() == 0); } typedef std::unique_ptr ReadHandleUP; + typedef std::unique_ptr EventHandleUP; typedef std::function Callback; @@ -48,6 +53,8 @@ const Callback &callback, Status &error); + virtual EventHandleUP RegisterEvent(const Callback &callback); + // 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. @@ -64,16 +71,34 @@ virtual void RequestTermination() { m_terminate_request = true; } protected: + struct EventInfo { + EventInfo(Callback callback) + : notified(false), callback(std::move(callback)) {} + std::atomic notified; + Callback callback; + }; + ReadHandleUP CreateReadHandle(const lldb::IOObjectSP &object_sp) { return ReadHandleUP(new ReadHandle(*this, object_sp->GetWaitableHandle())); } virtual void UnregisterReadObject(IOObject::WaitableHandle handle); + EventHandleUP CreateEventHandle(const Callback &callback) { + return EventHandleUP( + new EventHandle(*this, m_events.emplace(m_events.end(), callback))); + } + + virtual void UnregisterEvent(std::list::iterator info_it); + void ProcessReadObject(IOObject::WaitableHandle handle); + void ProcessEvents(); void ProcessPendingCallbacks(); + virtual void NotifyEvent(std::list::iterator info_it) = 0; + llvm::DenseMap m_read_fds; + std::list m_events; std::vector m_pending_callbacks; bool m_terminate_request : 1; @@ -94,6 +119,24 @@ const ReadHandle &operator=(const ReadHandle &) = delete; }; + class EventHandle { + public: + ~EventHandle() { m_mainloop.UnregisterEvent(m_info_it); } + + void Notify() { m_mainloop.NotifyEvent(m_info_it); } + + private: + EventHandle(MainLoopBase &mainloop, std::list::iterator info_it) + : m_mainloop(mainloop), m_info_it(info_it) {} + + MainLoopBase &m_mainloop; + std::list::iterator m_info_it; + + friend class MainLoopBase; + EventHandle(const EventHandle &) = delete; + const EventHandle &operator=(const EventHandle &) = delete; + }; + MainLoopBase(const MainLoopBase &) = delete; const MainLoopBase &operator=(const MainLoopBase &) = delete; }; Index: lldb/include/lldb/Host/posix/MainLoopPosix.h =================================================================== --- lldb/include/lldb/Host/posix/MainLoopPosix.h +++ lldb/include/lldb/Host/posix/MainLoopPosix.h @@ -11,6 +11,7 @@ #include "lldb/Host/Config.h" #include "lldb/Host/MainLoopBase.h" +#include "lldb/Host/Pipe.h" #include "llvm/ADT/DenseMap.h" #include #include @@ -44,6 +45,8 @@ Status Run() override; + void NotifyEvent(std::list::iterator info_it) override; + protected: void UnregisterSignal(int signo, std::list::iterator callback_it); @@ -76,6 +79,7 @@ class RunImpl; llvm::DenseMap m_signals; + Pipe m_event_pipe; #if HAVE_SYS_EVENT_H int m_kqueue; #endif Index: lldb/include/lldb/Host/windows/MainLoopWindows.h =================================================================== --- lldb/include/lldb/Host/windows/MainLoopWindows.h +++ lldb/include/lldb/Host/windows/MainLoopWindows.h @@ -22,14 +22,20 @@ // descriptors are not supported. class MainLoopWindows : public MainLoopBase { public: + MainLoopWindows(); + ~MainLoopWindows() override; + ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Status &error) override; Status Run() override; + void NotifyEvent(std::list::iterator info_it) override; + private: llvm::Expected Poll(); + void *m_event_event; }; } // namespace lldb_private Index: lldb/source/Host/common/MainLoopBase.cpp =================================================================== --- lldb/source/Host/common/MainLoopBase.cpp +++ lldb/source/Host/common/MainLoopBase.cpp @@ -30,18 +30,38 @@ return CreateReadHandle(object_sp); } +MainLoopBase::EventHandleUP +MainLoopBase::RegisterEvent(const Callback &callback) { + return CreateEventHandle(callback); +} + void MainLoopBase::UnregisterReadObject(IOObject::WaitableHandle handle) { bool erased = m_read_fds.erase(handle); UNUSED_IF_ASSERT_DISABLED(erased); assert(erased); } +void MainLoopBase::UnregisterEvent(std::list::iterator info_it) { + m_events.erase(info_it); +} + 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::ProcessEvents() { + // Create a copy as some events may become unregistered by the callbacks. + llvm::SmallVector callbacks_to_run; + for (auto &info : m_events) { + if (info.notified.exchange(false, std::memory_order_acq_rel) == true) + callbacks_to_run.push_back(info.callback); + } + for (auto &cb : callbacks_to_run) + cb(*this); // Do the work +} + void MainLoopBase::ProcessPendingCallbacks() { for (const Callback &callback : m_pending_callbacks) callback(*this); Index: lldb/source/Host/posix/MainLoopPosix.cpp =================================================================== --- lldb/source/Host/posix/MainLoopPosix.cpp +++ lldb/source/Host/posix/MainLoopPosix.cpp @@ -7,10 +7,12 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/posix/MainLoopPosix.h" + #include "lldb/Host/Config.h" #include "lldb/Host/PosixApi.h" #include "lldb/Utility/Status.h" #include "llvm/Config/llvm-config.h" +#include "llvm/Support/Errno.h" #include #include #include @@ -222,6 +224,17 @@ #endif MainLoopPosix::MainLoopPosix() { + Status error = m_event_pipe.CreateNew(/*child_process_inherit=*/false); + assert(error.Success()); + const int event_pipe_fd = m_event_pipe.GetReadFileDescriptor(); + m_read_fds.insert({event_pipe_fd, [this, event_pipe_fd](MainLoopBase &loop) { + char c; + ssize_t bytes_read = llvm::sys::RetryAfterSignal( + -1, ::read, event_pipe_fd, &c, 1); + assert(bytes_read == 1); + UNUSED_IF_ASSERT_DISABLED(bytes_read); + ProcessEvents(); + }}); #if HAVE_SYS_EVENT_H m_kqueue = kqueue(); assert(m_kqueue >= 0); @@ -232,6 +245,8 @@ #if HAVE_SYS_EVENT_H close(m_kqueue); #endif + m_read_fds.erase(m_event_pipe.GetReadFileDescriptor()); + m_event_pipe.Close(); assert(m_signals.size() == 0); } @@ -321,7 +336,9 @@ RunImpl impl(*this); // run until termination or until we run out of things to listen to - while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty())) { + // (m_read_fds will always contain m_event_pipe fd, so check for > 1) + while (!m_terminate_request && + (m_read_fds.size() > 1 || !m_signals.empty() || !m_events.empty())) { error = impl.Poll(); if (error.Fail()) @@ -345,3 +362,14 @@ x(*this); // Do the work } } + +void MainLoopPosix::NotifyEvent(std::list::iterator info_it) { + // This must be set before we notify the event. + info_it->notified.store(true, std::memory_order_release); + char c = '.'; + size_t bytes_written; + Status error = m_event_pipe.Write(&c, 1, bytes_written); + assert(error.Success()); + UNUSED_IF_ASSERT_DISABLED(error); + assert(bytes_written == 1); +} Index: lldb/source/Host/windows/MainLoopWindows.cpp =================================================================== --- lldb/source/Host/windows/MainLoopWindows.cpp +++ lldb/source/Host/windows/MainLoopWindows.cpp @@ -21,9 +21,19 @@ using namespace lldb; using namespace lldb_private; +MainLoopWindows::MainLoopWindows() { + m_event_event = WSACreateEvent(); + assert(m_event_event != WSA_INVALID_EVENT); +} + +MainLoopWindows::~MainLoopWindows() { + BOOL result = WSACloseEvent(m_event_event); + assert(result == TRUE); +} + llvm::Expected MainLoopWindows::Poll() { - std::vector read_events; - read_events.reserve(m_read_fds.size()); + std::vector events; + events.reserve(m_read_fds.size() + 1); for (auto &fd : m_read_fds) { WSAEVENT event = WSACreateEvent(); assert(event != WSA_INVALID_EVENT); @@ -32,24 +42,25 @@ WSAEventSelect(fd.first, event, FD_READ | FD_ACCEPT | FD_CLOSE); assert(result == 0); - read_events.push_back(event); + events.push_back(event); } + events.push_back(loop.m_event_event); - DWORD result = WSAWaitForMultipleEvents( - read_events.size(), read_events.data(), FALSE, WSA_INFINITE, FALSE); + DWORD result = WSAWaitForMultipleEvents(events.size(), 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) { + events.pop_back(); + for (auto &event : events) { BOOL result = WSACloseEvent(event); assert(result == TRUE); } - if (result >= WSA_WAIT_EVENT_0 && - result < WSA_WAIT_EVENT_0 + read_events.size()) + if (result >= WSA_WAIT_EVENT_0 && result <= WSA_WAIT_EVENT_0 + events.size()) return result - WSA_WAIT_EVENT_0; return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -73,16 +84,29 @@ Status error; // run until termination or until we run out of things to listen to - while (!m_terminate_request && !m_read_fds.empty()) { + while (!m_terminate_request && (!m_read_fds.empty() || !m_events.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); + if (*signaled_event < m_read_fds.size()) { + auto &fd_info = *std::next(m_read_fds.begin(), *signaled_event); + ProcessReadObject(fd_info.first); + } else { + assert(*signaled_event == m_read_fds.size()); + // Must be called before events are processed. + WSAResetEvent(m_event_event); + ProcessEvents(); + } - ProcessReadObject(fd_info.first); ProcessPendingCallbacks(); } return Status(); } + +void MainLoopWindows::NotifyEvent(std::list::iterator info_it) { + // This must be set before we notify the event. + info_it->notified.store(true, std::memory_order_release); + WSASetEvent(m_event_event); +} Index: lldb/unittests/Host/MainLoopTest.cpp =================================================================== --- lldb/unittests/Host/MainLoopTest.cpp +++ lldb/unittests/Host/MainLoopTest.cpp @@ -148,6 +148,39 @@ ASSERT_EQ(3u, callback_count); } +TEST_F(MainLoopTest, Event) { + MainLoop loop; + bool event_called = false; + auto handle = loop.RegisterEvent([&](MainLoopBase &loop) { + event_called = true; + loop.RequestTermination(); + }); + handle->Notify(); + ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded()); + ASSERT_TRUE(event_called); +} + +TEST_F(MainLoopTest, EventAndPendingRead) { + MainLoop loop; + bool event_called = false; + auto handle = loop.RegisterEvent([&](MainLoopBase &loop) { + event_called = true; + loop.RequestTermination(); + }); + Status error; + auto socket_handle = loop.RegisterReadObject( + socketpair[1], [](MainLoopBase &) {}, error); + ASSERT_TRUE(error.Success()); + ASSERT_TRUE(socket_handle); + std::thread notifier([&]() { + sleep(1); + handle->Notify(); + }); + ASSERT_THAT_ERROR(loop.Run().ToError(), llvm::Succeeded()); + notifier.join(); + ASSERT_TRUE(event_called); +} + #ifdef LLVM_ON_UNIX TEST_F(MainLoopTest, DetectsEOF) {