From 6f52631a2ad8f2715cd86100bf4b2377f54c5ed5 Mon Sep 17 00:00:00 2001 From: Kevin Zakka Date: Mon, 2 Mar 2026 12:21:34 -0800 Subject: [PATCH] Fix process hang on viewer close in launch_passive WaitForAtomicNoGil spins until the render loop consumes a pending set_texts/set_figures/set_images request (atomic == expected). If the user closes the viewer window after a request is submitted but before the render loop consumes it, the render loop exits and the atomic is never updated. The calling thread spins forever, preventing the process from terminating. Check exitrequest inside the spin loop so the wait bails out once the viewer is shutting down. --- python/mujoco/simulate.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/mujoco/simulate.cc b/python/mujoco/simulate.cc index 22209510..bf412381 100644 --- a/python/mujoco/simulate.cc +++ b/python/mujoco/simulate.cc @@ -247,6 +247,11 @@ class SimulateWrapper { if (simulate_) { py::gil_scoped_release no_gil; while (atomic.load() != expected) { + // If the viewer is exiting, stop waiting. The render loop will + // never consume the pending request once it has set exitrequest. + if (simulate_->exitrequest.load()) { + return true; + } // TODO(robotics-simulation): replace with `atomic.wait(expected)` when // we migrate python bindings to C++20 (we may need to drop GCC 10). std::this_thread::yield();