Skip to content

Commit fba080c

Browse files
KavenYauKaven Yaujacobperronwjwwood
authored
Fix action server deadlock issue that caused by other mutexes locked in CancelCallback (#1635)
* Fix deadlock issue that caused by other mutexes locked in CancelCallback Signed-off-by: Kaven Yau <[email protected]> * Add unit test for rclcpp action server deadlock Signed-off-by: Kaven Yau <[email protected]> * Update rclcpp_action/test/test_server.cpp Co-authored-by: William Woodall <[email protected]> Co-authored-by: Kaven Yau <[email protected]> Co-authored-by: Jacob Perron <[email protected]> Co-authored-by: William Woodall <[email protected]>
1 parent bff6916 commit fba080c

2 files changed

Lines changed: 27 additions & 10 deletions

File tree

rclcpp_action/include/rclcpp_action/server.hpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -356,16 +356,20 @@ class Server : public ServerBase, public std::enable_shared_from_this<Server<Act
356356
CancelResponse
357357
call_handle_cancel_callback(const GoalUUID & uuid) override
358358
{
359-
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
359+
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle;
360+
{
361+
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
362+
auto element = goal_handles_.find(uuid);
363+
if (element != goal_handles_.end()) {
364+
goal_handle = element->second.lock();
365+
}
366+
}
367+
360368
CancelResponse resp = CancelResponse::REJECT;
361-
auto element = goal_handles_.find(uuid);
362-
if (element != goal_handles_.end()) {
363-
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle = element->second.lock();
364-
if (goal_handle) {
365-
resp = handle_cancel_(goal_handle);
366-
if (CancelResponse::ACCEPT == resp) {
367-
goal_handle->_cancel_goal();
368-
}
369+
if (goal_handle) {
370+
resp = handle_cancel_(goal_handle);
371+
if (CancelResponse::ACCEPT == resp) {
372+
goal_handle->_cancel_goal();
369373
}
370374
}
371375
return resp;

rclcpp_action/test/test_server.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,10 +1234,14 @@ class TestDeadlockServer : public TestServer
12341234
this->TryLockFor(lock, std::chrono::milliseconds(1000));
12351235
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
12361236
},
1237-
[this](std::shared_ptr<GoalHandle>) {
1237+
[this](std::shared_ptr<GoalHandle> handle) {
12381238
// instead of making a deadlock, check if it can acquire the lock in a second
12391239
std::unique_lock<std::recursive_timed_mutex> lock(server_mutex_, std::defer_lock);
12401240
this->TryLockFor(lock, std::chrono::milliseconds(1000));
1241+
// TODO(KavenYau): this check may become obsolete with https://github.com/ros2/rclcpp/issues/1599
1242+
if (!handle->is_active()) {
1243+
return rclcpp_action::CancelResponse::REJECT;
1244+
}
12411245
return rclcpp_action::CancelResponse::ACCEPT;
12421246
},
12431247
[this](std::shared_ptr<GoalHandle> handle) {
@@ -1306,3 +1310,12 @@ TEST_F(TestDeadlockServer, deadlock_while_canceled)
13061310
send_goal_request(node_, uuid2_); // deadlock here
13071311
t.join();
13081312
}
1313+
1314+
TEST_F(TestDeadlockServer, deadlock_while_succeed_and_canceled)
1315+
{
1316+
send_goal_request(node_, uuid1_);
1317+
std::thread t(&TestDeadlockServer::GoalSucceeded, this);
1318+
rclcpp::sleep_for(std::chrono::milliseconds(50));
1319+
send_cancel_request(node_, uuid1_);
1320+
t.join();
1321+
}

0 commit comments

Comments
 (0)