@@ -82,6 +82,7 @@ def teardown_simulation_app(suppress_exceptions: bool = False, make_new_stage: b
8282 omni .usd .get_context ().new_stage ()
8383
8484
85+
8586def reapply_viewer_cfg (env ) -> None :
8687 """Re-apply ViewerCfg camera position after visualizers are initialized.
8788
@@ -95,6 +96,26 @@ def reapply_viewer_cfg(env) -> None:
9596 if vcc is not None :
9697 vcc .update_view_location ()
9798
99+ def _kill_child_processes () -> None :
100+ """SIGKILL all direct child processes of the current process via /proc."""
101+ import signal
102+
103+ my_pid = os .getpid ()
104+ with suppress (FileNotFoundError , PermissionError ):
105+ for entry in os .scandir ("/proc" ):
106+ if not entry .name .isdigit ():
107+ continue
108+ try :
109+ with open (f"/proc/{ entry .name } /status" ) as f :
110+ for line in f :
111+ if line .startswith ("PPid:" ):
112+ if int (line .split ()[1 ]) == my_pid :
113+ os .kill (int (entry .name ), signal .SIGKILL )
114+ break
115+ except (FileNotFoundError , PermissionError , ProcessLookupError , ValueError ):
116+ continue
117+
118+
98119
99120class SimulationAppContext :
100121 """Context manager for launching and closing a simulation app."""
@@ -129,12 +150,17 @@ def __exit__(self, exc_type, exc_val, exc_tb):
129150 os ._exit (1 )
130151
131152 # When launched as a test subprocess, skip app.close() which can hang
132- # indefinitely in Kit's shutdown path. The parent process owns the
133- # lifetime via process-group kill (see run_subprocess).
153+ # indefinitely in Kit's shutdown path.
134154 if os .environ .get ("ISAACLAB_ARENA_FORCE_EXIT_ON_COMPLETE" ) == "1" :
135155 print ("Force-exiting subprocess (ISAACLAB_ARENA_FORCE_EXIT_ON_COMPLETE=1)" )
136156 sys .stdout .flush ()
137157 sys .stderr .flush ()
158+ # SIGKILL orphaned Kit children (shader compiler, GPU workers, …)
159+ # so they don't hold GPU resources and block the next test subprocess.
160+ # We target each child individually via /proc to avoid signalling
161+ # ourselves (Kit installs a C-level SIGTERM handler that overrides
162+ # Python's SIG_IGN, so os.killpg is not safe here).
163+ _kill_child_processes ()
138164 os ._exit (0 )
139165
140166 # Normal interactive / non-test path: attempt a clean Kit shutdown.
0 commit comments