text: //===-- Listener.cpp ------------------------------------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "lldb/Utility/Listener.h"
  
  #include "lldb/Utility/Broadcaster.h"
  #include "lldb/Utility/ConstString.h"
  #include "lldb/Utility/Event.h"
  #include "lldb/Utility/Log.h"
  #include "lldb/Utility/Logging.h"
  
  #include "llvm/ADT/Optional.h"
  
  #include <algorithm>
  #include <memory>
  #include <utility>
  
  using namespace lldb;
  using namespace lldb_private;
  
  namespace {
  class BroadcasterManagerWPMatcher {
  public:
    BroadcasterManagerWPMatcher(BroadcasterManagerSP manager_sp)
        : m_manager_sp(std::move(manager_sp)) {}
    bool operator()(const BroadcasterManagerWP &input_wp) const {
      BroadcasterManagerSP input_sp = input_wp.lock();
      return (input_sp && input_sp == m_manager_sp);
    }
  
    BroadcasterManagerSP m_manager_sp;
  };
  } // anonymous namespace
  
  Listener::Listener(const char *name)
      : m_name(name), m_broadcasters(), m_broadcasters_mutex(), m_events(),
        m_events_mutex() {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
    if (log != nullptr)
      LLDB_LOGF(log, "%p Listener::Listener('%s')", static_cast<void *>(this),
                m_name.c_str());
  }
  
  Listener::~Listener() {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
  
    Clear();
  
    LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this),
              __FUNCTION__, m_name.c_str());
  }
  
  void Listener::Clear() {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
    std::lock_guard<std::recursive_mutex> broadcasters_guard(
        m_broadcasters_mutex);
    broadcaster_collection::iterator pos, end = m_broadcasters.end();
    for (pos = m_broadcasters.begin(); pos != end; ++pos) {
      Broadcaster::BroadcasterImplSP broadcaster_sp(pos->first.lock());
      if (broadcaster_sp)
        broadcaster_sp->RemoveListener(this, pos->second.event_mask);
    }
    m_broadcasters.clear();
  
    std::lock_guard<std::mutex> events_guard(m_events_mutex);
    m_events.clear();
    size_t num_managers = m_broadcaster_managers.size();
  
    for (size_t i = 0; i < num_managers; i++) {
      BroadcasterManagerSP manager_sp(m_broadcaster_managers[i].lock());
      if (manager_sp)
        manager_sp->RemoveListener(this);
    }
  
    LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this),
              __FUNCTION__, m_name.c_str());
  }
  
  uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster,
                                             uint32_t event_mask) {
    if (broadcaster) {
      // Scope for "locker"
      // Tell the broadcaster to add this object as a listener
      {
        std::lock_guard<std::recursive_mutex> broadcasters_guard(
            m_broadcasters_mutex);
        Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl());
        m_broadcasters.insert(
            std::make_pair(impl_wp, BroadcasterInfo(event_mask)));
      }
  
      uint32_t acquired_mask =
          broadcaster->AddListener(this->shared_from_this(), event_mask);
  
      Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
      if (log != nullptr)
        LLDB_LOGF(log,
                  "%p Listener::StartListeningForEvents (broadcaster = %p, "
                  "mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s",
                  static_cast<void *>(this), static_cast<void *>(broadcaster),
                  event_mask, acquired_mask, m_name.c_str());
  
      return acquired_mask;
    }
    return 0;
  }
  
  uint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster,
                                             uint32_t event_mask,
                                             HandleBroadcastCallback callback,
                                             void *callback_user_data) {
    if (broadcaster) {
      // Scope for "locker"
      // Tell the broadcaster to add this object as a listener
      {
        std::lock_guard<std::recursive_mutex> broadcasters_guard(
            m_broadcasters_mutex);
        Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl());
        m_broadcasters.insert(std::make_pair(
            impl_wp, BroadcasterInfo(event_mask, callback, callback_user_data)));
      }
  
      uint32_t acquired_mask =
          broadcaster->AddListener(this->shared_from_this(), event_mask);
  
      Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
      if (log != nullptr) {
        void **pointer = reinterpret_cast<void **>(&callback);
        LLDB_LOGF(log,
                  "%p Listener::StartListeningForEvents (broadcaster = %p, "
                  "mask = 0x%8.8x, callback = %p, user_data = %p) "
                  "acquired_mask = 0x%8.8x for %s",
                  static_cast<void *>(this), static_cast<void *>(broadcaster),
                  event_mask, *pointer, static_cast<void *>(callback_user_data),
                  acquired_mask, m_name.c_str());
      }
  
      return acquired_mask;
    }
    return 0;
  }
  
  bool Listener::StopListeningForEvents(Broadcaster *broadcaster,
                                        uint32_t event_mask) {
    if (broadcaster) {
      // Scope for "locker"
      {
        std::lock_guard<std::recursive_mutex> broadcasters_guard(
            m_broadcasters_mutex);
        m_broadcasters.erase(broadcaster->GetBroadcasterImpl());
      }
      // Remove the broadcaster from our set of broadcasters
      return broadcaster->RemoveListener(this->shared_from_this(), event_mask);
    }
  
    return false;
  }
  
  // Called when a Broadcaster is in its destructor. We need to remove all
  // knowledge of this broadcaster and any events that it may have queued up
  void Listener::BroadcasterWillDestruct(Broadcaster *broadcaster) {
    // Scope for "broadcasters_locker"
    {
      std::lock_guard<std::recursive_mutex> broadcasters_guard(
          m_broadcasters_mutex);
      m_broadcasters.erase(broadcaster->GetBroadcasterImpl());
    }
  
    // Scope for "event_locker"
    {
      std::lock_guard<std::mutex> events_guard(m_events_mutex);
      // Remove all events for this broadcaster object.
      event_collection::iterator pos = m_events.begin();
      while (pos != m_events.end()) {
        if ((*pos)->GetBroadcaster() == broadcaster)
          pos = m_events.erase(pos);
        else
          ++pos;
      }
    }
  }
  
  void Listener::BroadcasterManagerWillDestruct(BroadcasterManagerSP manager_sp) {
    // Just need to remove this broadcast manager from the list of managers:
    broadcaster_manager_collection::iterator iter,
        end_iter = m_broadcaster_managers.end();
    BroadcasterManagerWP manager_wp;
  
    BroadcasterManagerWPMatcher matcher(std::move(manager_sp));
    iter = std::find_if<broadcaster_manager_collection::iterator,
                        BroadcasterManagerWPMatcher>(
        m_broadcaster_managers.begin(), end_iter, matcher);
    if (iter != end_iter)
      m_broadcaster_managers.erase(iter);
  }
  
  void Listener::AddEvent(EventSP &event_sp) {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
    if (log != nullptr)
      LLDB_LOGF(log, "%p Listener('%s')::AddEvent (event_sp = {%p})",
                static_cast<void *>(this), m_name.c_str(),
                static_cast<void *>(event_sp.get()));
  
    std::lock_guard<std::mutex> guard(m_events_mutex);
    m_events.push_back(event_sp);
    m_events_condition.notify_all();
  }
  
  class EventBroadcasterMatches {
  public:
    EventBroadcasterMatches(Broadcaster *broadcaster)
        : m_broadcaster(broadcaster) {}
  
    bool operator()(const EventSP &event_sp) const {
      return event_sp->BroadcasterIs(m_broadcaster);
    }
  
  private:
    Broadcaster *m_broadcaster;
  };
  
  class EventMatcher {
  public:
    EventMatcher(Broadcaster *broadcaster, const ConstString *broadcaster_names,
                 uint32_t num_broadcaster_names, uint32_t event_type_mask)
        : m_broadcaster(broadcaster), m_broadcaster_names(broadcaster_names),
          m_num_broadcaster_names(num_broadcaster_names),
          m_event_type_mask(event_type_mask) {}
  
    bool operator()(const EventSP &event_sp) const {
      if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster))
        return false;
  
      if (m_broadcaster_names) {
        bool found_source = false;
        ConstString event_broadcaster_name =
            event_sp->GetBroadcaster()->GetBroadcasterName();
        for (uint32_t i = 0; i < m_num_broadcaster_names; ++i) {
          if (m_broadcaster_names[i] == event_broadcaster_name) {
            found_source = true;
            break;
          }
        }
        if (!found_source)
          return false;
      }
  
      return m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType();
    }
  
  private:
    Broadcaster *m_broadcaster;
    const ConstString *m_broadcaster_names;
    const uint32_t m_num_broadcaster_names;
    const uint32_t m_event_type_mask;
  };
  
  bool Listener::FindNextEventInternal(
      std::unique_lock<std::mutex> &lock,
      Broadcaster *broadcaster,             // nullptr for any broadcaster
      const ConstString *broadcaster_names, // nullptr for any event
      uint32_t num_broadcaster_names, uint32_t event_type_mask, EventSP &event_sp,
      bool remove) {
    // NOTE: callers of this function must lock m_events_mutex using a
    // Mutex::Locker
    // and pass the locker as the first argument. m_events_mutex is no longer
    // recursive.
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
  
    if (m_events.empty())
      return false;
  
    Listener::event_collection::iterator pos = m_events.end();
  
    if (broadcaster == nullptr && broadcaster_names == nullptr &&
        event_type_mask == 0) {
      pos = m_events.begin();
    } else {
      pos = std::find_if(m_events.begin(), m_events.end(),
                         EventMatcher(broadcaster, broadcaster_names,
                                      num_broadcaster_names, event_type_mask));
    }
  
    if (pos != m_events.end()) {
      event_sp = *pos;
  
      if (log != nullptr)
        LLDB_LOGF(log,
                  "%p '%s' Listener::FindNextEventInternal(broadcaster=%p, "
                  "broadcaster_names=%p[%u], event_type_mask=0x%8.8x, "
                  "remove=%i) event %p",
                  static_cast<void *>(this), GetName(),
                  static_cast<void *>(broadcaster),
                  static_cast<const void *>(broadcaster_names),
                  num_broadcaster_names, event_type_mask, remove,
                  static_cast<void *>(event_sp.get()));
  
      if (remove) {
        m_events.erase(pos);
        // Unlock the event queue here.  We've removed this event and are about
        // 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();
        event_sp->DoOnRemoval();
      }
      return true;
    }
  
    event_sp.reset();
    return false;
  }
  
  Event *Listener::PeekAtNextEvent() {
    std::unique_lock<std::mutex> guard(m_events_mutex);
    EventSP event_sp;
    if (FindNextEventInternal(guard, nullptr, nullptr, 0, 0, event_sp, false))
      return event_sp.get();
    return nullptr;
  }
  
  Event *Listener::PeekAtNextEventForBroadcaster(Broadcaster *broadcaster) {
    std::unique_lock<std::mutex> guard(m_events_mutex);
    EventSP event_sp;
    if (FindNextEventInternal(guard, broadcaster, nullptr, 0, 0, event_sp, false))
      return event_sp.get();
    return nullptr;
  }
  
  Event *
  Listener::PeekAtNextEventForBroadcasterWithType(Broadcaster *broadcaster,
                                                  uint32_t event_type_mask) {
    std::unique_lock<std::mutex> guard(m_events_mutex);
    EventSP event_sp;
    if (FindNextEventInternal(guard, broadcaster, nullptr, 0, event_type_mask,
                              event_sp, false))
      return event_sp.get();
    return nullptr;
  }
  
  bool Listener::GetEventInternal(
      const Timeout<std::micro> &timeout,
      Broadcaster *broadcaster,             // nullptr for any broadcaster
      const ConstString *broadcaster_names, // nullptr for any event
      uint32_t num_broadcaster_names, uint32_t event_type_mask,
      EventSP &event_sp) {
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
    LLDB_LOG(log, "this = {0}, timeout = {1} for {2}", this, timeout, m_name);
  
    std::unique_lock<std::mutex> lock(m_events_mutex);
  
    while (true) {
      if (FindNextEventInternal(lock, broadcaster, broadcaster_names,
                                num_broadcaster_names, event_type_mask, event_sp,
                                true)) {
        return true;
      } else {
        std::cv_status result = std::cv_status::no_timeout;
        if (!timeout)
          m_events_condition.wait(lock);
        else
          result = m_events_condition.wait_for(lock, *timeout);
  
        if (result == std::cv_status::timeout) {
          log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS);
          LLDB_LOGF(log, "%p Listener::GetEventInternal() timed out for %s",
                    static_cast<void *>(this), m_name.c_str());
          return false;
        } else if (result != std::cv_status::no_timeout) {
          log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS);
          LLDB_LOGF(log, "%p Listener::GetEventInternal() unknown error for %s",
                    static_cast<void *>(this), m_name.c_str());
          return false;
        }
      }
    }
  
    return false;
  }
  
  bool Listener::GetEventForBroadcasterWithType(
      Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp,
      const Timeout<std::micro> &timeout) {
    return GetEventInternal(timeout, broadcaster, nullptr, 0, event_type_mask,
                            event_sp);
  }
  
  bool Listener::GetEventForBroadcaster(Broadcaster *broadcaster,
                                        EventSP &event_sp,
                                        const Timeout<std::micro> &timeout) {
    return GetEventInternal(timeout, broadcaster, nullptr, 0, 0, event_sp);
  }
  
  bool Listener::GetEvent(EventSP &event_sp, const Timeout<std::micro> &timeout) {
    return GetEventInternal(timeout, nullptr, nullptr, 0, 0, event_sp);
  }
  
  size_t Listener::HandleBroadcastEvent(EventSP &event_sp) {
    size_t num_handled = 0;
    std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
    Broadcaster *broadcaster = event_sp->GetBroadcaster();
    if (!broadcaster)
      return 0;
    broadcaster_collection::iterator pos;
    broadcaster_collection::iterator end = m_broadcasters.end();
    Broadcaster::BroadcasterImplSP broadcaster_impl_sp(
        broadcaster->GetBroadcasterImpl());
    for (pos = m_broadcasters.find(broadcaster_impl_sp);
         pos != end && pos->first.lock() == broadcaster_impl_sp; ++pos) {
      BroadcasterInfo info = pos->second;
      if (event_sp->GetType() & info.event_mask) {
        if (info.callback != nullptr) {
          info.callback(event_sp, info.callback_user_data);
          ++num_handled;
        }
      }
    }
    return num_handled;
  }
  
  uint32_t
  Listener::StartListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
                                       const BroadcastEventSpec &event_spec) {
    if (!manager_sp)
      return 0;
  
    // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to
    // avoid violating the lock hierarchy (manager before broadcasters).
    std::lock_guard<std::recursive_mutex> manager_guard(
        manager_sp->m_manager_mutex);
    std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
  
    uint32_t bits_acquired = manager_sp->RegisterListenerForEvents(
        this->shared_from_this(), event_spec);
    if (bits_acquired) {
      broadcaster_manager_collection::iterator iter,
          end_iter = m_broadcaster_managers.end();
      BroadcasterManagerWP manager_wp(manager_sp);
      BroadcasterManagerWPMatcher matcher(manager_sp);
      iter = std::find_if<broadcaster_manager_collection::iterator,
                          BroadcasterManagerWPMatcher>(
          m_broadcaster_managers.begin(), end_iter, matcher);
      if (iter == end_iter)
        m_broadcaster_managers.push_back(manager_wp);
    }
  
    return bits_acquired;
  }
  
  bool Listener::StopListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
                                           const BroadcastEventSpec &event_spec) {
    if (!manager_sp)
      return false;
  
    std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
    return manager_sp->UnregisterListenerForEvents(this->shared_from_this(),
                                                   event_spec);
  }
  
  ListenerSP Listener::MakeListener(const char *name) {
    return ListenerSP(new Listener(name));
  }
  