Skip to content

Commit 1831e12

Browse files
fujitatomoyaclalancette
authored andcommitted
Bugfix 20210810 get current state (ros2#1756)
* protect state_machine_ with mutex lock. protect state_handle_ with mutex lock. reconsider mutex lock scope. remove mutex lock from constructors. lock just once during initialization of LifecycleNodeInterfaceImpl. Signed-off-by: Tomoya Fujita <[email protected]> * Move updating of current_state to right after initialization. This is slightly more correct in the case that registering one of the services fails. Signed-off-by: Chris Lalancette <[email protected]> Signed-off-by: Tomoya Fujita <[email protected]> Signed-off-by: Chris Lalancette <[email protected]> Co-authored-by: Chris Lalancette <[email protected]>
1 parent 642faa2 commit 1831e12

File tree

4 files changed

+95
-53
lines changed

4 files changed

+95
-53
lines changed

rclcpp_lifecycle/include/rclcpp_lifecycle/state.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef RCLCPP_LIFECYCLE__STATE_HPP_
1616
#define RCLCPP_LIFECYCLE__STATE_HPP_
1717

18+
#include <mutex>
1819
#include <string>
1920

2021
#include "rcl_lifecycle/data_types.h"
@@ -91,6 +92,7 @@ class State
9192

9293
bool owns_rcl_state_handle_;
9394

95+
mutable std::recursive_mutex state_handle_mutex_;
9496
rcl_lifecycle_state_t * state_handle_;
9597
};
9698

rclcpp_lifecycle/src/lifecycle_node_interface_impl.cpp

Lines changed: 86 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
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(
5960
LifecycleNode::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

203209
bool
@@ -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>
352365
LifecycleNode::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>
364378
LifecycleNode::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>
376391
LifecycleNode::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
}

rclcpp_lifecycle/src/lifecycle_node_interface_impl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class LifecycleNode::LifecycleNodeInterfaceImpl final
143143
node_interfaces::LifecycleNodeInterface::CallbackReturn
144144
execute_callback(unsigned int cb_id, const State & previous_state) const;
145145

146+
mutable std::recursive_mutex state_machine_mutex_;
146147
rcl_lifecycle_state_machine_t state_machine_;
147148
State current_state_;
148149
std::map<

rclcpp_lifecycle/src/state.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ State::State(
7070
if (!rcl_lifecycle_state_handle) {
7171
throw std::runtime_error("rcl_lifecycle_state_handle is null");
7272
}
73+
7374
state_handle_ = const_cast<rcl_lifecycle_state_t *>(rcl_lifecycle_state_handle);
7475
}
7576

@@ -93,6 +94,8 @@ State::operator=(const State & rhs)
9394
return *this;
9495
}
9596

97+
// hold the lock until state_handle_ is reconstructed
98+
std::lock_guard<std::recursive_mutex> lock(state_handle_mutex_);
9699
// reset all currently used resources
97100
reset();
98101

@@ -128,6 +131,7 @@ State::operator=(const State & rhs)
128131
uint8_t
129132
State::id() const
130133
{
134+
std::lock_guard<std::recursive_mutex> lock(state_handle_mutex_);
131135
if (!state_handle_) {
132136
throw std::runtime_error("Error in state! Internal state_handle is NULL.");
133137
}
@@ -137,6 +141,7 @@ State::id() const
137141
std::string
138142
State::label() const
139143
{
144+
std::lock_guard<std::recursive_mutex> lock(state_handle_mutex_);
140145
if (!state_handle_) {
141146
throw std::runtime_error("Error in state! Internal state_handle is NULL.");
142147
}
@@ -146,6 +151,7 @@ State::label() const
146151
void
147152
State::reset() noexcept
148153
{
154+
std::lock_guard<std::recursive_mutex> lock(state_handle_mutex_);
149155
if (!owns_rcl_state_handle_) {
150156
state_handle_ = nullptr;
151157
}

0 commit comments

Comments
 (0)