Skip to content

Commit ab72594

Browse files
authored
Unload controller upon sigterm (#3406) (#3427)
1 parent c2d3111 commit ab72594

3 files changed

Lines changed: 32 additions & 5 deletions

File tree

controller_manager/controller_manager/spawner.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def parse_args_advanced(args):
115115
"-u",
116116
"--unload-on-kill",
117117
action="store_true",
118-
help="Deactivate the active controllers and unload them on kill",
118+
help="Deactivate the active controllers and unload them on SIGINT or SIGTERM",
119119
)
120120
global_parser.add_argument("-h", "--help", action="store_true", help="Show help")
121121

@@ -253,7 +253,7 @@ def parse_native_args(args):
253253
parser.add_argument(
254254
"-u",
255255
"--unload-on-kill",
256-
help="Wait until this application is interrupted and unload controller",
256+
help="Wait until this application is interrupted (SIGINT or SIGTERM) and deactivate/unload controllers",
257257
action="store_true",
258258
)
259259
parser.add_argument(
@@ -372,6 +372,13 @@ def get_ros_params_files(argv):
372372
activate_as_group = global_args.activate_as_group
373373
unload_on_kill = global_args.unload_on_kill
374374
node = None
375+
lock = None
376+
377+
def _on_shutdown_signal(signum, frame):
378+
raise KeyboardInterrupt()
379+
380+
signal.signal(signal.SIGINT, _on_shutdown_signal)
381+
signal.signal(signal.SIGTERM, _on_shutdown_signal)
375382

376383
if spawner_ros_params_files:
377384
for controller in controllers:
@@ -604,6 +611,7 @@ def get_ros_params_files(argv):
604611
# second KeyboardInterrupt during the signal.signal() call itself.
605612
try:
606613
signal.signal(signal.SIGINT, signal.SIG_IGN)
614+
signal.signal(signal.SIGTERM, signal.SIG_IGN)
607615
except (KeyboardInterrupt, Exception):
608616
pass
609617
if unload_on_kill:
@@ -662,7 +670,7 @@ def get_ros_params_files(argv):
662670
finally:
663671
if node:
664672
node.destroy_node()
665-
if lock.is_locked:
673+
if lock is not None and lock.is_locked:
666674
lock.release()
667675
rclpy.shutdown()
668676

controller_manager/doc/userdoc.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ There are two scripts to interact with controller manager from launch files:
187187
DEPRECATED Namespace for the controller_manager and the controller(s)
188188
--load-only Only load the controller and leave unconfigured.
189189
--inactive Load and configure the controller, however do not activate them
190-
-u, --unload-on-kill Wait until this application is interrupted and unload controller
190+
-u, --unload-on-kill Wait until this application is interrupted (SIGINT or SIGTERM) and deactivate/unload controllers
191191
--controller-manager-timeout CONTROLLER_MANAGER_TIMEOUT
192192
Time to wait for the controller manager service to be available
193193
--switch-timeout SWITCH_TIMEOUT
@@ -274,7 +274,7 @@ The ``spawner`` now supports per controller arguments, while parsing the argumen
274274
--activate-as-group Activate controllers as a group
275275
--switch-asap, --no-switch-asap
276276
Switch controllers as soon as possible
277-
-u, --unload-on-kill Deactivate the active controllers and unload them on kill
277+
-u, --unload-on-kill Deactivate the active controllers and unload them on SIGINT or SIGTERM
278278
-h, --help Show help
279279
280280
Controller Options:

controller_manager/test/test_spawner_unspawner.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,25 @@ TEST_F(TestLoadController, unload_on_kill_activate_as_group)
605605
ASSERT_EQ(cm_->get_loaded_controllers().size(), 0ul);
606606
}
607607

608+
TEST_F(TestLoadController, unload_on_kill_with_sigterm)
609+
{
610+
// When a launch file shuts down because a required sibling process crashes,
611+
// it sends SIGINT and escalates to SIGTERM, --unload-on-kill must still
612+
// deactivate and unload the controller when SIGTERM is delivered.
613+
ControllerManagerRunner cm_runner(this);
614+
cm_->set_parameter(rclcpp::Parameter("ctrl_3.type", test_controller::TEST_CONTROLLER_CLASS_NAME));
615+
std::stringstream ss;
616+
ss << "timeout --signal=TERM 5 "
617+
<< std::string(coveragepy_script) +
618+
" $(ros2 pkg prefix controller_manager)/lib/controller_manager/spawner "
619+
<< "ctrl_3 -c test_controller_manager --unload-on-kill";
620+
621+
EXPECT_NE(std::system(ss.str().c_str()), 0)
622+
<< "timeout should have killed spawner and returned non 0 code";
623+
624+
ASSERT_EQ(cm_->get_loaded_controllers().size(), 0ul);
625+
}
626+
608627
TEST_F(TestLoadController, spawner_test_to_check_parameter_overriding)
609628
{
610629
const std::string main_test_file_path =

0 commit comments

Comments
 (0)