This revision was not accepted when it landed; it landed in state "Needs 
Revision".
This revision was automatically updated to reflect the committed changes.
Closed by commit rG2e7aa2ee34eb: Replace the singleton 
"ShadowListener" with a primary and N secondary Listeners (authored 
by jingham).

Changed prior to commit:
  https://reviews.llvm.org/D157556?vs=550495&id=550806#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D157556/new/

https://reviews.llvm.org/D157556

Files:
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Utility/Broadcaster.h
  lldb/include/lldb/Utility/Event.h
  lldb/source/Target/Process.cpp
  lldb/source/Utility/Broadcaster.cpp
  lldb/source/Utility/Event.cpp
  lldb/source/Utility/Listener.cpp
  lldb/test/API/api/listeners/TestListener.py
  
lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py
  lldb/test/API/python_api/event/TestEvents.py

Index: lldb/test/API/python_api/event/TestEvents.py
===================================================================
--- lldb/test/API/python_api/event/TestEvents.py
+++ lldb/test/API/python_api/event/TestEvents.py
@@ -313,3 +313,121 @@
         self.assertEqual(
             self.state, "stopped", "Both expected state changed events received"
         )
+
+    def wait_for_next_event(self, expected_state, test_shadow = False):
+        """Wait for an event from self.primary & self.shadow listener.
+           If test_shadow is true, we also check that the shadow listener only 
+           receives events AFTER the primary listener does."""
+        # Waiting on the shadow listener shouldn't have events yet because
+        # we haven't fetched them for the primary listener yet:
+        event = lldb.SBEvent()
+
+        if test_shadow:
+            success = self.shadow_listener.WaitForEvent(1, event)
+            self.assertFalse(success, "Shadow listener doesn't pull events")
+
+        # But there should be an event for the primary listener:
+        success = self.primary_listener.WaitForEvent(5, event)
+        self.assertTrue(success, "Primary listener got the event")
+
+        state = lldb.SBProcess.GetStateFromEvent(event)
+        restart = False
+        if state == lldb.eStateStopped:
+            restart = lldb.SBProcess.GetRestartedFromEvent(event)
+
+        if expected_state != None:
+            self.assertEqual(state, expected_state, "Primary thread got the correct event")
+            
+        # And after pulling that one there should be an equivalent event for the shadow
+        # listener:
+        success = self.shadow_listener.WaitForEvent(5, event)
+        self.assertTrue(success, "Shadow listener got event too")
+        self.assertEqual(state, lldb.SBProcess.GetStateFromEvent(event), "It was the same event")
+        self.assertEqual(restart, lldb.SBProcess.GetRestartedFromEvent(event), "It was the same restarted")
+            
+        return state, restart
+
+    def test_shadow_listener(self):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Now create a breakpoint on main.c by name 'c'.
+        bkpt1 = target.BreakpointCreateByName("c", "a.out")
+        self.trace("breakpoint:", bkpt1)
+        self.assertTrue(bkpt1.GetNumLocations() == 1, VALID_BREAKPOINT)
+
+        self.primary_listener = lldb.SBListener("my listener")
+        self.shadow_listener = lldb.SBListener("shadow listener")
+
+        self.cur_thread = None
+        
+        error = lldb.SBError()
+        launch_info = target.GetLaunchInfo()
+        launch_info.SetListener(self.primary_listener)
+        launch_info.SetShadowListener(self.shadow_listener)
+
+        self.runCmd("settings set target.process.extra-startup-command QSetLogging:bitmask=LOG_PROCESS|LOG_EXCEPTIONS|LOG_RNB_PACKETS|LOG_STEP;")
+        self.dbg.SetAsync(True)
+
+        self.process = target.Launch(launch_info, error)
+        self.assertSuccess(error, "Process launched successfully")
+
+        # Keep fetching events from the primary to trigger the do on removal and
+        # then from the shadow listener, and make sure they match:
+
+        # Events in the launch sequence might be platform dependent, so don't
+        # expect any particular event till we get the stopped:
+        state = lldb.eStateInvalid
+        while state != lldb.eStateStopped:
+            state, restart = self.wait_for_next_event(None, False)
+
+        # Okay, we're now at a good stop, so try a next:
+        self.cur_thread = self.process.threads[0]
+
+        # Make sure we're at our expected breakpoint:
+        self.assertTrue(self.cur_thread.IsValid(), "Got a zeroth thread")
+        self.assertEqual(self.cur_thread.stop_reason, lldb.eStopReasonBreakpoint)
+        self.assertEqual(self.cur_thread.GetStopReasonDataCount(), 2, "Only one breakpoint/loc here")
+        self.assertEqual(bkpt1.GetID(), self.cur_thread.GetStopReasonDataAtIndex(0), "Hit the right breakpoint")
+        # Disable the first breakpoint so it doesn't get in the way...
+        bkpt1.SetEnabled(False)
+
+        self.cur_thread.StepOver()
+        # We'll run the test for "shadow listener blocked by primary listener
+        # for the first couple rounds, then we'll skip the 1 second pause...
+        self.wait_for_next_event(lldb.eStateRunning, True)
+        self.wait_for_next_event(lldb.eStateStopped, True)
+
+        # Next try an auto-continue breakpoint and make sure the shadow listener got
+        # the resumed info as well.  Note that I'm not explicitly counting
+        # running events here.  At the point when I wrote this lldb sometimes
+        # emits two running events in a row.  Apparently the code to coalesce running
+        # events isn't working.  But that's not what this test is testing, we're really
+        # testing that the primary & shadow listeners hear the same thing and in the
+        # right order.
+
+        main_spec = lldb.SBFileSpec("main.c")
+        bkpt2 = target.BreakpointCreateBySourceRegex("b.2. returns %d", main_spec)
+        self.assertTrue(bkpt2.GetNumLocations() > 0, "BP2 worked")
+        bkpt2.SetAutoContinue(True)
+
+        bkpt3 = target.BreakpointCreateBySourceRegex("a.3. returns %d", main_spec)
+        self.assertTrue(bkpt3.GetNumLocations() > 0, "BP3 worked")
+
+        state = lldb.eStateStopped
+        restarted = False
+
+        # Put in a counter to make sure we don't spin forever if there is some
+        # error in the logic.
+        counter = 0
+        while state != lldb.eStateExited:
+            counter += 1
+            self.assertLess(counter, 50, "Took more than 50 events to hit two breakpoints.")
+            if state == lldb.eStateStopped and not restarted:
+                self.process.Continue()
+            state, restarted  = self.wait_for_next_event(None, False)
+            
Index: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py
===================================================================
--- lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py
+++ lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py
@@ -22,8 +22,10 @@
         self.script_module = "interactive_scripted_process"
         self.script_file = self.script_module + ".py"
 
