2626#include " rcpputils/scope_exit.hpp"
2727
2828#include " action_msgs/msg/goal_status_array.hpp"
29- #include " action_msgs/srv/cancel_goal.hpp"
3029#include " rclcpp/exceptions.hpp"
3130#include " rclcpp_action/server.hpp"
3231
@@ -65,6 +64,11 @@ class ServerBaseImpl
6564 std::atomic<bool > result_request_ready_{false };
6665 std::atomic<bool > goal_expired_{false };
6766
67+ // variable used to check, that the sequence of
68+ // is_ready and take_data is never interrupted by
69+ // another thread.
70+ std::atomic<bool > threadExlusive{false };
71+
6872 // Lock for unordered_maps
6973 std::recursive_mutex unordered_map_mutex_;
7074
@@ -78,6 +82,30 @@ class ServerBaseImpl
7882 rclcpp::Logger logger_;
7983};
8084} // namespace rclcpp_action
85+ struct ServerBaseData
86+ {
87+ using GoalRequestData = std::tuple<rcl_ret_t , const rcl_action_goal_info_t , rmw_request_id_t ,
88+ std::shared_ptr<void >>;
89+
90+ using CancelRequestData = std::tuple<rcl_ret_t ,
91+ std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
92+ rmw_request_id_t >;
93+
94+ using ResultRequestData = std::tuple<rcl_ret_t , std::shared_ptr<void >, rmw_request_id_t >;
95+
96+ using GoalExpiredData = struct Empty {};
97+
98+ std::variant<GoalRequestData, CancelRequestData, ResultRequestData, GoalExpiredData> data;
99+
100+ explicit ServerBaseData (GoalRequestData && dataIn)
101+ : data(std::move(dataIn)) {}
102+ explicit ServerBaseData (CancelRequestData && dataIn)
103+ : data(std::move(dataIn)) {}
104+ explicit ServerBaseData (ResultRequestData && dataIn)
105+ : data(std::move(dataIn)) {}
106+ explicit ServerBaseData (GoalExpiredData && dataIn)
107+ : data(std::move(dataIn)) {}
108+ };
81109
82110ServerBase::ServerBase (
83111 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
@@ -178,6 +206,11 @@ ServerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
178206bool
179207ServerBase::is_ready (rcl_wait_set_t * wait_set)
180208{
209+ if (pimpl_->threadExlusive .exchange (true )) {
210+ throw std::runtime_error (
211+ " ServerBase::Internal error, is_ready called, before take_data was called" );
212+ }
213+
181214 bool goal_request_ready;
182215 bool cancel_request_ready;
183216 bool result_request_ready;
@@ -203,16 +236,31 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set)
203236 rclcpp::exceptions::throw_from_rcl_error (ret);
204237 }
205238
206- return pimpl_->goal_request_ready_ .load () ||
207- pimpl_->cancel_request_ready_ .load () ||
208- pimpl_->result_request_ready_ .load () ||
209- pimpl_->goal_expired_ .load ();
239+
240+ bool isReady = pimpl_->goal_request_ready_ .load () ||
241+ pimpl_->cancel_request_ready_ .load () ||
242+ pimpl_->result_request_ready_ .load () ||
243+ pimpl_->goal_expired_ .load ();
244+
245+ if (!isReady) {
246+ if (!pimpl_->threadExlusive .exchange (false )) {
247+ throw std::runtime_error (" ServerBase::Internal error, is_ready reentrant called" );
248+ }
249+ }
250+ return isReady;
210251}
211252
212253std::shared_ptr<void >
213254ServerBase::take_data ()
214255{
215- if (pimpl_->goal_request_ready_ .load ()) {
256+ auto checkCallSequence = [this ]() {
257+ if (!pimpl_->threadExlusive .exchange (false )) {
258+ throw std::runtime_error (
259+ " ServerBase::Internal error, take_data called, before is_ready was called" );
260+ }
261+ };
262+
263+ if (pimpl_->goal_request_ready_ .exchange (false )) {
216264 rcl_ret_t ret;
217265 rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info ();
218266 rmw_request_id_t request_header;
@@ -225,13 +273,12 @@ ServerBase::take_data()
225273 &request_header,
226274 message.get ());
227275
276+ checkCallSequence ();
228277 return std::static_pointer_cast<void >(
229- std::make_shared
230- <std::tuple<rcl_ret_t , rcl_action_goal_info_t , rmw_request_id_t , std::shared_ptr<void >>>(
231- ret,
232- goal_info,
233- request_header, message));
234- } else if (pimpl_->cancel_request_ready_ .load ()) {
278+ std::make_shared<ServerBaseData>(
279+ ServerBaseData::GoalRequestData (
280+ ret, goal_info, request_header, message)));
281+ } else if (pimpl_->cancel_request_ready_ .exchange (false )) {
235282 rcl_ret_t ret;
236283 rmw_request_id_t request_header;
237284
@@ -244,11 +291,13 @@ ServerBase::take_data()
244291 &request_header,
245292 request.get ());
246293
294+ checkCallSequence ();
247295 return std::static_pointer_cast<void >(
248- std::make_shared
249- <std::tuple<rcl_ret_t , std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
250- rmw_request_id_t >>(ret, request, request_header));
251- } else if (pimpl_->result_request_ready_ .load ()) {
296+ std::make_shared<ServerBaseData>(
297+ ServerBaseData::CancelRequestData (
298+ ret, request,
299+ request_header)));
300+ } else if (pimpl_->result_request_ready_ .exchange (false )) {
252301 rcl_ret_t ret;
253302 // Get the result request message
254303 rmw_request_id_t request_header;
@@ -257,11 +306,15 @@ ServerBase::take_data()
257306 ret = rcl_action_take_result_request (
258307 pimpl_->action_server_ .get (), &request_header, result_request.get ());
259308
309+ checkCallSequence ();
260310 return std::static_pointer_cast<void >(
261- std::make_shared<std::tuple<rcl_ret_t , std::shared_ptr<void >, rmw_request_id_t >>(
262- ret, result_request, request_header));
311+ std::make_shared<ServerBaseData>(
312+ ServerBaseData::ResultRequestData (
313+ ret, result_request, request_header)));
263314 } else if (pimpl_->goal_expired_ .load ()) {
264- return nullptr ;
315+ checkCallSequence ();
316+ return std::static_pointer_cast<void >(
317+ std::make_shared<ServerBaseData>(ServerBaseData::GoalExpiredData ()));
265318 } else {
266319 throw std::runtime_error (" Taking data from action server but nothing is ready" );
267320 }
@@ -270,6 +323,11 @@ ServerBase::take_data()
270323std::shared_ptr<void >
271324ServerBase::take_data_by_entity_id (size_t id)
272325{
326+ if (pimpl_->threadExlusive .exchange (true )) {
327+ throw std::runtime_error (
328+ " ServerBase::Internal error, is_ready called, before take_data was called" );
329+ }
330+
273331 // Mark as ready the entity from which we want to take data
274332 switch (static_cast <EntityType>(id)) {
275333 case EntityType::GoalService:
@@ -287,31 +345,36 @@ ServerBase::take_data_by_entity_id(size_t id)
287345}
288346
289347void
290- ServerBase::execute (std::shared_ptr<void > & data )
348+ ServerBase::execute (std::shared_ptr<void > & dataIn )
291349{
292- if (!data && !pimpl_->goal_expired_ .load ()) {
293- throw std::runtime_error (" 'data' is empty" );
294- }
295-
296- if (pimpl_->goal_request_ready_ .load ()) {
297- execute_goal_request_received (data);
298- } else if (pimpl_->cancel_request_ready_ .load ()) {
299- execute_cancel_request_received (data);
300- } else if (pimpl_->result_request_ready_ .load ()) {
301- execute_result_request_received (data);
302- } else if (pimpl_->goal_expired_ .load ()) {
303- execute_check_expired_goals ();
304- } else {
305- throw std::runtime_error (" Executing action server but nothing is ready" );
306- }
350+ std::shared_ptr<ServerBaseData> dataPtr = std::static_pointer_cast<ServerBaseData>(dataIn);
351+
352+ std::visit (
353+ [&](auto && data) -> void {
354+ using T = std::decay_t <decltype (data)>;
355+ if constexpr (std::is_same_v<T, ServerBaseData::GoalRequestData>) {
356+ execute_goal_request_received (
357+ std::get<0 >(data), std::get<1 >(data), std::get<2 >(data),
358+ std::get<3 >(data));
359+ }
360+ if constexpr (std::is_same_v<T, ServerBaseData::CancelRequestData>) {
361+ execute_cancel_request_received (std::get<0 >(data), std::get<1 >(data), std::get<2 >(data));
362+ }
363+ if constexpr (std::is_same_v<T, ServerBaseData::ResultRequestData>) {
364+ execute_result_request_received (std::get<0 >(data), std::get<1 >(data), std::get<2 >(data));
365+ }
366+ if constexpr (std::is_same_v<T, ServerBaseData::GoalExpiredData>) {
367+ execute_check_expired_goals ();
368+ }
369+ },
370+ dataPtr->data );
307371}
308372
309373void
310- ServerBase::execute_goal_request_received (std::shared_ptr<void > & data)
374+ ServerBase::execute_goal_request_received (
375+ rcl_ret_t ret, rcl_action_goal_info_t goal_info, rmw_request_id_t request_header,
376+ const std::shared_ptr<void > message)
311377{
312- auto shared_ptr = std::static_pointer_cast
313- <std::tuple<rcl_ret_t , rcl_action_goal_info_t , rmw_request_id_t , std::shared_ptr<void >>>(data);
314- rcl_ret_t ret = std::get<0 >(*shared_ptr);
315378 if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
316379 // Ignore take failure because connext fails if it receives a sample without valid data.
317380 // This happens when a client shuts down and connext receives a sample saying the client is
@@ -320,14 +383,6 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
320383 } else if (RCL_RET_OK != ret) {
321384 rclcpp::exceptions::throw_from_rcl_error (ret);
322385 }
323- rcl_action_goal_info_t goal_info = std::get<1 >(*shared_ptr);
324- rmw_request_id_t request_header = std::get<2 >(*shared_ptr);
325- std::shared_ptr<void > message = std::get<3 >(*shared_ptr);
326-
327- bool expected = true ;
328- if (!pimpl_->goal_request_ready_ .compare_exchange_strong (expected, false )) {
329- return ;
330- }
331386
332387 GoalUUID uuid = get_goal_id_from_goal_request (message.get ());
333388 convert (uuid, &goal_info);
@@ -396,16 +451,13 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
396451 // Tell user to start executing action
397452 call_goal_accepted_callback (handle, uuid, message);
398453 }
399- data.reset ();
400454}
401455
402456void
403- ServerBase::execute_cancel_request_received (std::shared_ptr<void > & data)
457+ ServerBase::execute_cancel_request_received (
458+ rcl_ret_t ret, std::shared_ptr<action_msgs::srv::CancelGoal::Request> request,
459+ rmw_request_id_t request_header)
404460{
405- auto shared_ptr = std::static_pointer_cast
406- <std::tuple<rcl_ret_t , std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
407- rmw_request_id_t >>(data);
408- auto ret = std::get<0 >(*shared_ptr);
409461 if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
410462 // Ignore take failure because connext fails if it receives a sample without valid data.
411463 // This happens when a client shuts down and connext receives a sample saying the client is
@@ -414,9 +466,6 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
414466 } else if (RCL_RET_OK != ret) {
415467 rclcpp::exceptions::throw_from_rcl_error (ret);
416468 }
417- auto request = std::get<1 >(*shared_ptr);
418- auto request_header = std::get<2 >(*shared_ptr);
419- pimpl_->cancel_request_ready_ = false ;
420469
421470 // Convert c++ message to C message
422471 rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request ();
@@ -486,15 +535,13 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
486535 if (RCL_RET_OK != ret) {
487536 rclcpp::exceptions::throw_from_rcl_error (ret);
488537 }
489- data.reset ();
490538}
491539
492540void
493- ServerBase::execute_result_request_received (std::shared_ptr<void > & data)
541+ ServerBase::execute_result_request_received (
542+ rcl_ret_t ret, std::shared_ptr<void > result_request,
543+ rmw_request_id_t request_header)
494544{
495- auto shared_ptr = std::static_pointer_cast
496- <std::tuple<rcl_ret_t , std::shared_ptr<void >, rmw_request_id_t >>(data);
497- auto ret = std::get<0 >(*shared_ptr);
498545 if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
499546 // Ignore take failure because connext fails if it receives a sample without valid data.
500547 // This happens when a client shuts down and connext receives a sample saying the client is
@@ -503,10 +550,7 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
503550 } else if (RCL_RET_OK != ret) {
504551 rclcpp::exceptions::throw_from_rcl_error (ret);
505552 }
506- auto result_request = std::get<1 >(*shared_ptr);
507- auto request_header = std::get<2 >(*shared_ptr);
508553
509- pimpl_->result_request_ready_ = false ;
510554 std::shared_ptr<void > result_response;
511555
512556 // check if the goal exists
@@ -542,7 +586,6 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
542586 rclcpp::exceptions::throw_from_rcl_error (rcl_ret);
543587 }
544588 }
545- data.reset ();
546589}
547590
548591void
0 commit comments