Skip to content

Commit 7ebc9e4

Browse files
jmachowinskiJanosch Machowinskifujitatomoya
authored
fix: Don't deadlock if removing shutdown callbacks in a shutdown callback (#2886)
Signed-off-by: Janosch Machowinski <[email protected]> Co-authored-by: Janosch Machowinski <[email protected]> Co-authored-by: Tomoya Fujita <[email protected]>
1 parent 05eafed commit 7ebc9e4

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

rclcpp/include/rclcpp/context.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ class Context : public std::enable_shared_from_this<Context>
381381
std::recursive_mutex sub_contexts_mutex_;
382382

383383
std::vector<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
384-
mutable std::mutex on_shutdown_callbacks_mutex_;
384+
mutable std::recursive_mutex on_shutdown_callbacks_mutex_;
385385

386386
std::vector<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
387-
mutable std::mutex pre_shutdown_callbacks_mutex_;
387+
mutable std::recursive_mutex pre_shutdown_callbacks_mutex_;
388388

389389
/// Condition variable for timed sleep (see sleep_for).
390390
std::condition_variable interrupt_condition_variable_;

rclcpp/src/rclcpp/context.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,16 @@ Context::shutdown(const std::string & reason)
310310

311311
// call each pre-shutdown callback
312312
{
313-
std::lock_guard<std::mutex> lock{pre_shutdown_callbacks_mutex_};
314-
for (const auto & callback : pre_shutdown_callbacks_) {
315-
(*callback)();
313+
std::lock_guard<std::recursive_mutex> lock{pre_shutdown_callbacks_mutex_};
314+
// callbacks may delete other callbacks during the execution,
315+
// therefore we need to save a copy and check before execution
316+
// if the next callback is still present
317+
auto cpy = pre_shutdown_callbacks_;
318+
for (const auto & callback : cpy) {
319+
auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
320+
if(it != pre_shutdown_callbacks_.end()) {
321+
(*callback)();
322+
}
316323
}
317324
}
318325

@@ -325,9 +332,16 @@ Context::shutdown(const std::string & reason)
325332
shutdown_reason_ = reason;
326333
// call each shutdown callback
327334
{
328-
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
329-
for (const auto & callback : on_shutdown_callbacks_) {
330-
(*callback)();
335+
std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
336+
// callbacks may delete other callbacks during the execution,
337+
// therefore we need to save a copy and check before execution
338+
// if the next callback is still present
339+
auto cpy = on_shutdown_callbacks_;
340+
for (const auto & callback : cpy) {
341+
auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
342+
if(it != on_shutdown_callbacks_.end()) {
343+
(*callback)();
344+
}
331345
}
332346
}
333347

@@ -398,10 +412,10 @@ Context::add_shutdown_callback(
398412
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
399413

400414
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
401-
std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
415+
std::lock_guard<std::recursive_mutex> lock(pre_shutdown_callbacks_mutex_);
402416
pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
403417
} else {
404-
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
418+
std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
405419
on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
406420
}
407421

@@ -421,7 +435,7 @@ Context::remove_shutdown_callback(
421435
}
422436

423437
const auto remove_callback = [&callback_shared_ptr](auto & mutex, auto & callback_vector) {
424-
const std::lock_guard<std::mutex> lock(mutex);
438+
const std::lock_guard<std::recursive_mutex> lock(mutex);
425439
auto iter = callback_vector.begin();
426440
for (; iter != callback_vector.end(); iter++) {
427441
if ((*iter).get() == callback_shared_ptr.get()) {
@@ -462,7 +476,7 @@ std::vector<rclcpp::Context::ShutdownCallback>
462476
Context::get_shutdown_callback() const
463477
{
464478
const auto get_callback_vector = [](auto & mutex, auto & callback_set) {
465-
const std::lock_guard<std::mutex> lock(mutex);
479+
const std::lock_guard<std::recursive_mutex> lock(mutex);
466480
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
467481
for (auto & callback : callback_set) {
468482
callbacks.push_back(*callback);

0 commit comments

Comments
 (0)