1343181Sdim//===-- Listener.cpp --------------------------------------------*- C++ -*-===// 2343181Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343181Sdim// 7343181Sdim//===----------------------------------------------------------------------===// 8343181Sdim 9343181Sdim#include "lldb/Utility/Listener.h" 10343181Sdim 11343181Sdim#include "lldb/Utility/Broadcaster.h" 12343181Sdim#include "lldb/Utility/ConstString.h" 13343181Sdim#include "lldb/Utility/Event.h" 14343181Sdim#include "lldb/Utility/Log.h" 15343181Sdim#include "lldb/Utility/Logging.h" 16343181Sdim 17343181Sdim#include "llvm/ADT/Optional.h" 18343181Sdim 19343181Sdim#include <algorithm> 20343181Sdim#include <memory> 21343181Sdim#include <utility> 22343181Sdim 23343181Sdimusing namespace lldb; 24343181Sdimusing namespace lldb_private; 25343181Sdim 26343181Sdimnamespace { 27343181Sdimclass BroadcasterManagerWPMatcher { 28343181Sdimpublic: 29343181Sdim BroadcasterManagerWPMatcher(BroadcasterManagerSP manager_sp) 30353358Sdim : m_manager_sp(std::move(manager_sp)) {} 31353358Sdim bool operator()(const BroadcasterManagerWP &input_wp) const { 32343181Sdim BroadcasterManagerSP input_sp = input_wp.lock(); 33343181Sdim return (input_sp && input_sp == m_manager_sp); 34343181Sdim } 35343181Sdim 36343181Sdim BroadcasterManagerSP m_manager_sp; 37343181Sdim}; 38343181Sdim} // anonymous namespace 39343181Sdim 40343181SdimListener::Listener(const char *name) 41343181Sdim : m_name(name), m_broadcasters(), m_broadcasters_mutex(), m_events(), 42343181Sdim m_events_mutex() { 43343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); 44343181Sdim if (log != nullptr) 45360784Sdim LLDB_LOGF(log, "%p Listener::Listener('%s')", static_cast<void *>(this), 46360784Sdim m_name.c_str()); 47343181Sdim} 48343181Sdim 49343181SdimListener::~Listener() { 50343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); 51343181Sdim 52343181Sdim Clear(); 53343181Sdim 54360784Sdim LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this), 55360784Sdim __FUNCTION__, m_name.c_str()); 56343181Sdim} 57343181Sdim 58343181Sdimvoid Listener::Clear() { 59343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); 60343181Sdim std::lock_guard<std::recursive_mutex> broadcasters_guard( 61343181Sdim m_broadcasters_mutex); 62343181Sdim broadcaster_collection::iterator pos, end = m_broadcasters.end(); 63343181Sdim for (pos = m_broadcasters.begin(); pos != end; ++pos) { 64343181Sdim Broadcaster::BroadcasterImplSP broadcaster_sp(pos->first.lock()); 65343181Sdim if (broadcaster_sp) 66343181Sdim broadcaster_sp->RemoveListener(this, pos->second.event_mask); 67343181Sdim } 68343181Sdim m_broadcasters.clear(); 69343181Sdim 70343181Sdim std::lock_guard<std::mutex> events_guard(m_events_mutex); 71343181Sdim m_events.clear(); 72343181Sdim size_t num_managers = m_broadcaster_managers.size(); 73343181Sdim 74343181Sdim for (size_t i = 0; i < num_managers; i++) { 75343181Sdim BroadcasterManagerSP manager_sp(m_broadcaster_managers[i].lock()); 76343181Sdim if (manager_sp) 77343181Sdim manager_sp->RemoveListener(this); 78343181Sdim } 79343181Sdim 80360784Sdim LLDB_LOGF(log, "%p Listener::%s('%s')", static_cast<void *>(this), 81360784Sdim __FUNCTION__, m_name.c_str()); 82343181Sdim} 83343181Sdim 84343181Sdimuint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster, 85343181Sdim uint32_t event_mask) { 86343181Sdim if (broadcaster) { 87343181Sdim // Scope for "locker" 88343181Sdim // Tell the broadcaster to add this object as a listener 89343181Sdim { 90343181Sdim std::lock_guard<std::recursive_mutex> broadcasters_guard( 91343181Sdim m_broadcasters_mutex); 92343181Sdim Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl()); 93343181Sdim m_broadcasters.insert( 94343181Sdim std::make_pair(impl_wp, BroadcasterInfo(event_mask))); 95343181Sdim } 96343181Sdim 97343181Sdim uint32_t acquired_mask = 98343181Sdim broadcaster->AddListener(this->shared_from_this(), event_mask); 99343181Sdim 100343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); 101343181Sdim if (log != nullptr) 102360784Sdim LLDB_LOGF(log, 103360784Sdim "%p Listener::StartListeningForEvents (broadcaster = %p, " 104360784Sdim "mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s", 105360784Sdim static_cast<void *>(this), static_cast<void *>(broadcaster), 106360784Sdim event_mask, acquired_mask, m_name.c_str()); 107343181Sdim 108343181Sdim return acquired_mask; 109343181Sdim } 110343181Sdim return 0; 111343181Sdim} 112343181Sdim 113343181Sdimuint32_t Listener::StartListeningForEvents(Broadcaster *broadcaster, 114343181Sdim uint32_t event_mask, 115343181Sdim HandleBroadcastCallback callback, 116343181Sdim void *callback_user_data) { 117343181Sdim if (broadcaster) { 118343181Sdim // Scope for "locker" 119343181Sdim // Tell the broadcaster to add this object as a listener 120343181Sdim { 121343181Sdim std::lock_guard<std::recursive_mutex> broadcasters_guard( 122343181Sdim m_broadcasters_mutex); 123343181Sdim Broadcaster::BroadcasterImplWP impl_wp(broadcaster->GetBroadcasterImpl()); 124343181Sdim m_broadcasters.insert(std::make_pair( 125343181Sdim impl_wp, BroadcasterInfo(event_mask, callback, callback_user_data))); 126343181Sdim } 127343181Sdim 128343181Sdim uint32_t acquired_mask = 129343181Sdim broadcaster->AddListener(this->shared_from_this(), event_mask); 130343181Sdim 131343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); 132343181Sdim if (log != nullptr) { 133343181Sdim void **pointer = reinterpret_cast<void **>(&callback); 134360784Sdim LLDB_LOGF(log, 135360784Sdim "%p Listener::StartListeningForEvents (broadcaster = %p, " 136360784Sdim "mask = 0x%8.8x, callback = %p, user_data = %p) " 137360784Sdim "acquired_mask = 0x%8.8x for %s", 138360784Sdim static_cast<void *>(this), static_cast<void *>(broadcaster), 139360784Sdim event_mask, *pointer, static_cast<void *>(callback_user_data), 140360784Sdim acquired_mask, m_name.c_str()); 141343181Sdim } 142343181Sdim 143343181Sdim return acquired_mask; 144343181Sdim } 145343181Sdim return 0; 146343181Sdim} 147343181Sdim 148343181Sdimbool Listener::StopListeningForEvents(Broadcaster *broadcaster, 149343181Sdim uint32_t event_mask) { 150343181Sdim if (broadcaster) { 151343181Sdim // Scope for "locker" 152343181Sdim { 153343181Sdim std::lock_guard<std::recursive_mutex> broadcasters_guard( 154343181Sdim m_broadcasters_mutex); 155343181Sdim m_broadcasters.erase(broadcaster->GetBroadcasterImpl()); 156343181Sdim } 157343181Sdim // Remove the broadcaster from our set of broadcasters 158343181Sdim return broadcaster->RemoveListener(this->shared_from_this(), event_mask); 159343181Sdim } 160343181Sdim 161343181Sdim return false; 162343181Sdim} 163343181Sdim 164343181Sdim// Called when a Broadcaster is in its destructor. We need to remove all 165343181Sdim// knowledge of this broadcaster and any events that it may have queued up 166343181Sdimvoid Listener::BroadcasterWillDestruct(Broadcaster *broadcaster) { 167343181Sdim // Scope for "broadcasters_locker" 168343181Sdim { 169343181Sdim std::lock_guard<std::recursive_mutex> broadcasters_guard( 170343181Sdim m_broadcasters_mutex); 171343181Sdim m_broadcasters.erase(broadcaster->GetBroadcasterImpl()); 172343181Sdim } 173343181Sdim 174343181Sdim // Scope for "event_locker" 175343181Sdim { 176343181Sdim std::lock_guard<std::mutex> events_guard(m_events_mutex); 177343181Sdim // Remove all events for this broadcaster object. 178343181Sdim event_collection::iterator pos = m_events.begin(); 179343181Sdim while (pos != m_events.end()) { 180343181Sdim if ((*pos)->GetBroadcaster() == broadcaster) 181343181Sdim pos = m_events.erase(pos); 182343181Sdim else 183343181Sdim ++pos; 184343181Sdim } 185343181Sdim } 186343181Sdim} 187343181Sdim 188343181Sdimvoid Listener::BroadcasterManagerWillDestruct(BroadcasterManagerSP manager_sp) { 189343181Sdim // Just need to remove this broadcast manager from the list of managers: 190343181Sdim broadcaster_manager_collection::iterator iter, 191343181Sdim end_iter = m_broadcaster_managers.end(); 192343181Sdim BroadcasterManagerWP manager_wp; 193343181Sdim 194353358Sdim BroadcasterManagerWPMatcher matcher(std::move(manager_sp)); 195343181Sdim iter = std::find_if<broadcaster_manager_collection::iterator, 196343181Sdim BroadcasterManagerWPMatcher>( 197343181Sdim m_broadcaster_managers.begin(), end_iter, matcher); 198343181Sdim if (iter != end_iter) 199343181Sdim m_broadcaster_managers.erase(iter); 200343181Sdim} 201343181Sdim 202343181Sdimvoid Listener::AddEvent(EventSP &event_sp) { 203343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); 204343181Sdim if (log != nullptr) 205360784Sdim LLDB_LOGF(log, "%p Listener('%s')::AddEvent (event_sp = {%p})", 206360784Sdim static_cast<void *>(this), m_name.c_str(), 207360784Sdim static_cast<void *>(event_sp.get())); 208343181Sdim 209343181Sdim std::lock_guard<std::mutex> guard(m_events_mutex); 210343181Sdim m_events.push_back(event_sp); 211343181Sdim m_events_condition.notify_all(); 212343181Sdim} 213343181Sdim 214343181Sdimclass EventBroadcasterMatches { 215343181Sdimpublic: 216343181Sdim EventBroadcasterMatches(Broadcaster *broadcaster) 217343181Sdim : m_broadcaster(broadcaster) {} 218343181Sdim 219343181Sdim bool operator()(const EventSP &event_sp) const { 220343181Sdim return event_sp->BroadcasterIs(m_broadcaster); 221343181Sdim } 222343181Sdim 223343181Sdimprivate: 224343181Sdim Broadcaster *m_broadcaster; 225343181Sdim}; 226343181Sdim 227343181Sdimclass EventMatcher { 228343181Sdimpublic: 229343181Sdim EventMatcher(Broadcaster *broadcaster, const ConstString *broadcaster_names, 230343181Sdim uint32_t num_broadcaster_names, uint32_t event_type_mask) 231343181Sdim : m_broadcaster(broadcaster), m_broadcaster_names(broadcaster_names), 232343181Sdim m_num_broadcaster_names(num_broadcaster_names), 233343181Sdim m_event_type_mask(event_type_mask) {} 234343181Sdim 235343181Sdim bool operator()(const EventSP &event_sp) const { 236343181Sdim if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster)) 237343181Sdim return false; 238343181Sdim 239343181Sdim if (m_broadcaster_names) { 240343181Sdim bool found_source = false; 241353358Sdim ConstString event_broadcaster_name = 242343181Sdim event_sp->GetBroadcaster()->GetBroadcasterName(); 243343181Sdim for (uint32_t i = 0; i < m_num_broadcaster_names; ++i) { 244343181Sdim if (m_broadcaster_names[i] == event_broadcaster_name) { 245343181Sdim found_source = true; 246343181Sdim break; 247343181Sdim } 248343181Sdim } 249343181Sdim if (!found_source) 250343181Sdim return false; 251343181Sdim } 252343181Sdim 253343181Sdim return m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType(); 254343181Sdim } 255343181Sdim 256343181Sdimprivate: 257343181Sdim Broadcaster *m_broadcaster; 258343181Sdim const ConstString *m_broadcaster_names; 259343181Sdim const uint32_t m_num_broadcaster_names; 260343181Sdim const uint32_t m_event_type_mask; 261343181Sdim}; 262343181Sdim 263343181Sdimbool Listener::FindNextEventInternal( 264343181Sdim std::unique_lock<std::mutex> &lock, 265343181Sdim Broadcaster *broadcaster, // nullptr for any broadcaster 266343181Sdim const ConstString *broadcaster_names, // nullptr for any event 267343181Sdim uint32_t num_broadcaster_names, uint32_t event_type_mask, EventSP &event_sp, 268343181Sdim bool remove) { 269343181Sdim // NOTE: callers of this function must lock m_events_mutex using a 270343181Sdim // Mutex::Locker 271343181Sdim // and pass the locker as the first argument. m_events_mutex is no longer 272343181Sdim // recursive. 273343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); 274343181Sdim 275343181Sdim if (m_events.empty()) 276343181Sdim return false; 277343181Sdim 278343181Sdim Listener::event_collection::iterator pos = m_events.end(); 279343181Sdim 280343181Sdim if (broadcaster == nullptr && broadcaster_names == nullptr && 281343181Sdim event_type_mask == 0) { 282343181Sdim pos = m_events.begin(); 283343181Sdim } else { 284343181Sdim pos = std::find_if(m_events.begin(), m_events.end(), 285343181Sdim EventMatcher(broadcaster, broadcaster_names, 286343181Sdim num_broadcaster_names, event_type_mask)); 287343181Sdim } 288343181Sdim 289343181Sdim if (pos != m_events.end()) { 290343181Sdim event_sp = *pos; 291343181Sdim 292343181Sdim if (log != nullptr) 293360784Sdim LLDB_LOGF(log, 294360784Sdim "%p '%s' Listener::FindNextEventInternal(broadcaster=%p, " 295360784Sdim "broadcaster_names=%p[%u], event_type_mask=0x%8.8x, " 296360784Sdim "remove=%i) event %p", 297360784Sdim static_cast<void *>(this), GetName(), 298360784Sdim static_cast<void *>(broadcaster), 299360784Sdim static_cast<const void *>(broadcaster_names), 300360784Sdim num_broadcaster_names, event_type_mask, remove, 301360784Sdim static_cast<void *>(event_sp.get())); 302343181Sdim 303343181Sdim if (remove) { 304343181Sdim m_events.erase(pos); 305343181Sdim // Unlock the event queue here. We've removed this event and are about 306343181Sdim // to return it so it should be okay to get the next event off the queue 307343181Sdim // here - and it might be useful to do that in the "DoOnRemoval". 308343181Sdim lock.unlock(); 309343181Sdim event_sp->DoOnRemoval(); 310343181Sdim } 311343181Sdim return true; 312343181Sdim } 313343181Sdim 314343181Sdim event_sp.reset(); 315343181Sdim return false; 316343181Sdim} 317343181Sdim 318343181SdimEvent *Listener::PeekAtNextEvent() { 319343181Sdim std::unique_lock<std::mutex> guard(m_events_mutex); 320343181Sdim EventSP event_sp; 321343181Sdim if (FindNextEventInternal(guard, nullptr, nullptr, 0, 0, event_sp, false)) 322343181Sdim return event_sp.get(); 323343181Sdim return nullptr; 324343181Sdim} 325343181Sdim 326343181SdimEvent *Listener::PeekAtNextEventForBroadcaster(Broadcaster *broadcaster) { 327343181Sdim std::unique_lock<std::mutex> guard(m_events_mutex); 328343181Sdim EventSP event_sp; 329343181Sdim if (FindNextEventInternal(guard, broadcaster, nullptr, 0, 0, event_sp, false)) 330343181Sdim return event_sp.get(); 331343181Sdim return nullptr; 332343181Sdim} 333343181Sdim 334343181SdimEvent * 335343181SdimListener::PeekAtNextEventForBroadcasterWithType(Broadcaster *broadcaster, 336343181Sdim uint32_t event_type_mask) { 337343181Sdim std::unique_lock<std::mutex> guard(m_events_mutex); 338343181Sdim EventSP event_sp; 339343181Sdim if (FindNextEventInternal(guard, broadcaster, nullptr, 0, event_type_mask, 340343181Sdim event_sp, false)) 341343181Sdim return event_sp.get(); 342343181Sdim return nullptr; 343343181Sdim} 344343181Sdim 345343181Sdimbool Listener::GetEventInternal( 346343181Sdim const Timeout<std::micro> &timeout, 347343181Sdim Broadcaster *broadcaster, // nullptr for any broadcaster 348343181Sdim const ConstString *broadcaster_names, // nullptr for any event 349343181Sdim uint32_t num_broadcaster_names, uint32_t event_type_mask, 350343181Sdim EventSP &event_sp) { 351343181Sdim Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS)); 352343181Sdim LLDB_LOG(log, "this = {0}, timeout = {1} for {2}", this, timeout, m_name); 353343181Sdim 354343181Sdim std::unique_lock<std::mutex> lock(m_events_mutex); 355343181Sdim 356343181Sdim while (true) { 357343181Sdim if (FindNextEventInternal(lock, broadcaster, broadcaster_names, 358343181Sdim num_broadcaster_names, event_type_mask, event_sp, 359343181Sdim true)) { 360343181Sdim return true; 361343181Sdim } else { 362343181Sdim std::cv_status result = std::cv_status::no_timeout; 363343181Sdim if (!timeout) 364343181Sdim m_events_condition.wait(lock); 365343181Sdim else 366343181Sdim result = m_events_condition.wait_for(lock, *timeout); 367343181Sdim 368343181Sdim if (result == std::cv_status::timeout) { 369343181Sdim log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); 370360784Sdim LLDB_LOGF(log, "%p Listener::GetEventInternal() timed out for %s", 371360784Sdim static_cast<void *>(this), m_name.c_str()); 372343181Sdim return false; 373343181Sdim } else if (result != std::cv_status::no_timeout) { 374343181Sdim log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); 375360784Sdim LLDB_LOGF(log, "%p Listener::GetEventInternal() unknown error for %s", 376360784Sdim static_cast<void *>(this), m_name.c_str()); 377343181Sdim return false; 378343181Sdim } 379343181Sdim } 380343181Sdim } 381343181Sdim 382343181Sdim return false; 383343181Sdim} 384343181Sdim 385343181Sdimbool Listener::GetEventForBroadcasterWithType( 386343181Sdim Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp, 387343181Sdim const Timeout<std::micro> &timeout) { 388343181Sdim return GetEventInternal(timeout, broadcaster, nullptr, 0, event_type_mask, 389343181Sdim event_sp); 390343181Sdim} 391343181Sdim 392343181Sdimbool Listener::GetEventForBroadcaster(Broadcaster *broadcaster, 393343181Sdim EventSP &event_sp, 394343181Sdim const Timeout<std::micro> &timeout) { 395343181Sdim return GetEventInternal(timeout, broadcaster, nullptr, 0, 0, event_sp); 396343181Sdim} 397343181Sdim 398343181Sdimbool Listener::GetEvent(EventSP &event_sp, const Timeout<std::micro> &timeout) { 399343181Sdim return GetEventInternal(timeout, nullptr, nullptr, 0, 0, event_sp); 400343181Sdim} 401343181Sdim 402343181Sdimsize_t Listener::HandleBroadcastEvent(EventSP &event_sp) { 403343181Sdim size_t num_handled = 0; 404343181Sdim std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex); 405343181Sdim Broadcaster *broadcaster = event_sp->GetBroadcaster(); 406343181Sdim if (!broadcaster) 407343181Sdim return 0; 408343181Sdim broadcaster_collection::iterator pos; 409343181Sdim broadcaster_collection::iterator end = m_broadcasters.end(); 410343181Sdim Broadcaster::BroadcasterImplSP broadcaster_impl_sp( 411343181Sdim broadcaster->GetBroadcasterImpl()); 412343181Sdim for (pos = m_broadcasters.find(broadcaster_impl_sp); 413343181Sdim pos != end && pos->first.lock() == broadcaster_impl_sp; ++pos) { 414343181Sdim BroadcasterInfo info = pos->second; 415343181Sdim if (event_sp->GetType() & info.event_mask) { 416343181Sdim if (info.callback != nullptr) { 417343181Sdim info.callback(event_sp, info.callback_user_data); 418343181Sdim ++num_handled; 419343181Sdim } 420343181Sdim } 421343181Sdim } 422343181Sdim return num_handled; 423343181Sdim} 424343181Sdim 425343181Sdimuint32_t 426353358SdimListener::StartListeningForEventSpec(const BroadcasterManagerSP &manager_sp, 427343181Sdim const BroadcastEventSpec &event_spec) { 428343181Sdim if (!manager_sp) 429343181Sdim return 0; 430343181Sdim 431343181Sdim // The BroadcasterManager mutex must be locked before m_broadcasters_mutex to 432343181Sdim // avoid violating the lock hierarchy (manager before broadcasters). 433343181Sdim std::lock_guard<std::recursive_mutex> manager_guard( 434343181Sdim manager_sp->m_manager_mutex); 435343181Sdim std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex); 436343181Sdim 437343181Sdim uint32_t bits_acquired = manager_sp->RegisterListenerForEvents( 438343181Sdim this->shared_from_this(), event_spec); 439343181Sdim if (bits_acquired) { 440343181Sdim broadcaster_manager_collection::iterator iter, 441343181Sdim end_iter = m_broadcaster_managers.end(); 442343181Sdim BroadcasterManagerWP manager_wp(manager_sp); 443343181Sdim BroadcasterManagerWPMatcher matcher(manager_sp); 444343181Sdim iter = std::find_if<broadcaster_manager_collection::iterator, 445343181Sdim BroadcasterManagerWPMatcher>( 446343181Sdim m_broadcaster_managers.begin(), end_iter, matcher); 447343181Sdim if (iter == end_iter) 448343181Sdim m_broadcaster_managers.push_back(manager_wp); 449343181Sdim } 450343181Sdim 451343181Sdim return bits_acquired; 452343181Sdim} 453343181Sdim 454353358Sdimbool Listener::StopListeningForEventSpec(const BroadcasterManagerSP &manager_sp, 455343181Sdim const BroadcastEventSpec &event_spec) { 456343181Sdim if (!manager_sp) 457343181Sdim return false; 458343181Sdim 459343181Sdim std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex); 460343181Sdim return manager_sp->UnregisterListenerForEvents(this->shared_from_this(), 461343181Sdim event_spec); 462343181Sdim} 463343181Sdim 464343181SdimListenerSP Listener::MakeListener(const char *name) { 465343181Sdim return ListenerSP(new Listener(name)); 466343181Sdim} 467