Skip to content

Commit 2a14c06

Browse files
authored
[lldb] Make Broadcaster mutexes non-recursive (#97400)
Non-recursive mutexes encourage better locking discipline and avoid bugs like #96750, where one can unexpectedly re-enter the critical section on the same thread, and interrupt a presumed-indivisible operation. In this case, the only needed fix was to remove locking from some BroadcastManager functions, which were only called from the Listener class (and the listener already locked those mutexes to preserve lock ordering). While doing that, I noticed we don't have unit tests for these functions, so I added one.
1 parent 76c84e7 commit 2a14c06

File tree

4 files changed

+70
-31
lines changed

4 files changed

+70
-31
lines changed

lldb/include/lldb/Utility/Broadcaster.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,6 @@ class BroadcasterManager
8787

8888
~BroadcasterManager() = default;
8989

90-
uint32_t RegisterListenerForEvents(const lldb::ListenerSP &listener_sp,
91-
const BroadcastEventSpec &event_spec);
92-
93-
bool UnregisterListenerForEvents(const lldb::ListenerSP &listener_sp,
94-
const BroadcastEventSpec &event_spec);
95-
9690
lldb::ListenerSP
9791
GetListenerForEventSpec(const BroadcastEventSpec &event_spec) const;
9892

@@ -105,13 +99,20 @@ class BroadcasterManager
10599
void Clear();
106100

107101
private:
102+
uint32_t
103+
RegisterListenerForEventsNoLock(const lldb::ListenerSP &listener_sp,
104+
const BroadcastEventSpec &event_spec);
105+
106+
bool UnregisterListenerForEventsNoLock(const lldb::ListenerSP &listener_sp,
107+
const BroadcastEventSpec &event_spec);
108+
108109
typedef std::pair<BroadcastEventSpec, lldb::ListenerSP> event_listener_key;
109110
typedef std::map<BroadcastEventSpec, lldb::ListenerSP> collection;
110111
typedef std::set<lldb::ListenerSP> listener_collection;
111112
collection m_event_map;
112113
listener_collection m_listeners;
113114

114-
mutable std::recursive_mutex m_manager_mutex;
115+
mutable std::mutex m_manager_mutex;
115116
};
116117