+    # These tests are flakey and sometimes timeout.  They work most of the time
+    # so the basic event flow is right, but somehow the handling is off.
     @skipUnlessDarwin
-    @skipIfDarwin
+    @skipIfDarwin 
     def test_passthrough_launch(self):
         """Test a simple pass-through process launch"""
         self.passthrough_launch()
@@ -34,6 +36,15 @@
         self.assertSuccess(error, "Resuming multiplexer scripted process")
         self.assertTrue(self.mux_process.IsValid(), "Got a valid process")
 
+        event = lldbutil.fetch_next_event(
+            self, self.dbg.GetListener(), self.mux_process.GetBroadcaster(), timeout=20
+        )
+        self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning)
+        event = lldbutil.fetch_next_event(
+            self, self.dbg.GetListener(), self.mux_process.GetBroadcaster()
+        )
+        self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped)
+
         event = lldbutil.fetch_next_event(
             self, self.mux_process_listener, self.mux_process.GetBroadcaster()
         )
@@ -178,6 +189,13 @@
             )
             execution_events[event_target_idx][state] = True
 
+        for _ in range((self.dbg.GetNumTargets() - 1) * 2):
+            fetch_process_event(self, execution_events)
+
+        for target_index, event_states in execution_events.items():
+            for state, is_set in event_states.items():
+                self.assertTrue(is_set, f"Target {target_index} has state {state} set")
+
         event = lldbutil.fetch_next_event(
             self, self.mux_process_listener, self.mux_process.GetBroadcaster()
         )
