https://github.com/athierry-oct updated https://github.com/llvm/llvm-project/pull/144919
>From 52cb93996bf3d59036b84acce96c649724cf8711 Mon Sep 17 00:00:00 2001 From: Adrien Thierry <adrien.thie...@octasic.com> Date: Thu, 3 Jul 2025 10:13:28 -0400 Subject: [PATCH] [lldb] Transfer events from previous listener when hijacking This patch addresses a race condition that can occur when a script using the Python API calls SBProcess::Kill() around the same time a breakpoint is hit. If a stop event is broadcast before the hijack listener is installed, it may be delivered to the default listener and missed entirely by the hijack listener. This causes Process::WaitForProcessToStop() to hang until it times out, waiting for a public stop event that is never received. To fix this, we now transfer all pending events from the original listener to the hijack listener before hijacking, using a new Listener::MoveEvents method. We also do the reverse operation when restoring the original listener to avoid dropping any event. --- lldb/include/lldb/Utility/Listener.h | 7 +++++++ lldb/source/Utility/Broadcaster.cpp | 27 +++++++++++++++++++++++++++ lldb/source/Utility/Listener.cpp | 28 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/lldb/include/lldb/Utility/Listener.h b/lldb/include/lldb/Utility/Listener.h index d48816ec0ea4d..e48d210a3a749 100644 --- a/lldb/include/lldb/Utility/Listener.h +++ b/lldb/include/lldb/Utility/Listener.h @@ -53,6 +53,13 @@ class Listener : public std::enable_shared_from_this<Listener> { void AddEvent(lldb::EventSP &event); + // Transfers all events matching the specified broadcaster and type to the + // destination listener. This can be useful when setting up a hijack listener, + // to ensure that no relevant events are missed. + void MoveEvents(lldb::ListenerSP destination, + Broadcaster *broadcaster, // nullptr for any broadcaster + uint32_t event_type_mask); + void Clear(); const char *GetName() { return m_name.c_str(); } diff --git a/lldb/source/Utility/Broadcaster.cpp b/lldb/source/Utility/Broadcaster.cpp index c6b2606afe0c8..9d545d46bade4 100644 --- a/lldb/source/Utility/Broadcaster.cpp +++ b/lldb/source/Utility/Broadcaster.cpp @@ -335,6 +335,19 @@ bool Broadcaster::BroadcasterImpl::HijackBroadcaster( "{0} Broadcaster(\"{1}\")::HijackBroadcaster (listener(\"{2}\")={3})", static_cast<void *>(this), GetBroadcasterName(), listener_sp->m_name.c_str(), static_cast<void *>(listener_sp.get())); + + // Move pending events from the previous listener to the + // hijack listener. This ensures that no relevant event queued before the + // transition is missed by the hijack listener. + ListenerSP prev_listener; + if (!m_hijacking_listeners.empty()) + prev_listener = m_hijacking_listeners.back(); + else if (m_primary_listener_sp) + prev_listener = m_primary_listener_sp; + if (prev_listener && listener_sp) { + prev_listener->MoveEvents(listener_sp, &m_broadcaster, event_mask); + } + m_hijacking_listeners.push_back(listener_sp); m_hijacking_masks.push_back(event_mask); return true; @@ -367,6 +380,20 @@ void Broadcaster::BroadcasterImpl::RestoreBroadcaster() { static_cast<void *>(this), GetBroadcasterName(), listener_sp->m_name.c_str(), static_cast<void *>(listener_sp.get())); + + // Move any remaining events from the hijack listener back to + // the previous listener. This ensures that no events are dropped when + // restoring the original listener. + ListenerSP prev_listener; + if (m_hijacking_listeners.size() > 1) + prev_listener = m_hijacking_listeners[m_hijacking_listeners.size() - 2]; + else if (m_primary_listener_sp) + prev_listener = m_primary_listener_sp; + if (listener_sp && prev_listener && !m_hijacking_masks.empty()) { + listener_sp->MoveEvents(prev_listener, &m_broadcaster, + m_hijacking_masks.back()); + } + m_hijacking_listeners.pop_back(); } if (!m_hijacking_masks.empty()) diff --git a/lldb/source/Utility/Listener.cpp b/lldb/source/Utility/Listener.cpp index d4ce3bf25ec5a..448ecb9fcc3c4 100644 --- a/lldb/source/Utility/Listener.cpp +++ b/lldb/source/Utility/Listener.cpp @@ -176,6 +176,34 @@ void Listener::AddEvent(EventSP &event_sp) { m_events_condition.notify_all(); } +void Listener::MoveEvents( + ListenerSP destination, + Broadcaster *broadcaster, // nullptr for any broadcaster + uint32_t event_type_mask) { + Log *log = GetLog(LLDBLog::Events); + + std::lock_guard<std::mutex> guard(m_events_mutex); + auto pos = m_events.begin(); + while (pos != m_events.end()) { + EventSP &event_sp = *pos; + if (event_sp && + ((broadcaster == nullptr) || event_sp->BroadcasterIs(broadcaster)) && + (event_type_mask == 0 || event_type_mask & event_sp->GetType())) { + if (log) + LLDB_LOGF( + log, "%p Listener('%s')::MoveEvents moving event %p to %p('%s')", + static_cast<void *>(this), m_name.c_str(), + static_cast<void *>(event_sp.get()), + static_cast<void *>(destination.get()), destination->GetName()); + + destination->AddEvent(event_sp); + pos = m_events.erase(pos); + } else { + ++pos; + } + } +} + bool Listener::FindNextEventInternal( std::unique_lock<std::mutex> &lock, Broadcaster *broadcaster, // nullptr for any broadcaster _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits