Skip to content

Commit 1c61751

Browse files
authored
Fix occasionally missing goal result caused by race condition (ros2#1677)
* Fix occasionally missing goal result caused by race condition Signed-off-by: Kaven Yau <[email protected]> * Take action_server_reentrant_mutex_ out of the sending result loop Signed-off-by: Kaven Yau <[email protected]> * add note for explaining the current locking order in server.cpp Signed-off-by: Kaven Yau <[email protected]>
1 parent 0251f70 commit 1c61751

File tree

1 file changed

+25
-14
lines changed

1 file changed

+25
-14
lines changed

rclcpp_action/src/server.cpp

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,13 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
500500
result_response = create_result_response(action_msgs::msg::GoalStatus::STATUS_UNKNOWN);
501501
} else {
502502
// Goal exists, check if a result is already available
503+
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
503504
auto iter = pimpl_->goal_results_.find(uuid);
504505
if (iter != pimpl_->goal_results_.end()) {
505506
result_response = iter->second;
507+
} else {
508+
// Store the request so it can be responded to later
509+
pimpl_->result_requests_[uuid].push_back(request_header);
506510
}
507511
}
508512

@@ -514,10 +518,6 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
514518
if (RCL_RET_OK != rcl_ret) {
515519
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
516520
}
517-
} else {
518-
// Store the request so it can be responded to later
519-
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
520-
pimpl_->result_requests_[uuid].push_back(request_header);
521521
}
522522
data.reset();
523523
}
@@ -627,19 +627,30 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
627627
}
628628

629629
{
630-
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
630+
/**
631+
* NOTE: There is a potential deadlock issue if both unordered_map_mutex_ and
632+
* action_server_reentrant_mutex_ locked in other block scopes. Unless using
633+
* std::scoped_lock, locking order must be consistent with the current.
634+
*
635+
* Current locking order:
636+
*
637+
* 1. unordered_map_mutex_
638+
* 2. action_server_reentrant_mutex_
639+
*
640+
*/
641+
std::lock_guard<std::recursive_mutex> unordered_map_lock(pimpl_->unordered_map_mutex_);
631642
pimpl_->goal_results_[uuid] = result_msg;
632-
}
633643

634-
// if there are clients who already asked for the result, send it to them
635-
auto iter = pimpl_->result_requests_.find(uuid);
636-
if (iter != pimpl_->result_requests_.end()) {
637-
for (auto & request_header : iter->second) {
644+
// if there are clients who already asked for the result, send it to them
645+
auto iter = pimpl_->result_requests_.find(uuid);
646+
if (iter != pimpl_->result_requests_.end()) {
638647
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
639-
rcl_ret_t ret = rcl_action_send_result_response(
640-
pimpl_->action_server_.get(), &request_header, result_msg.get());
641-
if (RCL_RET_OK != ret) {
642-
rclcpp::exceptions::throw_from_rcl_error(ret);
648+
for (auto & request_header : iter->second) {
649+
rcl_ret_t ret = rcl_action_send_result_response(
650+
pimpl_->action_server_.get(), &request_header, result_msg.get());
651+
if (RCL_RET_OK != ret) {
652+
rclcpp::exceptions::throw_from_rcl_error(ret);
653+
}
643654
}
644655
}
645656
}

0 commit comments

Comments
 (0)