@@ -188,13 +206,6 @@
         )
         self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped)
 
-        for _ in range((self.dbg.GetNumTargets() - 1) * 2):
-            fetch_process_event(self, execution_events)
-
-        for target_index, event_states in execution_events.items():
-            for state, is_set in event_states.items():
-                self.assertTrue(is_set, f"Target {target_index} has state {state} set")
-
     def duplicate_target(self, driving_target):
         exe = driving_target.executable.fullpath
         triple = driving_target.triple
@@ -248,14 +259,14 @@
         self.assertSuccess(error, "Launched multiplexer scripted process")
         self.assertTrue(self.mux_process.IsValid(), "Got a valid process")
 
-        # Check that the mux process started running
+        # Check that the real process started running
         event = lldbutil.fetch_next_event(
-            self, self.mux_process_listener, self.mux_process.GetBroadcaster()
+            self, self.dbg.GetListener(), self.mux_process.GetBroadcaster()
         )
         self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning)
-        # Check that the real process started running
+        # Check that the mux process started running
         event = lldbutil.fetch_next_event(
-            self, self.dbg.GetListener(), self.mux_process.GetBroadcaster()
+            self, self.mux_process_listener, self.mux_process.GetBroadcaster()
         )
         self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning)
 
Index: lldb/test/API/api/listeners/TestListener.py
===================================================================
--- lldb/test/API/api/listeners/TestListener.py
+++ lldb/test/API/api/listeners/TestListener.py
@@ -52,7 +52,6 @@
         self.build()
 
         my_listener = lldb.SBListener("test_listener")
-
         my_listener.StartListeningForEventClass(
             self.dbg,
             lldb.SBTarget.GetBroadcasterClassName(),
Index: lldb/source/Utility/Listener.cpp
===================================================================
--- lldb/source/Utility/Listener.cpp
+++ lldb/source/Utility/Listener.cpp
@@ -231,8 +231,7 @@
       // to return it so it should be okay to get the next event off the queue
       // here - and it might be useful to do that in the "DoOnRemoval".
       lock.unlock();
-      if (!m_is_shadow)
-        event_sp->DoOnRemoval();
+      event_sp->DoOnRemoval();
     }
     return true;
   }
Index: lldb/source/Utility/Event.cpp
===================================================================
--- lldb/source/Utility/Event.cpp
+++ lldb/source/Utility/Event.cpp
@@ -11,6 +11,7 @@
 #include "lldb/Utility/Broadcaster.h"
 #include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Listener.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/lldb-enumerations.h"
@@ -80,8 +81,16 @@
 }
 
 void Event::DoOnRemoval() {
+  std::lock_guard<std::mutex> guard(m_listeners_mutex);
+
   if (m_data_sp)
     m_data_sp->DoOnRemoval(this);
+  // Now that the event has been handled by the primary event Listener, forward
+  // it to the other Listeners.
+  EventSP me_sp = shared_from_this();
+  for (auto listener_sp : m_pending_listeners)
+    listener_sp->AddEvent(me_sp);
+  m_pending_listeners.clear();
 }
 
 #pragma mark -
Index: lldb/source/Utility/Broadcaster.cpp
===================================================================
--- lldb/source/Utility/Broadcaster.cpp
+++ lldb/source/Utility/Broadcaster.cpp
@@ -50,22 +50,42 @@
 }
 
 llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4>
