[Lldb-commits] [lldb] [lldb] Fix race condition in Process::WaitForProcessToStop() (PR #144919)

via lldb-commits lldb-commits at lists.llvm.org
Thu Jul 3 11:30:00 PDT 2025


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.thierry at 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



More information about the lldb-commits mailing list