Skip to content

Commit 2da1417

Browse files
authored
Fix: on_shutdown callback of controllers never get executed (#1995)
1 parent e2e599b commit 2da1417

File tree

8 files changed

+79
-39
lines changed

8 files changed

+79
-39
lines changed

controller_manager/include/controller_manager/controller_manager.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ class ControllerManager : public rclcpp::Node
317317

318318
void initialize_parameters();
319319

320+
/**
321+
* Call shutdown to change the given controller lifecycle node to the finalized state.
322+
*
323+
* \param[in] controller controller to be shutdown.
324+
*/
325+
void shutdown_controller(controller_manager::ControllerSpec & controller) const;
326+
320327
/**
321328
* Clear request lists used when switching controllers. The lists are shared between "callback"
322329
* and "control loop" threads.

controller_manager/src/controller_manager.cpp

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ static const rmw_qos_profile_t qos_services = {
5858
false};
5959
#endif
6060

61+
inline bool is_controller_unconfigured(
62+
const controller_interface::ControllerInterfaceBase & controller)
63+
{
64+
return controller.get_lifecycle_state().id() ==
65+
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED;
66+
}
67+
6168
inline bool is_controller_inactive(const controller_interface::ControllerInterfaceBase & controller)
6269
{
6370
return controller.get_lifecycle_state().id() ==
@@ -695,39 +702,17 @@ controller_interface::return_type ControllerManager::unload_controller(
695702
return controller_interface::return_type::ERROR;
696703
}
697704

698-
RCLCPP_DEBUG(get_logger(), "Cleanup controller");
705+
RCLCPP_DEBUG(get_logger(), "Shutdown controller");
699706
controller_chain_spec_cleanup(controller_chain_spec_, controller_name);
700707
// TODO(destogl): remove reference interface if chainable; i.e., add a separate method for
701708
// cleaning-up controllers?
702-
if (is_controller_inactive(*controller.c))
709+
if (is_controller_inactive(*controller.c) || is_controller_unconfigured(*controller.c))
703710
{
704711
RCLCPP_DEBUG(
705-
get_logger(), "Controller '%s' is cleaned-up before unloading!", controller_name.c_str());
712+
get_logger(), "Controller '%s' is shutdown before unloading!", controller_name.c_str());
706713
// TODO(destogl): remove reference interface if chainable; i.e., add a separate method for
707714
// cleaning-up controllers?
708-
try
709-
{
710-
const auto new_state = controller.c->get_node()->cleanup();
711-
if (new_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED)
712-
{
713-
RCLCPP_WARN(
714-
get_logger(), "Failed to clean-up the controller '%s' before unloading!",
715-
controller_name.c_str());
716-
}
717-
}
718-
catch (const std::exception & e)
719-
{
720-
RCLCPP_ERROR(
721-
get_logger(),
722-
"Caught exception of type : %s while cleaning up the controller '%s' before unloading: %s",
723-
typeid(e).name(), controller_name.c_str(), e.what());
724-
}
725-
catch (...)
726-
{
727-
RCLCPP_ERROR(
728-
get_logger(), "Failed to clean-up the controller '%s' before unloading",
729-
controller_name.c_str());
730-
}
715+
shutdown_controller(controller);
731716
}
732717
executor_->remove_node(controller.c->get_node()->get_node_base_interface());
733718
to.erase(found_it);
@@ -745,6 +730,33 @@ controller_interface::return_type ControllerManager::unload_controller(
745730
return controller_interface::return_type::OK;
746731
}
747732

733+
void ControllerManager::shutdown_controller(controller_manager::ControllerSpec & controller) const
734+
{
735+
try
736+
{
737+
const auto new_state = controller.c->get_node()->shutdown();
738+
if (new_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED)
739+
{
740+
RCLCPP_WARN(
741+
get_logger(), "Failed to shutdown the controller '%s' before unloading!",
742+
controller.info.name.c_str());
743+
}
744+
}
745+
catch (const std::exception & e)
746+
{
747+
RCLCPP_ERROR(
748+
get_logger(),
749+
"Caught exception of type : %s while shutdown the controller '%s' before unloading: %s",
750+
typeid(e).name(), controller.info.name.c_str(), e.what());
751+
}
752+
catch (...)
753+
{
754+
RCLCPP_ERROR(
755+
get_logger(), "Failed to shutdown the controller '%s' before unloading",
756+
controller.info.name.c_str());
757+
}
758+
}
759+
748760
std::vector<ControllerSpec> ControllerManager::get_loaded_controllers() const
749761
{
750762
std::lock_guard<std::recursive_mutex> guard(rt_controllers_wrapper_.controllers_lock_);

controller_manager/test/test_controller/test_controller.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ CallbackReturn TestController::on_cleanup(const rclcpp_lifecycle::State & /*prev
160160
return CallbackReturn::SUCCESS;
161161
}
162162

163+
CallbackReturn TestController::on_shutdown(const rclcpp_lifecycle::State &)
164+
{
165+
if (shutdown_calls)
166+
{
167+
(*shutdown_calls)++;
168+
}
169+
return CallbackReturn::SUCCESS;
170+
}
171+
163172
void TestController::set_command_interface_configuration(
164173
const controller_interface::InterfaceConfiguration & cfg)
165174
{

controller_manager/test/test_controller/test_controller.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class TestController : public controller_interface::ControllerInterface
5454

5555
CallbackReturn on_cleanup(const rclcpp_lifecycle::State & previous_state) override;
5656

57+
CallbackReturn on_shutdown(const rclcpp_lifecycle::State & previous_state) override;
58+
5759
void set_command_interface_configuration(
5860
const controller_interface::InterfaceConfiguration & cfg);
5961

@@ -66,9 +68,10 @@ class TestController : public controller_interface::ControllerInterface
6668
rclcpp::Service<example_interfaces::srv::SetBool>::SharedPtr service_;
6769
unsigned int internal_counter = 0;
6870
bool simulate_cleanup_failure = false;
69-
// Variable where we store when cleanup was called, pointer because the controller
70-
// is usually destroyed after cleanup
71+
// Variable where we store when shutdown was called, pointer because the controller
72+
// is usually destroyed after shutdown
7173
size_t * cleanup_calls = nullptr;
74+
size_t * shutdown_calls = nullptr;
7275
controller_interface::InterfaceConfiguration cmd_iface_cfg_;
7376
controller_interface::InterfaceConfiguration state_iface_cfg_;
7477

controller_manager/test/test_controller_manager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ TEST_P(TestControllerManagerWithStrictness, controller_lifecycle)
228228
EXPECT_EQ(controller_interface::return_type::OK, unload_future.get());
229229

230230
EXPECT_EQ(
231-
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
231+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED,
232232
test_controller->get_lifecycle_state().id());
233233
EXPECT_EQ(1, test_controller.use_count());
234234
}
@@ -429,7 +429,7 @@ TEST_P(TestControllerManagerWithStrictness, async_controller_lifecycle)
429429
EXPECT_EQ(controller_interface::return_type::OK, unload_future.get());
430430

431431
EXPECT_EQ(
432-
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
432+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED,
433433
test_controller->get_lifecycle_state().id());
434434
EXPECT_EQ(1, test_controller.use_count());
435435
}

controller_manager/test/test_controller_manager_srvs.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -395,14 +395,14 @@ TEST_F(TestControllerManagerSrvs, reload_controller_libraries_srv)
395395
ASSERT_GT(test_controller.use_count(), 1)
396396
<< "Controller manager should have have a copy of this shared ptr";
397397

398-
cleanup_calls = 0;
399-
test_controller->cleanup_calls = &cleanup_calls;
398+
size_t shutdown_calls = 0;
399+
test_controller->shutdown_calls = &shutdown_calls;
400400
test_controller.reset(); // destroy our copy of the controller
401401

402402
request->force_kill = false;
403403
result = call_service_and_wait(*client, request, srv_executor, true);
404404
ASSERT_TRUE(result->ok);
405-
ASSERT_EQ(cleanup_calls, 1u);
405+
ASSERT_EQ(shutdown_calls, 1u);
406406
ASSERT_EQ(test_controller.use_count(), 0)
407407
<< "No more references to the controller after reloading.";
408408
test_controller.reset();
@@ -428,8 +428,8 @@ TEST_F(TestControllerManagerSrvs, reload_controller_libraries_srv)
428428
<< "Controller manager should still have have a copy of "
429429
"this shared ptr, no unloading was performed";
430430

431-
cleanup_calls = 0;
432-
test_controller->cleanup_calls = &cleanup_calls;
431+
shutdown_calls = 0;
432+
test_controller->shutdown_calls = &shutdown_calls;
433433
test_controller.reset(); // destroy our copy of the controller
434434

435435
// Force stop active controller
@@ -439,7 +439,7 @@ TEST_F(TestControllerManagerSrvs, reload_controller_libraries_srv)
439439

440440
ASSERT_EQ(test_controller_weak.use_count(), 0)
441441
<< "No more references to the controller after reloading.";
442-
ASSERT_EQ(cleanup_calls, 1u)
442+
ASSERT_EQ(shutdown_calls, 1u)
443443
<< "Controller should have been stopped and cleaned up with force_kill = true";
444444
}
445445

@@ -491,7 +491,7 @@ TEST_F(TestControllerManagerSrvs, unload_controller_srv)
491491
result = call_service_and_wait(*client, request, srv_executor, true);
492492
ASSERT_TRUE(result->ok);
493493
EXPECT_EQ(
494-
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
494+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED,
495495
test_controller->get_lifecycle_state().id());
496496
EXPECT_EQ(0u, cm_->get_loaded_controllers().size());
497497
}
@@ -526,7 +526,7 @@ TEST_F(TestControllerManagerSrvs, robot_description_on_load_and_unload_controlle
526526
unload_request->name = test_controller::TEST_CONTROLLER_NAME;
527527
auto result = call_service_and_wait(*unload_client, unload_request, srv_executor, true);
528528
EXPECT_EQ(
529-
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
529+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED,
530530
test_controller->get_lifecycle_state().id());
531531
EXPECT_EQ(0u, cm_->get_loaded_controllers().size());
532532

@@ -578,7 +578,7 @@ TEST_F(TestControllerManagerSrvs, configure_controller_srv)
578578
unload_request->name = test_controller::TEST_CONTROLLER_NAME;
579579
ASSERT_TRUE(call_service_and_wait(*unload_client, unload_request, srv_executor, true)->ok);
580580
EXPECT_EQ(
581-
lifecycle_msgs::msg::State::PRIMARY_STATE_UNCONFIGURED,
581+
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED,
582582
test_controller->get_lifecycle_state().id());
583583
EXPECT_EQ(0u, cm_->get_loaded_controllers().size());
584584
}

controller_manager/test/test_controller_with_interfaces/test_controller_with_interfaces.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ TestControllerWithInterfaces::on_cleanup(const rclcpp_lifecycle::State & /*previ
4545
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
4646
}
4747

48+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
49+
TestControllerWithInterfaces::on_shutdown(const rclcpp_lifecycle::State & /*previous_state*/)
50+
{
51+
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
52+
}
53+
4854
} // namespace test_controller_with_interfaces
4955

5056
#include "pluginlib/class_list_macros.hpp"

controller_manager/test/test_controller_with_interfaces/test_controller_with_interfaces.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class TestControllerWithInterfaces : public controller_interface::ControllerInte
5454

5555
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_cleanup(
5656
const rclcpp_lifecycle::State & previous_state) override;
57+
58+
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_shutdown(
59+
const rclcpp_lifecycle::State & previous_state) override;
5760
};
5861

5962
} // namespace test_controller_with_interfaces

0 commit comments

Comments
 (0)