Skip to content

Commit c364c3c

Browse files
Fixes "Taking data from the action action but nothing is ready" exception (#1)
* [rclcpp_action] fix race condition between take_data() and execute() * [rclcpp] skip adding waitable to triggered list if it's already in it
1 parent 45df355 commit c364c3c

File tree

2 files changed

+53
-73
lines changed

2 files changed

+53
-73
lines changed

rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,13 @@ class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
122122
}
123123
for (size_t i = 0; i < waitable_handles_.size(); ++i) {
124124
if (waitable_handles_[i]->is_ready(wait_set)) {
125-
waitable_triggered_handles_.emplace_back(std::move(waitable_handles_[i]));
125+
auto it = std::find(
126+
waitable_triggered_handles_.begin(),
127+
waitable_triggered_handles_.end(),
128+
waitable_handles_[i]);
129+
if (it == waitable_triggered_handles_.end()) {
130+
waitable_triggered_handles_.emplace_back(std::move(waitable_handles_[i]));
131+
}
126132
}
127133
}
128134

rclcpp_action/src/client.cpp

Lines changed: 46 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class ClientBaseImpl
122122
std::default_random_engine, 8, unsigned int> random_bytes_generator;
123123
};
124124

125+
using DataMessage = std::tuple<rcl_ret_t, ClientBase::EntityType, std::shared_ptr<void>>;
126+
using ResponseMessage = std::tuple<rmw_request_id_t, std::shared_ptr<void>>;
127+
125128
ClientBase::ClientBase(
126129
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
127130
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
@@ -552,42 +555,47 @@ ClientBase::take_data()
552555
{
553556
if (pimpl_->is_feedback_ready) {
554557
std::shared_ptr<void> feedback_message = this->create_feedback_message();
555-
rcl_ret_t ret = rcl_action_take_feedback(
556-
pimpl_->client_handle.get(), feedback_message.get());
558+
rcl_ret_t ret = rcl_action_take_feedback(pimpl_->client_handle.get(), feedback_message.get());
559+
pimpl_->is_feedback_ready = false;
557560
return std::static_pointer_cast<void>(
558-
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
559-
ret, feedback_message));
561+
std::make_shared<DataMessage>(ret, EntityType::FeedbackSubscription, feedback_message));
560562
} else if (pimpl_->is_status_ready) {
561563
std::shared_ptr<void> status_message = this->create_status_message();
562-
rcl_ret_t ret = rcl_action_take_status(
563-
pimpl_->client_handle.get(), status_message.get());
564+
rcl_ret_t ret = rcl_action_take_status(pimpl_->client_handle.get(), status_message.get());
565+
pimpl_->is_status_ready = false;
564566
return std::static_pointer_cast<void>(
565-
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
566-
ret, status_message));
567+
std::make_shared<DataMessage>(ret, EntityType::StatusSubscription, status_message));
567568
} else if (pimpl_->is_goal_response_ready) {
568569
rmw_request_id_t response_header;
569570
std::shared_ptr<void> goal_response = this->create_goal_response();
570571
rcl_ret_t ret = rcl_action_take_goal_response(
571572
pimpl_->client_handle.get(), &response_header, goal_response.get());
573+
pimpl_->is_goal_response_ready = false;
572574
return std::static_pointer_cast<void>(
573-
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
574-
ret, response_header, goal_response));
575+
std::make_shared<DataMessage>(
576+
ret, EntityType::GoalClient,
577+
std::make_shared<ResponseMessage>(response_header, goal_response)));
575578
} else if (pimpl_->is_result_response_ready) {
576579
rmw_request_id_t response_header;
577580
std::shared_ptr<void> result_response = this->create_result_response();
578581
rcl_ret_t ret = rcl_action_take_result_response(
579-
pimpl_->client_handle.get(), &response_header, result_response.get());
582+
pimpl_->client_handle.get(),
583+
&response_header, result_response.get());
584+
pimpl_->is_result_response_ready = false;
580585
return std::static_pointer_cast<void>(
581-
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
582-
ret, response_header, result_response));
586+
std::make_shared<DataMessage>(
587+
ret, EntityType::ResultClient,
588+
std::make_shared<ResponseMessage>(response_header, result_response)));
583589
} else if (pimpl_->is_cancel_response_ready) {
584590
rmw_request_id_t response_header;
585591
std::shared_ptr<void> cancel_response = this->create_cancel_response();
586592
rcl_ret_t ret = rcl_action_take_cancel_response(
587593
pimpl_->client_handle.get(), &response_header, cancel_response.get());
594+
pimpl_->is_cancel_response_ready = false;
588595
return std::static_pointer_cast<void>(
589-
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
590-
ret, response_header, cancel_response));
596+
std::make_shared<DataMessage>(
597+
ret, EntityType::CancelClient,
598+
std::make_shared<ResponseMessage>(response_header, cancel_response)));
591599
} else {
592600
throw std::runtime_error("Taking data from action client but nothing is ready");
593601
}
@@ -625,64 +633,30 @@ ClientBase::execute(std::shared_ptr<void> & data)
625633
throw std::runtime_error("'data' is empty");
626634
}
627635

