Skip to content

Commit a978e78

Browse files
authored
Trigger shutdown transition in destructor (backport #1979) (#2142)
1 parent d801582 commit a978e78

File tree

5 files changed

+52
-4
lines changed

5 files changed

+52
-4
lines changed

controller_interface/include/controller_interface/controller_interface_base.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class ControllerInterfaceBase : public rclcpp_lifecycle::node_interfaces::Lifecy
7070
ControllerInterfaceBase() = default;
7171

7272
CONTROLLER_INTERFACE_PUBLIC
73-
virtual ~ControllerInterfaceBase() = default;
73+
virtual ~ControllerInterfaceBase();
7474

7575
/// Get configuration for controller's required command interfaces.
7676
/**

controller_interface/src/controller_interface_base.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@
2424

2525
namespace controller_interface
2626
{
27+
ControllerInterfaceBase::~ControllerInterfaceBase()
28+
{
29+
// check if node is initialized and we still have a valid context
30+
if (
31+
node_.get() && get_state().id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED &&
32+
rclcpp::ok())
33+
{
34+
RCLCPP_DEBUG(
35+
get_node()->get_logger(),
36+
"Calling shutdown transition of controller node '%s' due to destruction.",
37+
get_node()->get_name());
38+
node_->shutdown();
39+
}
40+
}
41+
2742
return_type ControllerInterfaceBase::init(
2843
const std::string & controller_name, const std::string & namespace_,
2944
const rclcpp::NodeOptions & node_options)
@@ -47,6 +62,11 @@ return_type ControllerInterfaceBase::init(
4762
break;
4863
case LifecycleNodeInterface::CallbackReturn::ERROR:
4964
case LifecycleNodeInterface::CallbackReturn::FAILURE:
65+
RCLCPP_DEBUG(
66+
get_node()->get_logger(),
67+
"Calling shutdown transition of controller node '%s' due to init failure.",
68+
get_node()->get_name());
69+
node_->shutdown();
5070
return return_type::ERROR;
5171
}
5272

@@ -105,6 +125,10 @@ void ControllerInterfaceBase::release_interfaces()
105125

106126
const rclcpp_lifecycle::State & ControllerInterfaceBase::get_state() const
107127
{
128+
if (!node_.get())
129+
{
130+
throw std::runtime_error("Lifecycle node hasn't been initialized yet!");
131+
}
108132
return node_->get_current_state();
109133
}
110134

controller_interface/test/test_controller_interface.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <gmock/gmock.h>
1818
#include <memory>
1919

20+
#include "lifecycle_msgs/msg/state.hpp"
2021
#include "rclcpp/executor_options.hpp"
2122
#include "rclcpp/executors/multi_threaded_executor.hpp"
2223

@@ -33,13 +34,18 @@ TEST(TestableControllerInterface, init)
3334
rclcpp::init(argc, argv);
3435

3536
TestableControllerInterface controller;
37+
const TestableControllerInterface & const_controller = controller;
3638

3739
// try to get node when not initialized
3840
ASSERT_THROW(controller.get_node(), std::runtime_error);
41+
ASSERT_THROW(const_controller.get_node(), std::runtime_error);
42+
ASSERT_THROW(controller.get_state(), std::runtime_error);
3943

4044
// initialize, create node
4145
ASSERT_EQ(controller.init(TEST_CONTROLLER_NAME), controller_interface::return_type::OK);
4246
ASSERT_NO_THROW(controller.get_node());
47+
ASSERT_NO_THROW(const_controller.get_node());
48+
ASSERT_NO_THROW(controller.get_state());
4349

4450
// update_rate is set to default 0
4551
ASSERT_EQ(controller.get_update_rate(), 0u);
@@ -48,6 +54,7 @@ TEST(TestableControllerInterface, init)
4854
controller.configure();
4955
ASSERT_EQ(controller.get_update_rate(), 0u);
5056

57+
controller.get_node()->shutdown();
5158
rclcpp::shutdown();
5259
}
5360

@@ -111,6 +118,7 @@ TEST(TestableControllerInterface, setting_update_rate_in_configure)
111118
ASSERT_EQ(controller.get_update_rate(), 623u);
112119

113120
executor->cancel();
121+
controller.get_node()->shutdown();
114122
rclcpp::shutdown();
115123
}
116124

@@ -125,6 +133,7 @@ TEST(TestableControllerInterfaceInitError, init_with_error)
125133
// initialize, create node
126134
ASSERT_EQ(controller.init(TEST_CONTROLLER_NAME), controller_interface::return_type::ERROR);
127135

136+
ASSERT_EQ(controller.get_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
128137
rclcpp::shutdown();
129138
}
130139

@@ -139,6 +148,7 @@ TEST(TestableControllerInterfaceInitFailure, init_with_failure)
139148
// initialize, create node
140149
ASSERT_EQ(controller.init(TEST_CONTROLLER_NAME), controller_interface::return_type::ERROR);
141150

151+
ASSERT_EQ(controller.get_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED);
142152
rclcpp::shutdown();
143153
}
144154

controller_interface/test/test_controller_with_options.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ TEST(ControllerWithOption, init_with_overrides)
4242
rclcpp::init(argc, argv);
4343
// creates the controller
4444
FriendControllerWithOptions controller;
45-
EXPECT_EQ(controller.init("controller_name"), controller_interface::return_type::OK);
45+
ASSERT_EQ(controller.init("controller_name"), controller_interface::return_type::OK);
4646
// checks that the node options have been updated
4747
const auto & node_options = controller.get_node()->get_node_options();
4848
EXPECT_TRUE(node_options.allow_undeclared_parameters());
@@ -52,6 +52,8 @@ TEST(ControllerWithOption, init_with_overrides)
5252
EXPECT_EQ(controller.params["parameter1"], 1.);
5353
EXPECT_EQ(controller.params["parameter2"], 2.);
5454
EXPECT_EQ(controller.params["parameter3"], 3.);
55+
56+
controller.get_node()->shutdown();
5557
rclcpp::shutdown();
5658
}
5759

@@ -63,12 +65,13 @@ TEST(ControllerWithOption, init_without_overrides)
6365
rclcpp::init(argc, argv);
6466
// creates the controller
6567
FriendControllerWithOptions controller;
66-
EXPECT_EQ(controller.init("controller_name"), controller_interface::return_type::ERROR);
68+
ASSERT_EQ(controller.init("controller_name"), controller_interface::return_type::ERROR);
6769
// checks that the node options have been updated
6870
const auto & node_options = controller.get_node()->get_node_options();
6971
EXPECT_TRUE(node_options.allow_undeclared_parameters());
7072
EXPECT_TRUE(node_options.automatically_declare_parameters_from_overrides());
7173
// checks that no parameter has been declared from overrides
7274
EXPECT_EQ(controller.params.size(), 0u);
75+
7376
rclcpp::shutdown();
7477
}

joint_limits/test/joint_limits_rosparam_test.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "gtest/gtest.h"
2020

2121
#include "joint_limits/joint_limits_rosparam.hpp"
22+
#include "lifecycle_msgs/msg/state.hpp"
2223
#include "rclcpp/rclcpp.hpp"
2324
#include "rclcpp_lifecycle/lifecycle_node.hpp"
2425

@@ -283,7 +284,17 @@ class JointLimitsLifecycleNodeUndeclaredRosParamTest : public ::testing::Test
283284
lifecycle_node_ = rclcpp_lifecycle::LifecycleNode::make_shared("JointLimitsRosparamTestNode");
284285
}
285286

286-
void TearDown() { lifecycle_node_.reset(); }
287+
void TearDown()
288+
{
289+
if (
290+
lifecycle_node_->get_current_state().id() !=
291+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED &&
292+
rclcpp::ok())
293+
{
294+
lifecycle_node_->shutdown();
295+
}
296+
lifecycle_node_.reset();
297+
}
287298

288299
protected:
289300
rclcpp_lifecycle::LifecycleNode::SharedPtr lifecycle_node_;

0 commit comments

Comments
 (0)