diff --git a/embodichain/lab/sim/sim_manager.py b/embodichain/lab/sim/sim_manager.py index cb734239..544a6580 100644 --- a/embodichain/lab/sim/sim_manager.py +++ b/embodichain/lab/sim/sim_manager.py @@ -2054,17 +2054,30 @@ def wait_scene_destruction(timeout_ms: int = 10000) -> None: f"Scene destruction wait timeout, {scene_count} C++ scene(s) still alive!" ) - def destroy(self) -> None: + def destroy(self, exit_process: bool | None = None) -> None: """ No longer destructs C++ objects in place due to lingering deep local variables; instead, packages itself into a destruction task, submits to the cleanup queue, and waits for top-level delayed consumption. + + Args: + exit_process (bool | None): Whether to call os._exit(0) after queuing + the destruction task. If None, reads EMBODICHAIN_SIM_EXIT_PROCESS. """ - self._is_pending_kill = True + if exit_process is None: + exit_process = ( + os.getenv("EMBODICHAIN_SIM_EXIT_PROCESS", "1").strip().lower() + ) + exit_process = exit_process not in ("0", "false", "no", "off") + + self._is_pending_kill = True # Transfer the actual destruction logic to the cleanup queue SimulationManager._cleanup_queue.put(self._deferred_destroy) + if exit_process: + os._exit(0) + def _deferred_destroy(self) -> None: """Destroy all simulated assets and release resources.""" # Clean up all gizmos before destroying the simulation diff --git a/scripts/tutorials/gym/random_reach.py b/scripts/tutorials/gym/random_reach.py index b55a7a8e..ddea8b77 100644 --- a/scripts/tutorials/gym/random_reach.py +++ b/scripts/tutorials/gym/random_reach.py @@ -176,3 +176,5 @@ def _extend_obs(self, obs: EnvObs, **kwargs) -> EnvObs: print(f"FPS: {fps:.2f}") else: print("Elapsed time is too short to calculate FPS.") + + env.close() diff --git a/tests/conftest.py b/tests/conftest.py index d0824fd0..d076ff13 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,8 @@ import os import pytest +os.environ.setdefault("EMBODICHAIN_SIM_EXIT_PROCESS", "0") + def pytest_addoption(parser): parser.addoption(