-Broadcaster::BroadcasterImpl::GetListeners() {
+Broadcaster::BroadcasterImpl::GetListeners(uint32_t event_mask,
+                                           bool include_primary) {
   llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> listeners;
-  listeners.reserve(m_listeners.size());
+  size_t max_count = m_listeners.size();
+  if (include_primary)
+    max_count++;
+  listeners.reserve(max_count);
 
   for (auto it = m_listeners.begin(); it != m_listeners.end();) {
     lldb::ListenerSP curr_listener_sp(it->first.lock());
-    if (curr_listener_sp && it->second) {
-      listeners.emplace_back(std::move(curr_listener_sp), it->second);
+    if (curr_listener_sp) {
+      if (it->second & event_mask)
+        listeners.emplace_back(std::move(curr_listener_sp), it->second);
       ++it;
     } else
+      // If our listener_wp didn't resolve, then we should remove this entry.
       it = m_listeners.erase(it);
   }
+  if (include_primary && m_primary_listener_sp)
+    listeners.emplace_back(m_primary_listener_sp, m_primary_listener_mask);
 
   return listeners;
 }
 
+bool Broadcaster::BroadcasterImpl::HasListeners(uint32_t event_mask) {
+  if (m_primary_listener_sp)
+    return true;
+  for (auto it = m_listeners.begin(); it != m_listeners.end(); it++) {
+    // Don't return a listener if the other end of the WP is gone:
+    lldb::ListenerSP curr_listener_sp(it->first.lock());
+    if (curr_listener_sp && (it->second & event_mask))
+      return true;
+  }
+  return false;
+}
+
 void Broadcaster::BroadcasterImpl::Clear() {
   std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
 
@@ -75,6 +95,7 @@
     pair.first->BroadcasterWillDestruct(&m_broadcaster);
 
   m_listeners.clear();
+  m_primary_listener_sp.reset();
 }
 
 Broadcaster *Broadcaster::BroadcasterImpl::GetBroadcaster() {
@@ -122,7 +143,11 @@
 
   bool handled = false;
 
-  for (auto &pair : GetListeners()) {
+  if (listener_sp == m_primary_listener_sp)
+    // This already handles all bits so just return the mask:
+    return event_mask;
+
+  for (auto &pair : GetListeners(UINT32_MAX, false)) {
     if (pair.first == listener_sp) {
       handled = true;
       pair.second |= event_mask;
@@ -151,11 +176,11 @@
   if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back())
     return true;
 
-  for (auto &pair : GetListeners()) {
-    if (pair.second & event_type)
-      return true;
-  }
-  return false;
+  // The primary listener listens for all event bits:
+  if (m_primary_listener_sp)
+    return true;
+
+  return HasListeners(event_type);
 }
 
 bool Broadcaster::BroadcasterImpl::RemoveListener(
@@ -163,12 +188,33 @@
   if (!listener)
     return false;
 
+  if (listener == m_primary_listener_sp.get()) {
+    // Primary listeners listen for all the event bits for their broadcaster,
+    // so remove this altogether if asked:
+    m_primary_listener_sp.reset();
+    return true;
+  }
+
   std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
-  for (auto &pair : GetListeners()) {
-    if (pair.first.get() == listener) {
-      pair.second &= ~event_mask;
-      return true;
+  for (auto it = m_listeners.begin(); it != m_listeners.end();) {
+    lldb::ListenerSP curr_listener_sp(it->first.lock());
+
+    if (!curr_listener_sp) {
+      // The weak pointer for this listener didn't resolve, lets' prune it
+      // as we go.
+      m_listeners.erase(it);
+      continue;
     }
+
+    if (curr_listener_sp.get() == listener) {
+      it->second &= ~event_mask;
+      // If we removed all the event bits from a listener, remove it from
+      // the list as well.
+      if (!it->second)
+        m_listeners.erase(it);
+      return true;
+    } else
+      it++;
   }
   return false;
 }
@@ -222,25 +268,34 @@
              event_description.GetData(), unique,
              static_cast<void *>(hijacking_listener_sp.get()));
   }
+  ListenerSP primary_listener_sp
+      = hijacking_listener_sp ? hijacking_listener_sp : m_primary_listener_sp;
 
-  if (hijacking_listener_sp) {
-    if (unique && hijacking_listener_sp->PeekAtNextEventForBroadcasterWithType(
+  if (primary_listener_sp) {
+    if (unique && primary_listener_sp->PeekAtNextEventForBroadcasterWithType(
                       &m_broadcaster, event_type))
       return;
-    hijacking_listener_sp->AddEvent(event_sp);
-    if (m_shadow_listener)
-      m_shadow_listener->AddEvent(event_sp);
+    // Add the pending listeners but not if the event is hijacked, since that
+    // is given sole access to the event stream it is hijacking.
+    // Make sure to do this before adding the event to the primary or it might
+    // start handling the event before we're done adding all the pending
+    // listeners.
+    if (!hijacking_listener_sp) {
+      for (auto &pair : GetListeners(event_type, false)) {
+        if (unique && pair.first->PeekAtNextEventForBroadcasterWithType(
+                          &m_broadcaster, event_type))
+          continue;
+        event_sp->AddPendingListener(pair.first);
+      }
+    }
+    primary_listener_sp->AddEvent(event_sp);
   } else {
-    for (auto &pair : GetListeners()) {
-      if (!(pair.second & event_type))
-        continue;
+    for (auto &pair : GetListeners(event_type)) {
       if (unique && pair.first->PeekAtNextEventForBroadcasterWithType(
                         &m_broadcaster, event_type))
         continue;
 
       pair.first->AddEvent(event_sp);
-      if (m_shadow_listener)
-        m_shadow_listener->AddEvent(event_sp);
     }
   }
 }
@@ -263,6 +318,15 @@
   PrivateBroadcastEvent(event_sp, true);
 }
 
+void Broadcaster::BroadcasterImpl::SetPrimaryListener(lldb::ListenerSP
+                                                      listener_sp) {
+  // This might have already been added as a normal listener, make sure we
+  // don't hold two copies.
+  RemoveListener(listener_sp.get(), UINT32_MAX);
+  m_primary_listener_sp = listener_sp;
+                                                      
+}
+
 bool Broadcaster::BroadcasterImpl::HijackBroadcaster(
     const lldb::ListenerSP &listener_sp, uint32_t event_mask) {
   std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -438,7 +438,7 @@
       m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this),
       m_thread_list(this), m_thread_plans(*this), m_extended_thread_list(this),
       m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0),
-      m_notifications(), m_image_tokens(), m_listener_sp(listener_sp),
+      m_notifications(), m_image_tokens(),
       m_breakpoint_site_list(), m_dynamic_checkers_up(),
       m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(),
       m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
@@ -474,10 +474,9 @@
   m_private_state_control_broadcaster.SetEventName(
       eBroadcastInternalStateControlResume, "control-resume");
 
-  m_listener_sp->StartListeningForEvents(
-      this, eBroadcastBitStateChanged | eBroadcastBitInterrupt |
-                eBroadcastBitSTDOUT | eBroadcastBitSTDERR |
-                eBroadcastBitProfileData | eBroadcastBitStructuredData);
+  // The listener passed into process creation is the primary listener:
+  // It always listens for all the event bits for Process:
+  SetPrimaryListener(listener_sp);
 
   m_private_state_listener_sp->StartListeningForEvents(
       &m_private_state_broadcaster,
@@ -618,7 +617,7 @@
 StateType Process::GetNextEvent(EventSP &event_sp) {
   StateType state = eStateInvalid;
 
-  if (m_listener_sp->GetEventForBroadcaster(this, event_sp,
+  if (GetPrimaryListener()->GetEventForBroadcaster(this, event_sp,
                                             std::chrono::seconds(0)) &&
       event_sp)
     state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
@@ -966,7 +965,7 @@
 
   ListenerSP listener_sp = hijack_listener_sp;
   if (!listener_sp)
-    listener_sp = m_listener_sp;
+    listener_sp = GetPrimaryListener();
 
   StateType state = eStateInvalid;
   if (listener_sp->GetEventForBroadcasterWithType(
@@ -988,7 +987,7 @@
   LLDB_LOGF(log, "Process::%s...", __FUNCTION__);
 
   Event *event_ptr;
-  event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType(
+  event_ptr = GetPrimaryListener()->PeekAtNextEventForBroadcasterWithType(
       this, eBroadcastBitStateChanged);
   if (log) {
     if (event_ptr) {
@@ -4104,8 +4103,8 @@
   if (!still_should_stop && does_anybody_have_an_opinion) {
     // We've been asked to continue, so do that here.
     SetRestarted(true);
-    // Use the public resume method here, since this is just extending a
-    // public resume.
+    // Use the private resume method here, since we aren't changing the run
+    // lock state.
     process_sp->PrivateResume();
   } else {
     bool hijacked = process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) &&
Index: lldb/include/lldb/Utility/Event.h
===================================================================
--- lldb/include/lldb/Utility/Event.h
+++ lldb/include/lldb/Utility/Event.h
@@ -176,7 +176,7 @@
 };
 
 // lldb::Event
-class Event {
+class Event : public std::enable_shared_from_this<Event> {
   friend class Listener;
   friend class EventData;
   friend class Broadcaster::BroadcasterImpl;
@@ -226,6 +226,12 @@
 
   void Clear() { m_data_sp.reset(); }
 
+  /// This is used by Broadcasters with Primary Listeners to store the other
+  /// Listeners till after the Event's DoOnRemoval has completed.
+  void AddPendingListener(lldb::ListenerSP pending_listener_sp) {
+    m_pending_listeners.push_back(pending_listener_sp);
+  };
+
 private:
   // This is only called by Listener when it pops an event off the queue for
   // the listener.  It calls the Event Data's DoOnRemoval() method, which is
@@ -244,6 +250,8 @@
       m_broadcaster_wp;        // The broadcaster that sent this event
   uint32_t m_type;             // The bit describing this event
   lldb::EventDataSP m_data_sp; // User specific data for this event
+  std::vector<lldb::ListenerSP> m_pending_listeners;
+  std::mutex m_listeners_mutex;
 
   Event(const Event &) = delete;
   const Event &operator=(const Event &) = delete;
Index: lldb/include/lldb/Utility/Broadcaster.h
===================================================================
--- lldb/include/lldb/Utility/Broadcaster.h
+++ lldb/include/lldb/Utility/Broadcaster.h
@@ -312,8 +312,12 @@
 
   lldb::BroadcasterManagerSP GetManager();
 
-  virtual void SetShadowListener(lldb::ListenerSP listener_sp) {
-    m_broadcaster_sp->m_shadow_listener = listener_sp;
+  void SetPrimaryListener(lldb::ListenerSP listener_sp) {
+    m_broadcaster_sp->SetPrimaryListener(listener_sp);
+  }
+
+  lldb::ListenerSP GetPrimaryListener() {
+    return m_broadcaster_sp->m_primary_listener_sp;
   }
 
 protected:
@@ -377,6 +381,8 @@
 
     bool EventTypeHasListeners(uint32_t event_type);
 
+    void SetPrimaryListener(lldb::ListenerSP listener_sp);
+
     bool RemoveListener(lldb_private::Listener *listener,
                         uint32_t event_mask = UINT32_MAX);
 
@@ -400,7 +406,9 @@
     typedef std::map<uint32_t, std::string> event_names_map;
 
     llvm::SmallVector<std::pair<lldb::ListenerSP, uint32_t &>, 4>
-    GetListeners();
+    GetListeners(uint32_t event_mask = UINT32_MAX, bool include_primary = true);
+
+    bool HasListeners(uint32_t event_mask);
 
     /// The broadcaster that this implements.
     Broadcaster &m_broadcaster;
@@ -409,6 +417,27 @@
     /// event bit.
     event_names_map m_event_names;
 
+    /// A Broadcaster can have zero, one or many listeners.  A Broadcaster with
+    /// zero listeners is a no-op, with one Listener is trivial.
+    /// In most cases of multiple Listeners,the Broadcaster treats all its
+    /// Listeners as equal, sending each event to all of the Listeners in no
+    /// guaranteed order.
+    /// However, some Broadcasters - in particular the Process broadcaster, can
+    /// designate one Listener to be the "Primary Listener".  In the case of
+    /// the Process Broadcaster, the Listener passed to the Process constructor
+    /// will be the Primary Listener.
+    /// If the broadcaster has a Primary Listener, then the event gets
+    /// sent first to the Primary Listener, and then when the Primary Listener
+    /// pulls the event and the the event's DoOnRemoval finishes running,
+    /// the event is forwarded to all the other Listeners.
+    /// The other wrinkle is that a Broadcaster may be serving a Hijack
+    /// Listener.  If the Hijack Listener is present, events are only sent to
+    /// the Hijack Listener.  We use that, for instance, to absorb all the
+    /// events generated by running an expression so that they don't show up to
+    /// the driver or UI as starts and stops.
+    /// If a Broadcaster has both a Primary and a Hijack Listener, the top-most
+    /// Hijack Listener is treated as the current Primary Listener.
+
     /// A list of Listener / event_mask pairs that are listening to this
     /// broadcaster.
     collection m_listeners;
@@ -416,6 +445,11 @@
     /// A mutex that protects \a m_listeners.
     std::recursive_mutex m_listeners_mutex;
 
+    /// See the discussion of Broadcasters and Listeners above.
+    lldb::ListenerSP m_primary_listener_sp;
+    // The primary listener listens to all bits:
+    uint32_t m_primary_listener_mask = UINT32_MAX;
+
     /// A simple mechanism to intercept events from a broadcaster
     std::vector<lldb::ListenerSP> m_hijacking_listeners;
 
@@ -423,10 +457,6 @@
     /// for now this is just for private hijacking.
     std::vector<uint32_t> m_hijacking_masks;
 
-    /// A optional listener that all private events get also broadcasted to,
-    /// on top the hijacked / default listeners.
-    lldb::ListenerSP m_shadow_listener = nullptr;
-
   private:
     BroadcasterImpl(const BroadcasterImpl &) = delete;
     const BroadcasterImpl &operator=(const BroadcasterImpl &) = delete;
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -352,6 +352,14 @@
     eBroadcastBitProfileData = (1 << 4),
     eBroadcastBitStructuredData = (1 << 5),
   };
+  // This is all the event bits the public process broadcaster broadcasts.
+  // The process shadow listener signs up for all these bits...
+  static constexpr int g_all_event_bits = eBroadcastBitStateChanged 
+                                        | eBroadcastBitInterrupt
+                                        | eBroadcastBitSTDOUT 
+                                        | eBroadcastBitSTDERR
+                                        | eBroadcastBitProfileData 
+                                        | eBroadcastBitStructuredData;
 
   enum {
     eBroadcastInternalStateControlStop = (1 << 0),
@@ -382,11 +390,7 @@
   ConstString &GetBroadcasterClass() const override {
     return GetStaticBroadcasterClass();
   }
-
-  void SetShadowListener(lldb::ListenerSP listener_sp) override {
-    Broadcaster::SetShadowListener(listener_sp);
-  }
-
+  
 /// A notification structure that can be used by clients to listen
 /// for changes in a process's lifetime.
 ///
@@ -610,6 +614,15 @@
     return error;
   }
 
+  /// The "ShadowListener" for a process is just an ordinary Listener that 
+  /// listens for all the Process event bits.  It's convenient because you can
+  /// specify it in the LaunchInfo or AttachInfo, so it will get events from
+  /// the very start of the process.
+  void SetShadowListener(lldb::ListenerSP shadow_listener_sp) {
+    if (shadow_listener_sp)
+      AddListener(shadow_listener_sp, g_all_event_bits);
+  }
+
   // FUTURE WORK: GetLoadImageUtilityFunction are the first use we've
   // had of having other plugins cache data in the Process.  This is handy for
   // long-living plugins - like the Platform - which manage interactions whose
@@ -2979,8 +2992,6 @@
   std::vector<Notifications> m_notifications; ///< The list of notifications
                                               ///that this process can deliver.
   std::vector<lldb::addr_t> m_image_tokens;
-  lldb::ListenerSP m_listener_sp; ///< Shared pointer to the listener used for
-                                  ///public events.  Can not be empty.
   BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint
                                              ///locations we intend to insert in
                                              ///the target.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to