Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Guidelines for modifications:
* Diego Ferigo
* Dongxuan Fan
* Dorsa Rohani
* Dundy Pasupuleti
* Ege Sekkin
* Emily Sturman
* Emmanuel Ferdman
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fixed
^^^^^

* Fixed :class:`~isaaclab.app.AppLauncher` registering a Python handler for
``SIGABRT``. fatal-signal dispositions are left untouched (default or
carb's crash handler); graceful shutdown remains on ``SIGTERM`` only.
26 changes: 16 additions & 10 deletions source/isaaclab/isaaclab/app/app_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -1535,15 +1535,18 @@ class _SimulationAppLifecycle:

* Normal exit: the ``atexit`` hook closes the app once; a pending unhandled
exception turns into exit code 1.
* ``SIGTERM`` / ``kill -ABRT``: ``close(exit_code=128 + signum)`` — full
teardown before exiting on isaacsim.simulation_app >= 2.18.5, truthful
status either way; if ``close()`` returns (fast shutdown disabled),
re-raise the signal with the default action.
* ``SIGTERM``: ``close(exit_code=128 + signum)``; full teardown before
exiting on isaacsim.simulation_app >= 2.18.5, truthful status either way;
if ``close()`` returns (fast shutdown disabled), re-raise the signal with
the default action.
* Signal during a running close: fall back to the default action instead of
re-entering ``close()``, which previously recursed until stack overflow.
* ``SIGSEGV``: not intercepted — a Python handler cannot run for main-thread
faults, reports success for worker-thread faults, and suppresses the carb
crash reporter's minidumps.
* ``SIGSEGV`` / ``SIGABRT``: not intercepted; dispositions are left
untouched (default or carb's crash handler). a python ``SIGSEGV``
handler that returns re-faults forever and suppresses carb crash
minidumps. the former ``SIGABRT`` handler restored ``SIG_DFL`` and
re-raised, so skipping registration leaves abort to the existing
disposition instead of a graceful-teardown path.
* ``SIGINT``: Python's default handler, so Ctrl-C unwinds user code and
exits nonzero.

Expand Down Expand Up @@ -1579,8 +1582,11 @@ def install_exit_handlers(self):
# close on normal exit so Kit shuts down cleanly instead of via __del__
atexit.register(self._close_at_exit)
signal.signal(signal.SIGTERM, self._on_abort_signal)
signal.signal(signal.SIGABRT, self._on_abort_signal)
# no SIGSEGV handler and default SIGINT — see the class docstring
# leave SIGSEGV/SIGABRT untouched (default or carb's crash handler);
# skipping signal.signal does not install SIG_DFL. a python SIGSEGV
# handler that returns re-executes the fault forever; the former
# SIGABRT handler restored SIG_DFL and re-raised.
# restore default SIGINT so ctrl-c unwinds user code; see the class docstring
signal.signal(signal.SIGINT, signal.default_int_handler)

def _close_at_exit(self):
Expand All @@ -1593,7 +1599,7 @@ def _close_at_exit(self):
self._close_app(exit_code)

def _on_abort_signal(self, signum, frame):
"""Handle SIGTERM/SIGABRT: close the app once, then die by the signal."""
"""Handle SIGTERM: close the app once, then die by the signal."""
if self._closing:
signal.signal(signum, signal.SIG_DFL)
signal.raise_signal(signum)
Expand Down
33 changes: 33 additions & 0 deletions source/isaaclab/test/app/test_simulation_app_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,36 @@ def _close(): # simulates a SimulationApp.close() without the exit_code paramet

assert calls == [1]
assert "does not accept exit_code" in capfd.readouterr().err


def test_install_exit_handlers_leaves_fatal_signals_at_default(monkeypatch):
Comment thread
dundysm marked this conversation as resolved.
"""AppLauncher must not register SIGSEGV or SIGABRT; only SIGTERM gets a custom handler.

Regression for issue #7774: skipping signal.signal leaves those dispositions
untouched (default or carb's crash handler) rather than establishing SIG_DFL.
a python SIGSEGV handler that returns re-faults forever with no core dump;
the former SIGABRT handler restored SIG_DFL and re-raised. graceful teardown
stays on SIGTERM only.
"""
actions = _capture_signal_actions(monkeypatch)
lifecycle = _make_lifecycle(lambda exit_code=0: None)
monkeypatch.setattr(
"isaaclab.app.app_launcher.atexit.register",
lambda callback: actions.append(("atexit", callback)),
)

lifecycle.install_exit_handlers()

registered = {
signum: handler
for action in actions
if action[0] == "set_handler"
for _, signum, handler in (action,)
}
assert signal.SIGTERM in registered
assert registered[signal.SIGTERM] == lifecycle._on_abort_signal
assert signal.SIGSEGV not in registered
assert signal.SIGABRT not in registered
assert registered.get(signal.SIGINT) == signal.default_int_handler
assert any(action[0] == "atexit" for action in actions)