Skip to content

Commit 502334f

Browse files
Janosch MachowinskiJanosch Machowinski
authored andcommitted
!fix(Client): Do not hold goal_handle ptr if result cb is set.
The capture of the lambda held an copy of the shared_ptr to the goal handle. This lead to the behavior that the goal callbacks would be called, even though the user dropped the handle. Note, this will break the current tutorial, as it restores the documented behavior that a goal will be invalidated as soon as one drops the handle. Signed-off-by: Janosch Machowinski <[email protected]>
1 parent 1b83802 commit 502334f

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

rclcpp_action/include/rclcpp_action/client.hpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -726,23 +726,40 @@ class Client : public ClientBase, public std::enable_shared_from_this<Client<Act
726726
}
727727
using GoalResultRequest = typename ActionT::Impl::GetResultService::Request;
728728
auto goal_result_request = std::make_shared<GoalResultRequest>();
729+
const GoalUUID goal_id = goal_handle->get_goal_id();
729730
goal_result_request->goal_id.uuid = goal_handle->get_goal_id();
730731
try {
731732
this->send_result_request(
732733
std::static_pointer_cast<void>(goal_result_request),
733-
[goal_handle, this](std::shared_ptr<void> response) mutable
734+
[goal_id, this](std::shared_ptr<void> response) mutable
734735
{
736+
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
737+
if (goal_handles_.count(goal_id) == 0) {
738+
RCLCPP_DEBUG(
739+
this->get_logger(),
740+
"Received result for unknown goal. Ignoring...");
741+
return;
742+
}
743+
typename GoalHandle::SharedPtr goal_handle_ = goal_handles_[goal_id].lock();
744+
// Forget about the goal if there are no more user references
745+
if (!goal_handle_) {
746+
RCLCPP_DEBUG(
747+
this->get_logger(),
748+
"Dropping weak reference to goal handle during result callback");
749+
goal_handles_.erase(goal_id);
750+
return;
751+
}
752+
735753
// Wrap the response in a struct with the fields a user cares about
736754
WrappedResult wrapped_result;
737755
using GoalResultResponse = typename ActionT::Impl::GetResultService::Response;
738756
auto result_response = std::static_pointer_cast<GoalResultResponse>(response);
739757
wrapped_result.result = std::make_shared<typename ActionT::Result>();
740758
*wrapped_result.result = result_response->result;
741-
wrapped_result.goal_id = goal_handle->get_goal_id();
759+
wrapped_result.goal_id = goal_id;
742760
wrapped_result.code = static_cast<ResultCode>(result_response->status);
743-
goal_handle->set_result(wrapped_result);
744-
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
745-
goal_handles_.erase(goal_handle->get_goal_id());
761+
goal_handle_->set_result(wrapped_result);
762+
goal_handles_.erase(goal_id);
746763
});
747764
} catch (rclcpp::exceptions::RCLError & ex) {
748765
// This will cause an exception when the user tries to access the result

0 commit comments

Comments
 (0)