1414
1515#include < functional>
1616#include < memory>
17+ #include < mutex>
1718#include < stdexcept>
1819#include < string>
1920#include < utility>
@@ -58,7 +59,11 @@ LifecycleNode::LifecycleNodeInterfaceImpl::LifecycleNodeInterfaceImpl(
5859LifecycleNode::LifecycleNodeInterfaceImpl::~LifecycleNodeInterfaceImpl ()
5960{
6061 rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle ();
61- auto ret = rcl_lifecycle_state_machine_fini (&state_machine_, node_handle);
62+ rcl_ret_t ret;
63+ {
64+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
65+ ret = rcl_lifecycle_state_machine_fini (&state_machine_, node_handle);
66+ }
6267 if (ret != RCL_RET_OK) {
6368 RCUTILS_LOG_FATAL_NAMED (
6469 " rclcpp_lifecycle" ,
@@ -72,7 +77,6 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
7277 rcl_node_t * node_handle = node_base_interface_->get_rcl_node_handle ();
7378 const rcl_node_options_t * node_options =
7479 rcl_node_get_options (node_base_interface_->get_rcl_node_handle ());
75- state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine ();
7680 auto state_machine_options = rcl_lifecycle_get_default_state_machine_options ();
7781 state_machine_options.enable_com_interface = enable_communication_interface;
7882 state_machine_options.allocator = node_options->allocator ;
@@ -83,6 +87,8 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
8387 // The publisher takes a C-Typesupport since the publishing (i.e. creating
8488 // the message) is done fully in RCL.
8589 // Services are handled in C++, so that it needs a C++ typesupport structure.
90+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
91+ state_machine_ = rcl_lifecycle_get_zero_initialized_state_machine ();
8692 rcl_ret_t ret = rcl_lifecycle_state_machine_init (
8793 &state_machine_,
8894 node_handle,
@@ -99,6 +105,8 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
99105 node_base_interface_->get_name ());
100106 }
101107
108+ current_state_ = State (state_machine_.current_state );
109+
102110 if (enable_communication_interface) {
103111 { // change_state
104112 auto cb = std::bind (
@@ -182,8 +190,6 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
182190 nullptr );
183191 }
184192 }
185-
186- current_state_ = State (state_machine_.current_state );
187193}
188194
189195bool
@@ -202,28 +208,31 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_change_state(
202208 std::shared_ptr<ChangeStateSrv::Response> resp)
203209{
204210 (void )header;
205- if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
206- throw std::runtime_error (
207- " Can't get state. State machine is not initialized." );
208- }
211+ std::uint8_t transition_id;
212+ {
213+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
214+ if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
215+ throw std::runtime_error (" Can't get state. State machine is not initialized." );
216+ }
209217
210- auto transition_id = req->transition .id ;
211- // if there's a label attached to the request,
212- // we check the transition attached to this label.
213- // we further can't compare the id of the looked up transition
214- // because ros2 service call defaults all intergers to zero.
215- // that means if we call ros2 service call ... {transition: {label: shutdown}}
216- // the id of the request is 0 (zero) whereas the id from the lookup up transition
217- // can be different.
218- // the result of this is that the label takes presedence of the id.
219- if (req->transition .label .size () != 0 ) {
220- auto rcl_transition = rcl_lifecycle_get_transition_by_label (
221- state_machine_.current_state , req->transition .label .c_str ());
222- if (rcl_transition == nullptr ) {
223- resp->success = false ;
224- return ;
218+ transition_id = req->transition .id ;
219+ // if there's a label attached to the request,
220+ // we check the transition attached to this label.
221+ // we further can't compare the id of the looked up transition
222+ // because ros2 service call defaults all intergers to zero.
223+ // that means if we call ros2 service call ... {transition: {label: shutdown}}
224+ // the id of the request is 0 (zero) whereas the id from the lookup up transition
225+ // can be different.
226+ // the result of this is that the label takes presedence of the id.
227+ if (req->transition .label .size () != 0 ) {
228+ auto rcl_transition = rcl_lifecycle_get_transition_by_label (
229+ state_machine_.current_state , req->transition .label .c_str ());
230+ if (rcl_transition == nullptr ) {
231+ resp->success = false ;
232+ return ;
233+ }
234+ transition_id = static_cast <std::uint8_t >(rcl_transition->id );
225235 }
226- transition_id = static_cast <std::uint8_t >(rcl_transition->id );
227236 }
228237
229238 node_interfaces::LifecycleNodeInterface::CallbackReturn cb_return_code;
@@ -244,6 +253,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_state(
244253{
245254 (void )header;
246255 (void )req;
256+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
247257 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
248258 throw std::runtime_error (
249259 " Can't get state. State machine is not initialized." );
@@ -260,6 +270,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_available_states(
260270{
261271 (void )header;
262272 (void )req;
273+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
263274 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
264275 throw std::runtime_error (
265276 " Can't get available states. State machine is not initialized." );
@@ -282,6 +293,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_available_transitions(
282293{
283294 (void )header;
284295 (void )req;
296+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
285297 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
286298 throw std::runtime_error (
287299 " Can't get available transitions. State machine is not initialized." );
@@ -309,6 +321,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::on_get_transition_graph(
309321{
310322 (void )header;
311323 (void )req;
324+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
312325 if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
313326 throw std::runtime_error (
314327 " Can't get available transitions. State machine is not initialized." );
@@ -338,6 +351,7 @@ std::vector<State>
338351LifecycleNode::LifecycleNodeInterfaceImpl::get_available_states () const
339352{
340353 std::vector<State> states;
354+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
341355 states.reserve (state_machine_.transition_map .states_size );
342356
343357 for (unsigned int i = 0 ; i < state_machine_.transition_map .states_size ; ++i) {
@@ -350,6 +364,7 @@ std::vector<Transition>
350364LifecycleNode::LifecycleNodeInterfaceImpl::get_available_transitions () const
351365{
352366 std::vector<Transition> transitions;
367+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
353368 transitions.reserve (state_machine_.current_state ->valid_transition_size );
354369
355370 for (unsigned int i = 0 ; i < state_machine_.current_state ->valid_transition_size ; ++i) {
@@ -362,6 +377,7 @@ std::vector<Transition>
362377LifecycleNode::LifecycleNodeInterfaceImpl::get_transition_graph () const
363378{
364379 std::vector<Transition> transitions;
380+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
365381 transitions.reserve (state_machine_.transition_map .transitions_size );
366382
367383 for (unsigned int i = 0 ; i < state_machine_.transition_map .transitions_size ; ++i) {
@@ -375,26 +391,33 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
375391 std::uint8_t transition_id,
376392 node_interfaces::LifecycleNodeInterface::CallbackReturn & cb_return_code)
377393{
378- if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
379- RCUTILS_LOG_ERROR (
380- " Unable to change state for state machine for %s: %s" ,
381- node_base_interface_->get_name (), rcl_get_error_string ().str );
382- return RCL_RET_ERROR;
383- }
384-
385394 constexpr bool publish_update = true ;
386- // keep the initial state to pass to a transition callback
387- State initial_state (state_machine_. current_state ) ;
395+ State initial_state;
396+ unsigned int current_state_id ;
388397
389- if (
390- rcl_lifecycle_trigger_transition_by_id (
391- &state_machine_, transition_id, publish_update) != RCL_RET_OK)
392398 {
393- RCUTILS_LOG_ERROR (
394- " Unable to start transition %u from current state %s: %s" ,
395- transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
396- rcutils_reset_error ();
397- return RCL_RET_ERROR;
399+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
400+ if (rcl_lifecycle_state_machine_is_initialized (&state_machine_) != RCL_RET_OK) {
401+ RCUTILS_LOG_ERROR (
402+ " Unable to change state for state machine for %s: %s" ,
403+ node_base_interface_->get_name (), rcl_get_error_string ().str );
404+ return RCL_RET_ERROR;
405+ }
406+
407+ // keep the initial state to pass to a transition callback
408+ initial_state = State (state_machine_.current_state );
409+
410+ if (
411+ rcl_lifecycle_trigger_transition_by_id (
412+ &state_machine_, transition_id, publish_update) != RCL_RET_OK)
413+ {
414+ RCUTILS_LOG_ERROR (
415+ " Unable to start transition %u from current state %s: %s" ,
416+ transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
417+ rcutils_reset_error ();
418+ return RCL_RET_ERROR;
419+ }
420+ current_state_id = state_machine_.current_state ->id ;
398421 }
399422
400423 // Update the internal current_state_
@@ -411,18 +434,22 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
411434 return rcl_lifecycle_transition_error_label;
412435 };
413436
414- cb_return_code = execute_callback (state_machine_. current_state -> id , initial_state);
437+ cb_return_code = execute_callback (current_state_id , initial_state);
415438 auto transition_label = get_label_for_return_code (cb_return_code);
416439
417- if (
418- rcl_lifecycle_trigger_transition_by_label (
419- &state_machine_, transition_label, publish_update) != RCL_RET_OK)
420440 {
421- RCUTILS_LOG_ERROR (
422- " Failed to finish transition %u. Current state is now: %s (%s)" ,
423- transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
424- rcutils_reset_error ();
425- return RCL_RET_ERROR;
441+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
442+ if (
443+ rcl_lifecycle_trigger_transition_by_label (
444+ &state_machine_, transition_label, publish_update) != RCL_RET_OK)
445+ {
446+ RCUTILS_LOG_ERROR (
447+ " Failed to finish transition %u. Current state is now: %s (%s)" ,
448+ transition_id, state_machine_.current_state ->label , rcl_get_error_string ().str );
449+ rcutils_reset_error ();
450+ return RCL_RET_ERROR;
451+ }
452+ current_state_id = state_machine_.current_state ->id ;
426453 }
427454
428455 // Update the internal current_state_
@@ -433,8 +460,9 @@ LifecycleNode::LifecycleNodeInterfaceImpl::change_state(
433460 if (cb_return_code == node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR) {
434461 RCUTILS_LOG_WARN (" Error occurred while doing error handling." );
435462
436- auto error_cb_code = execute_callback (state_machine_. current_state -> id , initial_state);
463+ auto error_cb_code = execute_callback (current_state_id , initial_state);
437464 auto error_cb_label = get_label_for_return_code (error_cb_code);
465+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
438466 if (
439467 rcl_lifecycle_trigger_transition_by_label (
440468 &state_machine_, error_cb_label, publish_update) != RCL_RET_OK)
@@ -486,8 +514,13 @@ const State & LifecycleNode::LifecycleNodeInterfaceImpl::trigger_transition(
486514 const char * transition_label,
487515 node_interfaces::LifecycleNodeInterface::CallbackReturn & cb_return_code)
488516{
489- auto transition =
490- rcl_lifecycle_get_transition_by_label (state_machine_.current_state , transition_label);
517+ const rcl_lifecycle_transition_t * transition;
518+ {
519+ std::lock_guard<std::recursive_mutex> lock (state_machine_mutex_);
520+
521+ transition =
522+ rcl_lifecycle_get_transition_by_label (state_machine_.current_state , transition_label);
523+ }
491524 if (transition) {
492525 change_state (static_cast <uint8_t >(transition->id ), cb_return_code);
493526 }
0 commit comments