-
Notifications
You must be signed in to change notification settings - Fork 44
[CI] Fix CI subprocess test hangs #557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c9b0c4b
9178911
24bcecb
085e52e
50fa74d
e2296f4
4c02c87
e4ab718
7bb8dd5
867391f
f41979d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -31,26 +31,70 @@ | |||||||||||||||
| _AT_LEAST_ONE_TEST_FAILED = False | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| def run_subprocess(cmd, env=None): | ||||||||||||||||
| print(f"Running command: {cmd}") | ||||||||||||||||
| _SUBPROCESS_TIMEOUT_SEC = int(os.environ.get("ISAACLAB_ARENA_SUBPROCESS_TIMEOUT", "600")) | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| def run_subprocess( | ||||||||||||||||
| cmd, | ||||||||||||||||
| env=None, | ||||||||||||||||
| timeout_sec: int | None = None, | ||||||||||||||||
| capture_output: bool = False, | ||||||||||||||||
| ) -> subprocess.CompletedProcess | None: | ||||||||||||||||
| """Run a command in a subprocess with timeout. | ||||||||||||||||
|
|
||||||||||||||||
| The child is launched with ``start_new_session=True`` so it lives in its | ||||||||||||||||
| own process group. The child-side ``SimulationAppContext`` uses this to | ||||||||||||||||
| SIGTERM its entire group before ``os._exit()``, preventing orphaned Kit | ||||||||||||||||
| children (shader compiler, GPU workers, …) from holding GPU resources and | ||||||||||||||||
| blocking the next subprocess. | ||||||||||||||||
|
|
||||||||||||||||
| Args: | ||||||||||||||||
| cmd: Command to run (list of strings). | ||||||||||||||||
| env: Optional environment dict. Defaults to inheriting the parent env. | ||||||||||||||||
| timeout_sec: Per-subprocess wall-clock timeout in seconds. | ||||||||||||||||
| Defaults to ``_SUBPROCESS_TIMEOUT_SEC`` (env ``ISAACLAB_ARENA_SUBPROCESS_TIMEOUT``, fallback 600). | ||||||||||||||||
| capture_output: If True, capture stdout/stderr and return a | ||||||||||||||||
| ``CompletedProcess``. When False (default) output streams to | ||||||||||||||||
| the parent process and the function returns None on success. | ||||||||||||||||
|
|
||||||||||||||||
| Returns: | ||||||||||||||||
| ``CompletedProcess`` when *capture_output* is True, else None. | ||||||||||||||||
| """ | ||||||||||||||||
| if timeout_sec is None: | ||||||||||||||||
| timeout_sec = _SUBPROCESS_TIMEOUT_SEC | ||||||||||||||||
|
|
||||||||||||||||
| print(f"Running command (timeout={timeout_sec}s): {cmd}") | ||||||||||||||||
| global _AT_LEAST_ONE_TEST_FAILED | ||||||||||||||||
|
|
||||||||||||||||
| if env is None: | ||||||||||||||||
| env = os.environ.copy() | ||||||||||||||||
|
Comment on lines
+69
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a caller passes their own
Suggested change
|
||||||||||||||||
| env["ISAACLAB_ARENA_FORCE_EXIT_ON_COMPLETE"] = "1" | ||||||||||||||||
|
|
||||||||||||||||
| try: | ||||||||||||||||
| result = subprocess.run( | ||||||||||||||||
| cmd, | ||||||||||||||||
| check=True, | ||||||||||||||||
| env=env, | ||||||||||||||||
| # Don't capture output, let it flow through in real-time | ||||||||||||||||
| capture_output=False, | ||||||||||||||||
| text=True, | ||||||||||||||||
| # Explicitly set stdout and stderr to None to use parent process's pipes | ||||||||||||||||
| stdout=None, | ||||||||||||||||
| stderr=None, | ||||||||||||||||
| timeout=timeout_sec, | ||||||||||||||||
| capture_output=capture_output, | ||||||||||||||||
| text=capture_output, | ||||||||||||||||
| start_new_session=True, | ||||||||||||||||
| ) | ||||||||||||||||
| print(f"Command completed with return code: {result.returncode}") | ||||||||||||||||
| except subprocess.CalledProcessError as e: | ||||||||||||||||
| sys.stderr.write(f"Command failed with return code {e.returncode}: {e}\n") | ||||||||||||||||
| except subprocess.TimeoutExpired: | ||||||||||||||||
| sys.stderr.write(f"\n[isaaclab-arena] Subprocess timed out after {timeout_sec}s\n") | ||||||||||||||||
| _AT_LEAST_ONE_TEST_FAILED = True | ||||||||||||||||
| raise e | ||||||||||||||||
| raise subprocess.SubprocessError(f"Subprocess timed out after {timeout_sec}s: {cmd}") | ||||||||||||||||
|
|
||||||||||||||||
| print(f"Command completed with return code: {result.returncode}") | ||||||||||||||||
| if result.returncode != 0: | ||||||||||||||||
| sys.stderr.write(f"Command failed with return code {result.returncode}\n") | ||||||||||||||||
| if capture_output and result.stderr: | ||||||||||||||||
| sys.stderr.write(result.stderr) | ||||||||||||||||
| _AT_LEAST_ONE_TEST_FAILED = True | ||||||||||||||||
| raise subprocess.CalledProcessError(result.returncode, cmd, result.stdout, result.stderr) | ||||||||||||||||
|
|
||||||||||||||||
| if capture_output: | ||||||||||||||||
| return result | ||||||||||||||||
| return None | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| class _IsolatedArgv: | ||||||||||||||||
|
|
@@ -108,7 +152,7 @@ def get_persistent_simulation_app(headless: bool, enable_cameras: bool = False) | |||||||||||||||
| first_headless, first_enable_cameras = _PERSISTENT_INIT_ARGS | ||||||||||||||||
| if (headless != first_headless) or (enable_cameras != first_enable_cameras): | ||||||||||||||||
| print( | ||||||||||||||||
| "[isaac-arena] Warning: persistent SimulationApp already initialized with " | ||||||||||||||||
| "[isaaclab-arena] Warning: persistent SimulationApp already initialized with " | ||||||||||||||||
| f"headless={first_headless}, enable_cameras={first_enable_cameras}. " | ||||||||||||||||
| "Ignoring new values." | ||||||||||||||||
| ) | ||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!