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 @@ -39,9 +39,12 @@ class MainLoop : public MainLoopBase { private: class SignalHandle; + class EventHandle; + struct EventInfo; public: typedef std::unique_ptr SignalHandleUP; + typedef std::unique_ptr EventHandleUP; MainLoop(); ~MainLoop() override; @@ -60,6 +63,8 @@ SignalHandleUP RegisterSignal(int signo, const Callback &callback, Status &error); + 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. @@ -77,9 +82,14 @@ void UnregisterSignal(int signo, std::list::iterator callback_it); + void UnregisterEvent(std::list::iterator info_it); + private: void ProcessReadObject(IOObject::WaitableHandle handle); void ProcessSignal(int signo); + void ProcessEvents(); + + void NotifyEvent(std::list::iterator info_it); class SignalHandle { public: @@ -99,6 +109,19 @@ const SignalHandle &operator=(const SignalHandle &) = delete; }; + class EventHandle { + public: + EventHandle(MainLoop &mainloop, std::list::iterator info_it) + : m_mainloop(mainloop), m_info_it(info_it) {} + ~EventHandle() { m_mainloop.UnregisterEvent(m_info_it); } + + void Notify() { m_mainloop.NotifyEvent(m_info_it); } + + private: + MainLoop &m_mainloop; + std::list::iterator m_info_it; + }; + struct SignalInfo { std::list callbacks; #ifndef SIGNAL_POLLING_UNSUPPORTED @@ -106,13 +129,25 @@ #endif bool was_blocked : 1; }; + struct EventInfo { + EventInfo(Callback callback) + : notified(false), callback(std::move(callback)) {} + std::atomic notified; + Callback callback; + }; class RunImpl; llvm::DenseMap m_read_fds; llvm::DenseMap m_signals; + std::list m_events; std::vector m_pending_callbacks; #if HAVE_SYS_EVENT_H int m_kqueue; +#endif +#ifdef _WIN32 + void *m_event_event; +#else + Pipe m_event_pipe; #endif bool m_terminate_request : 1; }; diff --git a/lldb/source/Host/common/MainLoop.cpp b/lldb/source/Host/common/MainLoop.cpp --- a/lldb/source/Host/common/MainLoop.cpp +++ b/lldb/source/Host/common/MainLoop.cpp @@ -91,8 +91,8 @@ } Status MainLoop::RunImpl::Poll() { - std::vector read_events; - read_events.reserve(loop.m_read_fds.size()); + std::vector events; + events.reserve(loop.m_read_fds.size() + 1); for(auto &fd : loop.m_read_fds) { WSAEVENT event = WSACreateEvent(); assert(event != WSA_INVALID_EVENT); @@ -100,23 +100,25 @@ int result = 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 : loop.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()) { signaled_event = result - WSA_WAIT_EVENT_0; return Status(); } @@ -125,9 +127,14 @@ void MainLoop::RunImpl::ProcessEvents() { assert(signaled_event); - auto &fd_info = *std::next(loop.m_read_fds.begin(), *signaled_event); - - loop.ProcessReadObject(fd_info.first); + if (*signaled_event < loop.m_read_fds.size()) { + auto &fd_info = *std::next(loop.m_read_fds.begin(), *signaled_event); + loop.ProcessReadObject(fd_info.first); + } else { + // Must be called before events are processed. + WSAResetEvent(loop.m_event_event); + loop.ProcessEvents(); + } } #else class MainLoop::RunImpl { @@ -317,11 +324,27 @@ #endif #endif +void MainLoop::NotifyEvent(std::list::iterator info_it) { + // This must be set before we notify the event. + info_it->notified.store(true, std::memory_order_release); +#ifdef _WIN32 + WSASetEvent(m_event_event); +#else + ... +#endif +} + MainLoop::MainLoop() : m_terminate_request(false) { #if HAVE_SYS_EVENT_H m_kqueue = kqueue(); assert(m_kqueue >= 0); #endif +#ifdef _WIN32 + m_event_event = WSACreateEvent(); + assert(m_event_event != WSA_INVALID_EVENT); +#else + ... +#endif } MainLoop::~MainLoop() { #if HAVE_SYS_EVENT_H @@ -409,6 +432,10 @@ #endif } +MainLoop::EventHandleUP MainLoop::RegisterEvent(const Callback &callback) { + return EventHandleUP(new EventHandle(*this, m_events.emplace(m_events.end(), callback))); +} + void MainLoop::AddPendingCallback(const Callback &callback) { m_pending_callbacks.push_back(callback); } @@ -419,6 +446,10 @@ assert(erased); } +void MainLoop::UnregisterEvent(std::list::iterator info_it) { + m_events.erase(info_it); +} + void MainLoop::UnregisterSignal(int signo, std::list::iterator callback_it) { #if SIGNAL_POLLING_UNSUPPORTED @@ -460,7 +491,7 @@ 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())) { + while (!m_terminate_request && (!m_read_fds.empty() || !m_signals.empty() || !m_events.empty())) { error = impl.Poll(); if (error.Fail()) @@ -492,3 +523,14 @@ if (it != m_read_fds.end()) it->second(*this); // Do the work } + +void MainLoop::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 +} diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp --- a/lldb/unittests/Host/MainLoopTest.cpp +++ b/lldb/unittests/Host/MainLoopTest.cpp @@ -148,6 +148,18 @@ 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); +} + #ifdef LLVM_ON_UNIX TEST_F(MainLoopTest, DetectsEOF) {