Running and Signals
Python run() and run_async() always use caller-managed signal policy. The binding installs no
signal handler, creates no managed-signal domain, and does not change process signal masks or
dispositions.
Treat RunSession.request_stop() as the public cooperative stop operation and make the surrounding
Python application responsible for translating UI, service, notebook, or process-control events
into that request.
- What caller-managed means for Python runs
- How context-manager ownership prevents abandoned sessions
- How
wait(timeout=...)interacts with CPython signal delivery - How to turn a caller-owned event into cooperative stop
Python does not receive C++ managed SIGINT/SIGTERM behavior. Do not assume that hs.run(plan)
installs an SDK Ctrl-C handler. It always leaves process signals under the caller’s existing policy.
Prefer scoped asynchronous sessions
request_stop() is thread-safe, nonblocking, and idempotent. It requests ordinary cooperative
cleanup; it does not wait. wait() returns True after the session reaches terminal state, or
False when its timeout expires. A successful wait does not request stop.
Exiting the context manager requests stop and waits even when the body raises. If a session loses its last Python reference elsewhere, cleanup is delegated to ordinary thread context rather than performed from a runtime callback.
Understand Ctrl-C and the GIL
CPython’s low-level SIGINT handler records a pending interrupt. KeyboardInterrupt is raised on the
main thread when it next reacquires the GIL and executes Python code. Holoscan releases the GIL while
waiting and periodically returns to Python’s signal check.
For a command-line application that relies on the default CPython Ctrl-C behavior, use bounded waits so the main thread regularly returns to Python:
This loop waits on the session; it does not poll runtime state with a sleep. The half-second interval is a responsiveness choice, not a scheduler setting.
Bridge an application-owned event
Larger applications often already own shutdown policy. Keep a Python signal handler minimal by
setting a threading.Event, then request stop from ordinary control flow:
Python signal callbacks execute later on the main interpreter thread. Never call request_stop()
directly from a native POSIX signal handler: the C++ operation is thread-safe but not
async-signal-safe.
Blocking run remains inspectable
For a finite plan, hs.run(plan) releases the GIL while waiting and returns the terminal session:
Prefer run_async() for recurring graphs or applications that need responsive Python-side stop
control.
Recap
- Python runs are caller-managed; the SDK installs no signal handler or managed-signal domain.
- Use
RunSessionas a context manager and stop withrequest_stop(). - Bounded waits let the main thread observe
KeyboardInterruptor caller-owned shutdown events. - Never invoke
request_stop()from a native POSIX signal handler.