From 0c9083723c69f7f3c7b08c1a8b43324b2f7739cb Mon Sep 17 00:00:00 2001 From: "robin.moss" Date: Mon, 23 Feb 2026 13:29:15 +0000 Subject: [PATCH] fix: release the gil when calling compile in spec.recompile The pybind for recompile was not releasing the GIL when it called mj_recompile, this can caused deadlocks with the asset loading threads. For example if a resource provider was trying to call Python. To resolve this the GIL is release just for the mj_recompile. Addresses: https://github.com/google-deepmind/mujoco/issues/3118 --- python/mujoco/structs.cc | 13 ++++++++++++- python/mujoco/structs_wrappers.cc | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index d8a88f0b..253feb0a 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -77,7 +77,18 @@ py::tuple RecompileSpec(raw::MjSpec* spec, const MjModelWrapper& old_m, raw::MjModel* m = static_cast(mju_malloc(sizeof(mjModel))); m->buffer = nullptr; raw::MjData* d = mj_copyData(nullptr, old_m.get(), old_d.get()); - if (mj_recompile(spec, nullptr, m, d)) { + + bool compile_failed = false; + + { + // Release GIL before calling mj_recompile which may spawn threads + py::gil_scoped_release no_gil; + if (mj_recompile(spec, nullptr, m, d)) { + compile_failed = true; + } + } + + if (compile_failed) { throw py::value_error(mjs_getError(spec)); } diff --git a/python/mujoco/structs_wrappers.cc b/python/mujoco/structs_wrappers.cc index eac086f5..ebfa907b 100644 --- a/python/mujoco/structs_wrappers.cc +++ b/python/mujoco/structs_wrappers.cc @@ -400,7 +400,18 @@ py::tuple RecompileSpec(raw::MjSpec* spec, const MjModelWrapper& old_m, raw::MjModel* m = static_cast(mju_malloc(sizeof(mjModel))); m->buffer = nullptr; raw::MjData* d = mj_copyData(nullptr, old_m.get(), old_d.get()); - if (mj_recompile(spec, nullptr, m, d)) { + + bool compile_failed = false; + + { + // Release GIL before calling mj_recompile which may spawn threads + py::gil_scoped_release no_gil; + if (mj_recompile(spec, nullptr, m, d)) { + compile_failed = true; + } + } + + if (compile_failed) { throw py::value_error(mjs_getError(spec)); }