1414
1515#include < functional>
1616#include < memory>
17+ #include < mutex>
1718#include < stdexcept>
1819#include < string>
1920#include < utility>
@@ -59,7 +60,11 @@ LifecycleNode::LifecycleNodeInterfaceImpl::LifecycleNodeInterfaceImpl(
5960LifecycleNode::LifecycleNodeInterfaceImpl::~LifecycleNodeInterfaceImpl ()
6061{
6162 rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle ();
62- auto ret = rcl_lifecycle_state_machine_fini (&state_machine_, node_handle);
63+ rcl_ret_t ret;
64+ {
65+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
66+ ret = rcl_lifecycle_state_machine_fini (&state_machine_, node_handle);
67+ }
6368 if (ret != RCL_RET_OK) {
6469 RCUTILS_LOG_FATAL_NAMED (
6570 " rclcpp_lifecycle" ,
@@ -73,7 +78,6 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
7378 rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle ();
7479 const rcl_node_options_t * node_options =
7580 rcl_node_get_options (node_base_interface_->get_rcl_node_handle ());
76- state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine ();
7781 auto state_machine_options = rcl_lifecycle_get_default_state_machine_options ();
7882 state_machine_options.enable_com_interface = enable_communication_interface;
7983 state_machine_options.allocator = node_options->allocator ;
@@ -92,6 +96,8 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
9296 // The publisher takes a C-Typesupport since the publishing (i.e. creating
9397 // the message) is done fully in RCL.
9498 // Services are handled in C++, so that it needs a C++ typesupport structure.
99+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
100+ state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine ();
95101 rcl_ret_t ret = rcl_lifecycle_state_machine_init (
96102 &state_machine_,
97103 node_handle,
@@ -108,6 +114,8 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
108114 node_base_interface_->get_name ());
109115 }
110116
117+ current_state_ = State (state_machine_.current_state );
118+
111119 if (enable_communication_interface) {
112120 { // change_state
113121 auto cb = std::bind (
@@ -196,8 +204,6 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
196204 nullptr );
197205 }
198206 }
199-
200- current_state_ = State (state_machine_.current_state );
201207}
202208
203209bool
@@ -216,28 +222,31 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_change_state(
216222 std::shared_ptr<ChangeStateSrv::Response> resp)
217223{
218224 (void )header;
219- if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
220- throw std::runtime_error (
221- " Can't get state. State machine is not initialized." );
222- }
225+ std::uint8_t transition_id;
226+ {
227+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
228+ if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
229+ throw std::runtime_error (" Can't get state. State machine is not initialized." );
230+ }
223231
224- auto transition_id = req->transition .id ;
225- // if there's a label attached to the request,
226- // we check the transition attached to this label.
227- // we further can't compare the id of the looked up transition
228- // because ros2 service call defaults all intergers to zero.
229- // that means if we call ros2 service call ... {transition: {label: shutdown}}
230- // the id of the request is 0 (zero) whereas the id from the lookup up transition
231- // can be different.
232- // the result of this is that the label takes presedence of the id.
233- if (req->transition .label .size () != 0 ) {
234- auto rcl_transition = rcl_lifecycle_get_transition_by_label (
235- state_machine_.current_state , req->transition .label .c_str ());
236- if (rcl_transition == nullptr ) {
237- resp->success = false ;
238- return ;
232+ transition_id = req->transition .id ;
233+ // if there's a label attached to the request,
234+ // we check the transition attached to this label.
235+ // we further can't compare the id of the looked up transition
236+ // because ros2 service call defaults all intergers to zero.
237+ // that means if we call ros2 service call ... {transition: {label: shutdown}}
238+ // the id of the request is 0 (zero) whereas the id from the lookup up transition
239+ // can be different.
240+ // the result of this is that the label takes presedence of the id.
241+ if (req->transition .label .size () != 0 ) {
242+ auto rcl_transition = rcl_lifecycle_get_transition_by_label (
243+ state_machine_.current_state , req->transition .label .c_str ());
244+ if (rcl_transition == nullptr ) {
245+ resp->success = false ;
246+ return ;
247+ }
248+ transition_id = static_cast <std::uint8_t >(rcl_transition->id );
239249 }
240- transition_id = static_cast <std::uint8_t >(rcl_transition->id );
241250 }
242251
243252 node_interfaces::LifecycleNodeInterface::CallbackReturn cb_return_code;
@@ -258,6 +267,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_state(
258267{
259268 (void )header;
260269 (void )req;
270+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
261271 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
262272 throw std::runtime_error (
263273 " Can't get state. State machine is not initialized." );
@@ -274,6 +284,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_available_states(
274284{
275285 (void )header;
276286 (void )req;
287+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
277288 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
278289 throw std::runtime_error (
279290 " Can't get available states. State machine is not initialized." );
@@ -296,6 +307,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_available_transitions(
296307{
297308 (void )header;
298309 (void )req;
310+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
299311 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
300312 throw std::runtime_error (
301313 " Can't get available transitions. State machine is not initialized." );
@@ -323,6 +335,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_transition_graph(
323335{
324336 (void )header;
325337 (void )req;
338+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
326339 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
327340 throw std::runtime_error (
328341 " Can't get available transitions. State machine is not initialized." );
@@ -352,6 +365,7 @@ std::vector<State>
352365LifecycleNode::LifecycleNodeInterfaceImpl::get_available_states () const
353366{
354367 std::vector<State> states;
368+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
355369 states.reserve (state_machine_.transition_map .states_size );
356370
357371 for (unsigned int i = 0 ; i < state_machine_.transition_map .states_size ; ++i) {
@@ -364,6 +378,7 @@ std::vector<Transition>
364378LifecycleNode::LifecycleNodeInterfaceImpl::get_available_transitions () const
365379{
366380 std::vector<Transition> transitions;
381+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
367382 transitions.reserve (state_machine_.current_state ->valid_transition_size );
368383
369384 for (unsigned int i = 0 ; i < state_machine_.current_state ->valid_transition_size ; ++i) {
@@ -376,6 +391,7 @@ std::vector<Transition>
376391LifecycleNode::LifecycleNodeInterfaceImpl::get_transition_graph () const
377392{
378393 std::vector<Transition> transitions;
394+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
379395 transitions.reserve (state_machine_.transition_map .transitions_size );
380396
381397 for (unsigned int i = 0 ; i < state_machine_.transition_map .transitions_size ; ++i) {
@@ -389,26 +405,33 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
389405 std::uint8_t transition_id,
390406 node_interfaces::LifecycleNodeInterface::CallbackReturn & cb_return_code)
391407{
392- if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
393- RCUTILS_LOG_ERROR (
394- " Unable to change state for state machine for %s: %s" ,
395- node_base_interface_->get_name (), rcl_get_error_string ().str );
396- return RCL_RET_ERROR;
397- }
398-
399408 constexpr bool publish_update = true ;
400- // keep the initial state to pass to a transition callback
401- State initial_state (state_machine_. current_state ) ;
409+ State initial_state;
410+ unsigned int current_state_id ;
402411
403- if (
404- rcl_lifecycle_trigger_transition_by_id (
405- &state_machine_, transition_id, publish_update) != RCL_RET_OK)
406412 {
407- RCUTILS_LOG_ERROR (
408- " Unable to start transition %u from current state %s: %s" ,
409- transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
410- rcutils_reset_error ();
411- return RCL_RET_ERROR;
413+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
414+ if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
415+ RCUTILS_LOG_ERROR (
416+ " Unable to change state for state machine for %s: %s" ,
417+ node_base_interface_->get_name (), rcl_get_error_string ().str );
418+ return RCL_RET_ERROR;
419+ }
420+
421+ // keep the initial state to pass to a transition callback
422+ initial_state = State (state_machine_.current_state );
423+
424+ if (
425+ rcl_lifecycle_trigger_transition_by_id (
426+ &state_machine_, transition_id, publish_update) != RCL_RET_OK)
427+ {
428+ RCUTILS_LOG_ERROR (
429+ " Unable to start transition %u from current state %s: %s" ,
430+ transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
431+ rcutils_reset_error ();
432+ return RCL_RET_ERROR;
433+ }
434+ current_state_id = state_machine_.current_state ->id ;
412435 }
413436
414437 // Update the internal current_state_
@@ -425,18 +448,22 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
425448 return rcl_lifecycle_transition_error_label;
426449 };
427450
428- cb_return_code = execute_callback (state_machine_. current_state -> id , initial_state);
451+ cb_return_code = execute_callback (current_state_id , initial_state);
429452 auto transition_label = get_label_for_return_code (cb_return_code);
430453
431- if (
432- rcl_lifecycle_trigger_transition_by_label (
433- &state_machine_, transition_label, publish_update) != RCL_RET_OK)
434454 {
435- RCUTILS_LOG_ERROR (
436- " Failed to finish transition %u. Current state is now: %s (%s)" ,
437- transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
438- rcutils_reset_error ();
439- return RCL_RET_ERROR;
455+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
456+ if (
457+ rcl_lifecycle_trigger_transition_by_label (
458+ &state_machine_, transition_label, publish_update) != RCL_RET_OK)
459+ {
460+ RCUTILS_LOG_ERROR (
461+ " Failed to finish transition %u. Current state is now: %s (%s)" ,
462+ transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
463+ rcutils_reset_error ();
464+ return RCL_RET_ERROR;
465+ }
466+ current_state_id = state_machine_.current_state ->id ;
440467 }
441468
442469 // Update the internal current_state_
@@ -447,8 +474,9 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
447474 if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) {
448475 RCUTILS_LOG_WARN (" Error occurred while doing error handling." );
449476
450- auto error_cb_code = execute_callback (state_machine_. current_state -> id , initial_state);
477+ auto error_cb_code = execute_callback (current_state_id , initial_state);
451478 auto error_cb_label = get_label_for_return_code (error_cb_code);
479+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
452480 if (
453481 rcl_lifecycle_trigger_transition_by_label (
454482 &state_machine_, error_cb_label, publish_update) != RCL_RET_OK)
@@ -500,8 +528,13 @@ const State & LifecycleNode::LifecycleNodeInterfaceImpl::trigger_transition(
500528 const char * transition_label,
501529 node_interfaces::LifecycleNodeInterface::CallbackReturn & cb_return_code)
502530{
503- auto transition =
504- rcl_lifecycle_get_transition_by_label (state_machine_.current_state , transition_label);
531+ const rcl_lifecycle_transition_t * transition;
532+ {
533+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
534+
535+ transition =
536+ rcl_lifecycle_get_transition_by_label (state_machine_.current_state , transition_label);
537+ }
505538 if (transition) {
506539 change_state (static_cast <uint8_t >(transition->id ), cb_return_code);
507540 }
0 commit comments