Skip to content

Commit b97ad4a

Browse files
Avoid deadlocks when dispatching graph event callbacks
Queue graph event callbacks while holding graph_mutex_, then dispatch them after releasing internal locks. This preserves graph event ordering across concurrent graph updates while avoiding callback execution under graph_mutex_ or events_mutex_. Also move event callbacks out of event_mutex_ critical sections and use weak subscription data captures for graph event callbacks. Catch and log exceptions from queued graph callbacks so one failing callback does not leave dispatching stuck or block later events. Signed-off-by: zhufangda fangda.zhu@vbot.cn
1 parent b396570 commit b97ad4a

4 files changed

Lines changed: 401 additions & 232 deletions

File tree

rmw_zenoh_cpp/src/detail/event.cpp

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -68,31 +68,53 @@ rmw_zenoh_event_status_t::rmw_zenoh_event_status_t()
6868
void DataCallbackManager::set_callback(
6969
const void * user_data, rmw_event_callback_t callback)
7070
{
71-
std::lock_guard<std::mutex> lock_mutex(event_mutex_);
71+
rmw_event_callback_t pending_callback = nullptr;
72+
const void * pending_user_data = nullptr;
73+
size_t pending_unread_count = 0;
7274

73-
if (callback) {
74-
// Push events arrived before setting the the executor callback.
75-
if (unread_count_) {
76-
callback(user_data, unread_count_);
77-
unread_count_ = 0;
75+
{
76+
std::lock_guard<std::mutex> lock_mutex(event_mutex_);
77+
78+
if (callback) {
79+
// Push events arrived before setting the the executor callback.
80+
if (unread_count_) {
81+
pending_callback = callback;
82+
pending_user_data = user_data;
83+
pending_unread_count = unread_count_;
84+
unread_count_ = 0;
85+
}
86+
user_data_ = user_data;
87+
callback_ = callback;
88+
} else {
89+
user_data_ = nullptr;
90+
callback_ = nullptr;
7891
}
79-
user_data_ = user_data;
80-
callback_ = callback;
81-
} else {
82-
user_data_ = nullptr;
83-
callback_ = nullptr;
92+
}
93+
94+
if (pending_callback != nullptr) {
95+
pending_callback(pending_user_data, pending_unread_count);
8496
}
8597
}
8698

8799
///=============================================================================
88100
void DataCallbackManager::trigger_callback()
89101
{
90102
// Trigger the user provided event callback if available.
91-
std::lock_guard<std::mutex> lock_mutex(event_mutex_);
92-
if (callback_ != nullptr) {
93-
callback_(user_data_, 1);
94-
} else {
95-
++unread_count_;
103+
rmw_event_callback_t callback = nullptr;
104+
const void * user_data = nullptr;
105+
106+
{
107+
std::lock_guard<std::mutex> lock_mutex(event_mutex_);
108+
if (callback_ != nullptr) {
109+
callback = callback_;
110+
user_data = user_data_;
111+
} else {
112+
++unread_count_;
113+
}
114+
}
115+
116+
if (callback != nullptr) {
117+
callback(user_data, 1);
96118
}
97119
}
98120

@@ -110,16 +132,28 @@ void EventsManager::event_set_callback(
110132
return;
111133
}
112134

113-
std::lock_guard<std::mutex> lock(event_mutex_);
135+
rmw_event_callback_t pending_callback = nullptr;
136+
const void * pending_user_data = nullptr;
137+
size_t pending_unread_count = 0;
138+
139+
{
140+
std::lock_guard<std::mutex> lock(event_mutex_);
141+
142+
// Set the user callback data
143+
event_callback_[event_id] = callback;
144+
event_data_[event_id] = user_data;
114145

115-
// Set the user callback data
116-
event_callback_[event_id] = callback;
117-
event_data_[event_id] = user_data;
146+
if (callback && event_unread_count_[event_id]) {
147+
// Push events happened before having assigned a callback
148+
pending_callback = callback;
149+
pending_user_data = user_data;
150+
pending_unread_count = event_unread_count_[event_id];
151+
event_unread_count_[event_id] = 0;
152+
}
153+
}
118154

119-
if (callback && event_unread_count_[event_id]) {
120-
// Push events happened before having assigned a callback
121-
callback(user_data, event_unread_count_[event_id]);
122-
event_unread_count_[event_id] = 0;
155+
if (pending_callback != nullptr) {
156+
pending_callback(pending_user_data, pending_unread_count);
123157
}
124158
return;
125159
}
@@ -135,12 +169,22 @@ void EventsManager::trigger_event_callback(rmw_zenoh_event_type_t event_id)
135169
return;
136170
}
137171

138-
std::lock_guard<std::mutex> lock(event_mutex_);
172+
rmw_event_callback_t callback = nullptr;
173+
const void * user_data = nullptr;
174+
175+
{
176+
std::lock_guard<std::mutex> lock(event_mutex_);
177+
178+
if (event_callback_[event_id] != nullptr) {
179+
callback = event_callback_[event_id];
180+
user_data = event_data_[event_id];
181+
} else {
182+
++event_unread_count_[event_id];
183+
}
184+
}
139185

140-
if (event_callback_[event_id] != nullptr) {
141-
event_callback_[event_id](event_data_[event_id], 1);
142-
} else {
143-
++event_unread_count_[event_id];
186+
if (callback != nullptr) {
187+
callback(user_data, 1);
144188
}
145189
return;
146190
}

0 commit comments

Comments
 (0)