117118
/// \class Broadcaster Broadcaster.h "lldb/Utility/Broadcaster.h" An event
@@ -441,7 +442,7 @@ class Broadcaster {
441442
collection m_listeners;
442443

443444
/// A mutex that protects \a m_listeners.
444-
std::recursive_mutex m_listeners_mutex;
445+
std::mutex m_listeners_mutex;
445446

446447
/// See the discussion of Broadcasters and Listeners above.
447448
lldb::ListenerSP m_primary_listener_sp;

lldb/source/Utility/Broadcaster.cpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ bool Broadcaster::BroadcasterImpl::HasListeners(uint32_t event_mask) {
8787
}
8888

8989
void Broadcaster::BroadcasterImpl::Clear() {
90-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
90+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
9191

9292
// Make sure the listener forgets about this broadcaster. We do this in the
9393
// broadcaster in case the broadcaster object initiates the removal.
@@ -137,7 +137,7 @@ Broadcaster::BroadcasterImpl::AddListener(const lldb::ListenerSP &listener_sp,
137137
if (!listener_sp)
138138
return 0;
139139

140-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
140+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
141141

142142
// See if we already have this listener, and if so, update its mask
143143

@@ -171,7 +171,7 @@ Broadcaster::BroadcasterImpl::AddListener(const lldb::ListenerSP &listener_sp,
171171
}
172172

173173
bool Broadcaster::BroadcasterImpl::EventTypeHasListeners(uint32_t event_type) {
174-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
174+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
175175

176176
if (!m_hijacking_listeners.empty() && event_type & m_hijacking_masks.back())
177177
return true;
@@ -195,7 +195,7 @@ bool Broadcaster::BroadcasterImpl::RemoveListener(
195195
return true;
196196
}
197197

198-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
198+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
199199
for (auto it = m_listeners.begin(); it != m_listeners.end();) {
200200
lldb::ListenerSP curr_listener_sp(it->first.lock());
201201

@@ -243,7 +243,7 @@ void Broadcaster::BroadcasterImpl::PrivateBroadcastEvent(EventSP &event_sp,
243243

244244
const uint32_t event_type = event_sp->GetType();
245245

246-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
246+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
247247

248248
ListenerSP hijacking_listener_sp;
249249

@@ -327,7 +327,7 @@ void Broadcaster::BroadcasterImpl::SetPrimaryListener(lldb::ListenerSP
327327

328328
bool Broadcaster::BroadcasterImpl::HijackBroadcaster(
329329
const lldb::ListenerSP &listener_sp, uint32_t event_mask) {
330-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
330+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
331331

332332
Log *log = GetLog(LLDBLog::Events);
333333
LLDB_LOG(
@@ -341,7 +341,7 @@ bool Broadcaster::BroadcasterImpl::HijackBroadcaster(
341341
}
342342

343343
bool Broadcaster::BroadcasterImpl::IsHijackedForEvent(uint32_t event_mask) {
344-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
344+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
345345

346346
if (!m_hijacking_listeners.empty())
347347
return (event_mask & m_hijacking_masks.back()) != 0;
@@ -356,7 +356,7 @@ const char *Broadcaster::BroadcasterImpl::GetHijackingListenerName() {
356356
}
357357

358358
void Broadcaster::BroadcasterImpl::RestoreBroadcaster() {
359-
std::lock_guard<std::recursive_mutex> guard(m_listeners_mutex);
359+
std::lock_guard<std::mutex> guard(m_listeners_mutex);
360360

361361
if (!m_hijacking_listeners.empty()) {
362362
ListenerSP listener_sp = m_hijacking_listeners.back();
@@ -391,10 +391,8 @@ lldb::BroadcasterManagerSP BroadcasterManager::MakeBroadcasterManager() {
391391
return lldb::BroadcasterManagerSP(new BroadcasterManager());
392392
}
393393

394-
uint32_t BroadcasterManager::RegisterListenerForEvents(
394+
uint32_t BroadcasterManager::RegisterListenerForEventsNoLock(
395395
const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) {
396-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
397-
398396
collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end();
399397
uint32_t available_bits = event_spec.GetEventBits();
400398

@@ -419,9 +417,8 @@ uint32_t BroadcasterManager::RegisterListenerForEvents(
419417
return available_bits;
420418
}
421419

422-
bool BroadcasterManager::UnregisterListenerForEvents(
420+
bool BroadcasterManager::UnregisterListenerForEventsNoLock(
423421
const lldb::ListenerSP &listener_sp, const BroadcastEventSpec &event_spec) {
424-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
425422
bool removed_some = false;
426423

427424
if (m_listeners.erase(listener_sp) == 0)
@@ -464,7 +461,7 @@ bool BroadcasterManager::UnregisterListenerForEvents(
464461

465462
ListenerSP BroadcasterManager::GetListenerForEventSpec(
466463
const BroadcastEventSpec &event_spec) const {
467-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
464+
std::lock_guard<std::mutex> guard(m_manager_mutex);
468465

469466
auto event_spec_matches =
470467
[&event_spec](const event_listener_key &input) -> bool {
@@ -479,7 +476,7 @@ ListenerSP BroadcasterManager::GetListenerForEventSpec(
479476
}
480477

481478
void BroadcasterManager::RemoveListener(Listener *listener) {
482-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
479+
std::lock_guard<std::mutex> guard(m_manager_mutex);
483480
auto listeners_predicate =
484481
[&listener](const lldb::ListenerSP &input) -> bool {
485482
return input.get() == listener;
@@ -504,7 +501,7 @@ void BroadcasterManager::RemoveListener(Listener *listener) {
504501
}
505502

506503
void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) {
507-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
504+
std::lock_guard<std::mutex> guard(m_manager_mutex);
508505

509506
auto listener_matches =
510507
[&listener_sp](const event_listener_key &input) -> bool {
@@ -526,7 +523,7 @@ void BroadcasterManager::RemoveListener(const lldb::ListenerSP &listener_sp) {
526523

527524
void BroadcasterManager::SignUpListenersForBroadcaster(
528525
Broadcaster &broadcaster) {
529-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
526+
std::lock_guard<std::mutex> guard(m_manager_mutex);
530527

531528
collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end();
532529

@@ -544,7 +541,7 @@ void BroadcasterManager::SignUpListenersForBroadcaster(
544541
}
545542

546543
void BroadcasterManager::Clear() {
547-
std::lock_guard<std::recursive_mutex> guard(m_manager_mutex);
544+
std::lock_guard<std::mutex> guard(m_manager_mutex);
548545

549546
for (auto &listener : m_listeners)
550547
listener->BroadcasterManagerWillDestruct(this->shared_from_this());

lldb/source/Utility/Listener.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,10 @@ Listener::StartListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
356356
};
357357
// The BroadcasterManager mutex must be locked before m_broadcasters_mutex to
358358
// avoid violating the lock hierarchy (manager before broadcasters).
359-
std::lock_guard<std::recursive_mutex> manager_guard(
360-
manager_sp->m_manager_mutex);
359+
std::lock_guard<std::mutex> manager_guard(manager_sp->m_manager_mutex);
361360
std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
362361

363-
uint32_t bits_acquired = manager_sp->RegisterListenerForEvents(
362+
uint32_t bits_acquired = manager_sp->RegisterListenerForEventsNoLock(
364363
this->shared_from_this(), event_spec);
365364
if (bits_acquired) {
366365
BroadcasterManagerWP manager_wp(manager_sp);
@@ -377,9 +376,12 @@ bool Listener::StopListeningForEventSpec(const BroadcasterManagerSP &manager_sp,
377376
if (!manager_sp)
378377
return false;
379378

379+
// The BroadcasterManager mutex must be locked before m_broadcasters_mutex to
380+
// avoid violating the lock hierarchy (manager before broadcasters).
381+
std::lock_guard<std::mutex> manager_guard(manager_sp->m_manager_mutex);
380382
std::lock_guard<std::recursive_mutex> guard(m_broadcasters_mutex);
381-
return manager_sp->UnregisterListenerForEvents(this->shared_from_this(),
382-
event_spec);
383+
return manager_sp->UnregisterListenerForEventsNoLock(this->shared_from_this(),
384+
event_spec);
383385
}
384386

385387
ListenerSP Listener::MakeListener(const char *name) {

lldb/unittests/Utility/ListenerTest.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "gtest/gtest.h"
1010

1111
#include "lldb/Utility/Broadcaster.h"
12+
#include "lldb/Utility/Event.h"
1213
#include "lldb/Utility/Listener.h"
1314
#include <future>
1415
#include <thread>
@@ -111,3 +112,41 @@ TEST(ListenerTest, GetEventWait) {
111112
&broadcaster, event_mask, event_sp, std::nullopt));
112113
async_broadcast.get();
113114
}
115+
116+
TEST(ListenerTest, StartStopListeningForEventSpec) {
117+
constexpr uint32_t event_mask = 1;
118+
static constexpr llvm::StringLiteral broadcaster_class = "broadcaster-class";
119+
120+
class TestBroadcaster : public Broadcaster {
121+
using Broadcaster::Broadcaster;
122+
llvm::StringRef GetBroadcasterClass() const override {
123+
return broadcaster_class;
124+
}
125+
};
126+
127+
BroadcasterManagerSP manager_sp =
128+
BroadcasterManager::MakeBroadcasterManager();
129+
ListenerSP listener_sp = Listener::MakeListener("test-listener");
130+
131+
// Create two broadcasters, one while we're waiting for new broadcasters, and
132+
// one when we're not.
133+
ASSERT_EQ(listener_sp->StartListeningForEventSpec(
134+
manager_sp, BroadcastEventSpec(broadcaster_class, event_mask)),
135+
event_mask);
136+
TestBroadcaster broadcaster1(manager_sp, "test-broadcaster-1");
137+
broadcaster1.CheckInWithManager();
138+
ASSERT_TRUE(listener_sp->StopListeningForEventSpec(
139+
manager_sp, BroadcastEventSpec(broadcaster_class, event_mask)));
140+
TestBroadcaster broadcaster2(manager_sp, "test-broadcaster-2");
141+
broadcaster2.CheckInWithManager();
142+
143+
// Use both broadcasters to send an event.
144+
for (auto *b : {&broadcaster1, &broadcaster2})
145+
b->BroadcastEvent(event_mask, nullptr);
146+
147+
// Use should only get the event from the first one.
148+
EventSP event_sp;
149+
ASSERT_TRUE(listener_sp->GetEvent(event_sp, std::chrono::seconds(0)));
150+
ASSERT_EQ(event_sp->GetBroadcaster(), &broadcaster1);
151+
ASSERT_FALSE(listener_sp->GetEvent(event_sp, std::chrono::seconds(0)));
152+
}

0 commit comments

Comments
 (0)