blob: a39ce6121b32743934df9732eff9e4533f0b0c78 [file] [log] [blame]
//===-- Listener.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Core/Listener.h"
#include "lldb/Core/Broadcaster.h"
#include "lldb/Core/Event.h"
#include "lldb/Utility/ConstString.h" // for ConstString
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Logging.h" // for GetLogIfAllCategoriesSet, LIBL...
#include "llvm/ADT/Optional.h" // for Optional
#include <algorithm>
#include <memory> // for make_shared
#include <utility> // for pair, make_pair
using namespace lldb;
using namespace lldb_private;
namespace {
class BroadcasterManagerWPMatcher {
public:
BroadcasterManagerWPMatcher(BroadcasterManagerSP manager_sp)
: m_manager_sp(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)
log->Printf("%p Listener::Listener('%s')", static_cast<void *>(this),
m_name.c_str());
}
Listener::~Listener() {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
Clear();
if (log)
log->Printf("%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);
}
if (log)
log->Printf("%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)
log->Printf("%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);
log->Printf("%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(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)
log->Printf("%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;
const 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;
}
if (m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType())
return true;
return false;
}
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)
log->Printf("%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);
if (log)
log->Printf("%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);
if (log)
log->Printf("%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(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(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));
}