628-
if (pimpl_->is_feedback_ready) {
629-
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
630-
auto ret = std::get<0>(*shared_ptr);
631-
pimpl_->is_feedback_ready = false;
632-
if (RCL_RET_OK == ret) {
633-
auto feedback_message = std::get<1>(*shared_ptr);
634-
this->handle_feedback_message(feedback_message);
635-
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
636-
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
637-
}
638-
} else if (pimpl_->is_status_ready) {
639-
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
640-
auto ret = std::get<0>(*shared_ptr);
641-
pimpl_->is_status_ready = false;
642-
if (RCL_RET_OK == ret) {
643-
auto status_message = std::get<1>(*shared_ptr);
644-
this->handle_status_message(status_message);
645-
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
646-
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
647-
}
648-
} else if (pimpl_->is_goal_response_ready) {
649-
auto shared_ptr = std::static_pointer_cast<
650-
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
651-
auto ret = std::get<0>(*shared_ptr);
652-
pimpl_->is_goal_response_ready = false;
653-
if (RCL_RET_OK == ret) {
654-
auto response_header = std::get<1>(*shared_ptr);
655-
auto goal_response = std::get<2>(*shared_ptr);
656-
this->handle_goal_response(response_header, goal_response);
657-
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
658-
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
659-
}
660-
} else if (pimpl_->is_result_response_ready) {
661-
auto shared_ptr = std::static_pointer_cast<
662-
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
663-
auto ret = std::get<0>(*shared_ptr);
664-
pimpl_->is_result_response_ready = false;
665-
if (RCL_RET_OK == ret) {
666-
auto response_header = std::get<1>(*shared_ptr);
667-
auto result_response = std::get<2>(*shared_ptr);
668-
this->handle_result_response(response_header, result_response);
669-
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
670-
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
671-
}
672-
} else if (pimpl_->is_cancel_response_ready) {
673-
auto shared_ptr = std::static_pointer_cast<
674-
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
675-
auto ret = std::get<0>(*shared_ptr);
676-
pimpl_->is_cancel_response_ready = false;
677-
if (RCL_RET_OK == ret) {
678-
auto response_header = std::get<1>(*shared_ptr);
679-
auto cancel_response = std::get<2>(*shared_ptr);
680-
this->handle_cancel_response(response_header, cancel_response);
681-
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
682-
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
683-
}
636+
auto data_ptr = std::static_pointer_cast<DataMessage>(data);
637+
auto ret = std::get<0>(*data_ptr);
638+
if (RCL_RET_OK != ret) {
639+
rclcpp::exceptions::throw_from_rcl_error(ret, "execute is called on invalid data");
640+
}
641+
642+
auto type = std::get<1>(*data_ptr);
643+
if (EntityType::FeedbackSubscription == type) {
644+
auto message = std::get<2>(*data_ptr);
645+
handle_feedback_message(message);
646+
} else if (EntityType::StatusSubscription == type) {
647+
auto message = std::get<2>(*data_ptr);
648+
this->handle_status_message(message);
649+
} else if (EntityType::GoalClient == type) {
650+
auto message = std::static_pointer_cast<ResponseMessage>(std::get<2>(*data_ptr));
651+
handle_goal_response(std::get<0>(*message), std::get<1>(*message));
652+
} else if (EntityType::ResultClient == type) {
653+
auto message = std::static_pointer_cast<ResponseMessage>(std::get<2>(*data_ptr));
654+
handle_result_response(std::get<0>(*message), std::get<1>(*message));
655+
} else if (EntityType::CancelClient == type) {
656+
auto message = std::static_pointer_cast<ResponseMessage>(std::get<2>(*data_ptr));
657+
handle_cancel_response(std::get<0>(*message), std::get<1>(*message));
684658
} else {
685-
throw std::runtime_error("Executing action client but nothing is ready");
659+
throw std::logic_error("Unknown entity type");
686660
}
687661
}
688662

0 commit comments

Comments
 (0)