1 | //===-- Broadcaster.cpp ---------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Utility/Broadcaster.h" |
10 | #include "lldb/Utility/Event.h" |
11 | #include "lldb/Utility/LLDBLog.h" |
12 | #include "lldb/Utility/Listener.h" |
13 | #include "lldb/Utility/Stream.h" |
14 | #include "lldb/Utility/StreamString.h" |
15 | |
16 | #include <algorithm> |
17 | #include <memory> |
18 | #include <utility> |
19 | |
20 | #include <cassert> |
21 | #include <cstddef> |
22 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | Broadcaster::Broadcaster(BroadcasterManagerSP manager_sp, std::string name) |
27 | : m_broadcaster_sp(std::make_shared<BroadcasterImpl>(args&: *this)), |
28 | m_manager_sp(std::move(manager_sp)), m_broadcaster_name(std::move(name)) { |
29 | Log *log = GetLog(mask: LLDBLog::Object); |
30 | LLDB_LOG(log, "{0} Broadcaster::Broadcaster(\"{1}\")" , |
31 | static_cast<void *>(this), GetBroadcasterName()); |
32 | } |
33 | |
34 | Broadcaster::BroadcasterImpl::BroadcasterImpl(Broadcaster &broadcaster) |
35 | : m_broadcaster(broadcaster), m_listeners(), m_listeners_mutex(), |
36 | m_hijacking_listeners(), m_hijacking_masks() {} |
37 | |
38 | Broadcaster::~Broadcaster() { |
39 | Log *log = GetLog(mask: LLDBLog::Object); |
40 | LLDB_LOG(log, "{0} Broadcaster::~Broadcaster(\"{1}\")" , |
41 | static_cast<void *>(this), GetBroadcasterName()); |
42 | |
43 | Clear(); |
44 | } |
45 | |
46 | void Broadcaster::CheckInWithManager() { |
47 | if (m_manager_sp) { |
48 | m_manager_sp->SignUpListenersForBroadcaster(broadcaster&: *this); |
49 | } |
50 | } |
51 | |
52 | llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> |
53 | Broadcaster::BroadcasterImpl::GetListeners(uint32_t event_mask, |
54 | bool include_primary) { |
55 | llvm::SmallVector<std::pair<ListenerSP, uint32_t &>, 4> listeners; |
56 | size_t max_count = m_listeners.size(); |
57 | if (include_primary) |
58 | max_count++; |
59 | listeners.reserve(N: max_count); |
60 | |
61 | for (auto it = m_listeners.begin(); it != m_listeners.end();) { |
62 | lldb::ListenerSP curr_listener_sp(it->first.lock()); |
63 | if (curr_listener_sp) { |
64 | if (it->second & event_mask) |
65 | listeners.emplace_back(Args: std::move(curr_listener_sp), Args&: it->second); |
66 | ++it; |
67 | } else |
68 | // If our listener_wp didn't resolve, then we should remove this entry. |
69 | it = m_listeners.erase(CI: it); |
70 | } |
71 | if (include_primary && m_primary_listener_sp) |
72 | listeners.emplace_back(Args&: m_primary_listener_sp, Args&: m_primary_listener_mask); |
73 | |
74 | return listeners; |
75 | } |
76 | |
77 | bool Broadcaster::BroadcasterImpl::HasListeners(uint32_t event_mask) { |
78 | if (m_primary_listener_sp) |
79 | return true; |
80 | for (auto it = m_listeners.begin(); it != m_listeners.end(); it++) { |
81 | // Don't return a listener if the other end of the WP is gone: |
82 | lldb::ListenerSP curr_listener_sp(it->first.lock()); |
83 | if (curr_listener_sp && (it->second & event_mask)) |
84 | return true; |
85 | } |
86 | return false; |
87 | } |
88 | |
89 | void Broadcaster::BroadcasterImpl::Clear() { |
90 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
91 | |
92 | // Make sure the listener forgets about this broadcaster. We do this in the |
93 | // broadcaster in case the broadcaster object initiates the removal. |
94 | for (auto &pair : GetListeners()) |
95 | pair.first->BroadcasterWillDestruct(&m_broadcaster); |
96 | |
97 | m_listeners.clear(); |
98 | m_primary_listener_sp.reset(); |
99 | } |
100 | |
101 | Broadcaster *Broadcaster::BroadcasterImpl::GetBroadcaster() { |
102 | return &m_broadcaster; |
103 | } |
104 | |
105 | bool Broadcaster::BroadcasterImpl::GetEventNames( |
106 | Stream &s, uint32_t event_mask, bool prefix_with_broadcaster_name) const { |
107 | uint32_t num_names_added = 0; |
108 | if (event_mask && !m_event_names.empty()) { |
109 | event_names_map::const_iterator end = m_event_names.end(); |
110 | for (uint32_t bit = 1u, mask = event_mask; mask != 0 && bit != 0; |
111 | bit <<= 1, mask >>= 1) { |
112 | if (mask & 1) { |
113 | event_names_map::const_iterator pos = m_event_names.find(x: bit); |
114 | if (pos != end) { |
115 | if (num_names_added > 0) |
116 | s.PutCString(cstr: ", " ); |
117 | |
118 | if (prefix_with_broadcaster_name) { |
119 | s.PutCString(cstr: GetBroadcasterName()); |
120 | s.PutChar(ch: '.'); |
121 | } |
122 | s.PutCString(cstr: pos->second); |
123 | ++num_names_added; |
124 | } |
125 | } |
126 | } |
127 | } |
128 | return num_names_added > 0; |
129 | } |
130 | |
131 | void Broadcaster::AddInitialEventsToListener( |
132 | const lldb::ListenerSP &listener_sp, uint32_t requested_events) {} |
133 | |
134 | uint32_t |
135 | Broadcaster::BroadcasterImpl::AddListener(const lldb::ListenerSP &listener_sp, |
136 | uint32_t event_mask) { |
137 | if (!listener_sp) |
138 | return 0; |
139 | |
140 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
141 | |
142 | // See if we already have this listener, and if so, update its mask |
143 | |
144 | bool handled = false; |
145 | |
146 | if (listener_sp == m_primary_listener_sp) |
147 | // This already handles all bits so just return the mask: |
148 | return event_mask; |
149 | |
150 | for (auto &pair : GetListeners(UINT32_MAX, include_primary: false)) { |
151 | if (pair.first == listener_sp) { |
152 | handled = true; |
153 | pair.second |= event_mask; |
154 | m_broadcaster.AddInitialEventsToListener(listener_sp, requested_events: event_mask); |
155 | break; |
156 | } |
157 | } |
158 | |
159 | if (!handled) { |
160 | // Grant a new listener the available event bits |
161 | m_listeners.push_back( |
162 | Elt: std::make_pair(x: lldb::ListenerWP(listener_sp), y&: event_mask)); |
163 | |
164 | // Individual broadcasters decide whether they have outstanding data when a |
165 | // listener attaches, and insert it into the listener with this method. |
166 | m_broadcaster.AddInitialEventsToListener(listener_sp, requested_events: event_mask); |
167 | } |
168 | |
169 | // Return the event bits that were granted to the listener |
170 | return event_mask; |
171 | } |
172 | |
173 | bool Broadcaster::BroadcasterImpl::EventTypeHasListeners(uint32_t event_type) { |
174 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
175 | |
176 | if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back()) |
177 | return true; |
178 | |
179 | // The primary listener listens for all event bits: |
180 | if (m_primary_listener_sp) |
181 | return true; |
182 | |
183 | return HasListeners(event_mask: event_type); |
184 | } |
185 | |
186 | bool Broadcaster::BroadcasterImpl::RemoveListener( |
187 | lldb_private::Listener *listener, uint32_t event_mask) { |
188 | if (!listener) |
189 | return false; |
190 | |
191 | if (listener == m_primary_listener_sp.get()) { |
192 | // Primary listeners listen for all the event bits for their broadcaster, |
193 | // so remove this altogether if asked: |
194 | m_primary_listener_sp.reset(); |
195 | return true; |
196 | } |
197 | |
198 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
199 | for (auto it = m_listeners.begin(); it != m_listeners.end();) { |
200 | lldb::ListenerSP curr_listener_sp(it->first.lock()); |
201 | |
202 | if (!curr_listener_sp) { |
203 | // The weak pointer for this listener didn't resolve, lets' prune it |
204 | // as we go. |
205 | m_listeners.erase(CI: it); |
206 | continue; |
207 | } |
208 | |
209 | if (curr_listener_sp.get() == listener) { |
210 | it->second &= ~event_mask; |
211 | // If we removed all the event bits from a listener, remove it from |
212 | // the list as well. |
213 | if (!it->second) |
214 | m_listeners.erase(CI: it); |
215 | return true; |
216 | } else |
217 | it++; |
218 | } |
219 | return false; |
220 | } |
221 | |
222 | bool Broadcaster::BroadcasterImpl::RemoveListener( |
223 | const lldb::ListenerSP &listener_sp, uint32_t event_mask) { |
224 | return RemoveListener(listener: listener_sp.get(), event_mask); |
225 | } |
226 | |
227 | void Broadcaster::BroadcasterImpl::BroadcastEvent(EventSP &event_sp) { |
228 | return PrivateBroadcastEvent(event_sp, unique: false); |
229 | } |
230 | |
231 | void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(EventSP &event_sp) { |
232 | return PrivateBroadcastEvent(event_sp, unique: true); |
233 | } |
234 | |
235 | void Broadcaster::BroadcasterImpl::PrivateBroadcastEvent(EventSP &event_sp, |
236 | bool unique) { |
237 | // Can't add a nullptr event... |
238 | if (!event_sp) |
239 | return; |
240 | |
241 | // Update the broadcaster on this event |
242 | event_sp->SetBroadcaster(&m_broadcaster); |
243 | |
244 | const uint32_t event_type = event_sp->GetType(); |
245 | |
246 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
247 | |
248 | ListenerSP hijacking_listener_sp; |
249 | |
250 | if (!m_hijacking_listeners.empty()) { |
251 | assert(!m_hijacking_masks.empty()); |
252 | hijacking_listener_sp = m_hijacking_listeners.back(); |
253 | if ((event_type & m_hijacking_masks.back()) == 0) |
254 | hijacking_listener_sp.reset(); |
255 | } |
256 | |
257 | Log *log = GetLog(mask: LLDBLog::Events); |
258 | if (!log && event_sp->GetData()) |
259 | log = event_sp->GetData()->GetLogChannel(); |
260 | |
261 | if (log) { |
262 | StreamString event_description; |
263 | event_sp->Dump(s: &event_description); |
264 | LLDB_LOG(log, |
265 | "{0:x} Broadcaster(\"{1}\")::BroadcastEvent (event_sp = {2}, " |
266 | "unique={3}) hijack = {4:x}" , |
267 | static_cast<void *>(this), GetBroadcasterName(), |
268 | event_description.GetData(), unique, |
269 | static_cast<void *>(hijacking_listener_sp.get())); |
270 | } |
271 | ListenerSP primary_listener_sp |
272 | = hijacking_listener_sp ? hijacking_listener_sp : m_primary_listener_sp; |
273 | |
274 | if (primary_listener_sp) { |
275 | if (unique && primary_listener_sp->PeekAtNextEventForBroadcasterWithType( |
276 | broadcaster: &m_broadcaster, event_type_mask: event_type)) |
277 | return; |
278 | // Add the pending listeners but not if the event is hijacked, since that |
279 | // is given sole access to the event stream it is hijacking. |
280 | // Make sure to do this before adding the event to the primary or it might |
281 | // start handling the event before we're done adding all the pending |
282 | // listeners. |
283 | // Also, don't redo the check for unique here, since otherwise that could |
284 | // be racy, and if we send the event to the primary listener then we SHOULD |
285 | // send it to the secondary listeners or they will get out of sync with the |
286 | // primary listener. |
287 | if (!hijacking_listener_sp) { |
288 | for (auto &pair : GetListeners(event_mask: event_type, include_primary: false)) |
289 | event_sp->AddPendingListener(pending_listener_sp: pair.first); |
290 | } |
291 | primary_listener_sp->AddEvent(event&: event_sp); |
292 | } else { |
293 | for (auto &pair : GetListeners(event_mask: event_type)) { |
294 | if (unique && pair.first->PeekAtNextEventForBroadcasterWithType( |
295 | broadcaster: &m_broadcaster, event_type_mask: event_type)) |
296 | continue; |
297 | |
298 | pair.first->AddEvent(event&: event_sp); |
299 | } |
300 | } |
301 | } |
302 | |
303 | void Broadcaster::BroadcasterImpl::BroadcastEvent(uint32_t event_type) { |
304 | auto event_sp = std::make_shared<Event>(args&: event_type, /*data = */ args: nullptr); |
305 | PrivateBroadcastEvent(event_sp, unique: false); |
306 | } |
307 | |
308 | void Broadcaster::BroadcasterImpl::BroadcastEvent( |
309 | uint32_t event_type, const lldb::EventDataSP &event_data_sp) { |
310 | auto event_sp = std::make_shared<Event>(args&: event_type, args: event_data_sp); |
311 | PrivateBroadcastEvent(event_sp, unique: false); |
312 | } |
313 | |
314 | void Broadcaster::BroadcasterImpl::BroadcastEventIfUnique(uint32_t event_type) { |
315 | auto event_sp = std::make_shared<Event>(args&: event_type, /*data = */ args: nullptr); |
316 | PrivateBroadcastEvent(event_sp, unique: true); |
317 | } |
318 | |
319 | void Broadcaster::BroadcasterImpl::SetPrimaryListener(lldb::ListenerSP |
320 | listener_sp) { |
321 | // This might have already been added as a normal listener, make sure we |
322 | // don't hold two copies. |
323 | RemoveListener(listener: listener_sp.get(), UINT32_MAX); |
324 | m_primary_listener_sp = listener_sp; |
325 | |
326 | } |
327 | |
328 | bool Broadcaster::BroadcasterImpl::HijackBroadcaster( |
329 | const lldb::ListenerSP &listener_sp, uint32_t event_mask) { |
330 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
331 | |
332 | Log *log = GetLog(mask: LLDBLog::Events); |
333 | LLDB_LOG( |
334 | log, |
335 | "{0} Broadcaster(\"{1}\")::HijackBroadcaster (listener(\"{2}\")={3})" , |
336 | static_cast<void *>(this), GetBroadcasterName(), |
337 | listener_sp->m_name.c_str(), static_cast<void *>(listener_sp.get())); |
338 | m_hijacking_listeners.push_back(x: listener_sp); |
339 | m_hijacking_masks.push_back(x: event_mask); |
340 | return true; |
341 | } |
342 | |
343 | bool Broadcaster::BroadcasterImpl::IsHijackedForEvent(uint32_t event_mask) { |
344 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
345 | |
346 | if (!m_hijacking_listeners.empty()) |
347 | return (event_mask & m_hijacking_masks.back()) != 0; |
348 | return false; |
349 | } |
350 | |
351 | const char *Broadcaster::BroadcasterImpl::GetHijackingListenerName() { |
352 | if (m_hijacking_listeners.size()) { |
353 | return m_hijacking_listeners.back()->GetName(); |
354 | } |
355 | return nullptr; |
356 | } |
357 | |
358 | void Broadcaster::BroadcasterImpl::RestoreBroadcaster() { |
359 | std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex); |
360 | |
361 | if (!m_hijacking_listeners.empty()) { |
362 | ListenerSP listener_sp = m_hijacking_listeners.back(); |
363 | Log *log = GetLog(mask: LLDBLog::Events); |
364 | LLDB_LOG(log, |
365 | "{0} Broadcaster(\"{1}\")::RestoreBroadcaster (about to pop " |
366 | "listener(\"{2}\")={3})" , |
367 | static_cast<void *>(this), GetBroadcasterName(), |
368 | listener_sp->m_name.c_str(), |
369 | static_cast<void *>(listener_sp.get())); |
370 | m_hijacking_listeners.pop_back(); |
371 | } |
372 | if (!m_hijacking_masks.empty()) |
373 | m_hijacking_masks.pop_back(); |
374 | } |
375 | |
376 | ConstString &Broadcaster::GetBroadcasterClass() const { |
377 | static ConstString class_name("lldb.anonymous" ); |
378 | return class_name; |
379 | } |
380 | |
381 | bool BroadcastEventSpec::operator<(const BroadcastEventSpec &rhs) const { |
382 | if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) { |
383 | return GetEventBits() < rhs.GetEventBits(); |
384 | } |
385 | return GetBroadcasterClass() < rhs.GetBroadcasterClass(); |
386 | } |
387 | |
388 | BroadcasterManager::BroadcasterManager() : m_manager_mutex() {} |
389 | |
390 | lldb::BroadcasterManagerSP BroadcasterManager::MakeBroadcasterManager() { |
391 | return lldb::BroadcasterManagerSP(new BroadcasterManager()); |
392 | } |
393 | |
394 | uint32_t BroadcasterManager::RegisterListenerForEvents( |
395 | const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { |
396 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
397 | |
398 | collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); |
399 | uint32_t available_bits = event_spec.GetEventBits(); |
400 | |
401 | auto class_matches = [&event_spec](const event_listener_key &input) -> bool { |
402 | return input.first.GetBroadcasterClass() == |
403 | event_spec.GetBroadcasterClass(); |
404 | }; |
405 | |
406 | while (iter != end_iter && |
407 | (iter = find_if(first: iter, last: end_iter, pred: class_matches)) != end_iter) { |
408 | available_bits &= ~((*iter).first.GetEventBits()); |
409 | iter++; |
410 | } |
411 | |
412 | if (available_bits != 0) { |
413 | m_event_map.insert(x: event_listener_key( |
414 | BroadcastEventSpec(event_spec.GetBroadcasterClass(), available_bits), |
415 | listener_sp)); |
416 | m_listeners.insert(x: listener_sp); |
417 | } |
418 | |
419 | return available_bits; |
420 | } |
421 | |
422 | bool BroadcasterManager::UnregisterListenerForEvents( |
423 | const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) { |
424 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
425 | bool removed_some = false; |
426 | |
427 | if (m_listeners.erase(x: listener_sp) == 0) |
428 | return false; |
429 | |
430 | auto listener_matches_and_shared_bits = |
431 | [&listener_sp, &event_spec](const event_listener_key &input) -> bool { |
432 | return input.first.GetBroadcasterClass() == |
433 | event_spec.GetBroadcasterClass() && |
434 | (input.first.GetEventBits() & event_spec.GetEventBits()) != 0 && |
435 | input.second == listener_sp; |
436 | }; |
437 | std::vector<BroadcastEventSpec> to_be_readded; |
438 | uint32_t event_bits_to_remove = event_spec.GetEventBits(); |
439 | |
440 | // Go through the map and delete the exact matches, and build a list of |
441 | // matches that weren't exact to re-add: |
442 | for (auto iter = m_event_map.begin(), end = m_event_map.end();;) { |
443 | iter = find_if(first: iter, last: end, pred: listener_matches_and_shared_bits); |
444 | if (iter == end) |
445 | break; |
446 | uint32_t iter_event_bits = (*iter).first.GetEventBits(); |
447 | removed_some = true; |
448 | |
449 | if (event_bits_to_remove != iter_event_bits) { |
450 | uint32_t new_event_bits = iter_event_bits & ~event_bits_to_remove; |
451 | to_be_readded.emplace_back(args: event_spec.GetBroadcasterClass(), |
452 | args&: new_event_bits); |
453 | } |
454 | iter = m_event_map.erase(position: iter); |
455 | } |
456 | |
457 | // Okay now add back the bits that weren't completely removed: |
458 | for (const auto &event : to_be_readded) { |
459 | m_event_map.insert(x: event_listener_key(event, listener_sp)); |
460 | } |
461 | |
462 | return removed_some; |
463 | } |
464 | |
465 | ListenerSP BroadcasterManager::GetListenerForEventSpec( |
466 | const BroadcastEventSpec &event_spec) const { |
467 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
468 | |
469 | auto event_spec_matches = |
470 | [&event_spec](const event_listener_key &input) -> bool { |
471 | return input.first.IsContainedIn(in_spec: event_spec); |
472 | }; |
473 | |
474 | auto iter = llvm::find_if(Range: m_event_map, P: event_spec_matches); |
475 | if (iter != m_event_map.end()) |
476 | return (*iter).second; |
477 | |
478 | return nullptr; |
479 | } |
480 | |
481 | void BroadcasterManager::RemoveListener(Listener *listener) { |
482 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
483 | auto listeners_predicate = |
484 | [&listener](const lldb::ListenerSP &input) -> bool { |
485 | return input.get() == listener; |
486 | }; |
487 | |
488 | if (auto iter = llvm::find_if(Range&: m_listeners, P: listeners_predicate); |
489 | iter != m_listeners.end()) |
490 | m_listeners.erase(position: iter); |
491 | |
492 | auto events_predicate = [listener](const event_listener_key &input) -> bool { |
493 | return input.second.get() == listener; |
494 | }; |
495 | |
496 | // TODO: use 'std::map::erase_if' when moving to c++20. |
497 | for (auto iter = m_event_map.begin(), end = m_event_map.end();;) { |
498 | iter = find_if(first: iter, last: end, pred: events_predicate); |
499 | if (iter == end) |
500 | break; |
501 | |
502 | iter = m_event_map.erase(position: iter); |
503 | } |
504 | } |
505 | |
506 | void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) { |
507 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
508 | |
509 | auto listener_matches = |
510 | [&listener_sp](const event_listener_key &input) -> bool { |
511 | return input.second == listener_sp; |
512 | }; |
513 | |
514 | if (m_listeners.erase(x: listener_sp) == 0) |
515 | return; |
516 | |
517 | // TODO: use 'std::map::erase_if' when moving to c++20. |
518 | for (auto iter = m_event_map.begin(), end_iter = m_event_map.end();;) { |
519 | iter = find_if(first: iter, last: end_iter, pred: listener_matches); |
520 | if (iter == end_iter) |
521 | break; |
522 | |
523 | iter = m_event_map.erase(position: iter); |
524 | } |
525 | } |
526 | |
527 | void BroadcasterManager::SignUpListenersForBroadcaster( |
528 | Broadcaster &broadcaster) { |
529 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
530 | |
531 | collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); |
532 | |
533 | auto class_matches = [&broadcaster](const event_listener_key &input) -> bool { |
534 | return input.first.GetBroadcasterClass() == |
535 | broadcaster.GetBroadcasterClass(); |
536 | }; |
537 | |
538 | while (iter != end_iter && |
539 | (iter = find_if(first: iter, last: end_iter, pred: class_matches)) != end_iter) { |
540 | (*iter).second->StartListeningForEvents(broadcaster: &broadcaster, |
541 | event_mask: (*iter).first.GetEventBits()); |
542 | iter++; |
543 | } |
544 | } |
545 | |
546 | void BroadcasterManager::Clear() { |
547 | std::lock_guard<std::recursive_mutex> guard(m_manager_mutex); |
548 | |
549 | for (auto &listener : m_listeners) |
550 | listener->BroadcasterManagerWillDestruct(manager_sp: this->shared_from_this()); |
551 | m_listeners.clear(); |
552 | m_event_map.clear(); |
553 | } |
554 | |