diff --git a/python/mujoco/CMakeLists.txt b/python/mujoco/CMakeLists.txt index 49caf532..8fffbcbd 100644 --- a/python/mujoco/CMakeLists.txt +++ b/python/mujoco/CMakeLists.txt @@ -68,6 +68,19 @@ add_link_options("${MUJOCO_HARDEN_LINK_OPTIONS}") find_package(Python3 COMPONENTS Interpreter Development) +# Detect free-threaded Python and enable Py_GIL_DISABLED. +# This is needed because we have some STATIC libraries that do not automatically +# get the Py_GIL_DISABLED macro definition through pybind11. +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('Py_GIL_DISABLED') or 0)" + OUTPUT_VARIABLE PYTHON_GIL_DISABLED + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if(PYTHON_GIL_DISABLED EQUAL "1") + message(STATUS "Free-threaded Python detected, enabling Py_GIL_DISABLED") + add_compile_definitions(Py_GIL_DISABLED) +endif() + include(FindOrFetch) # ==================== MUJOCO LIBRARY ========================================== diff --git a/python/mujoco/gil_test.py b/python/mujoco/gil_test.py new file mode 100644 index 00000000..8901eb73 --- /dev/null +++ b/python/mujoco/gil_test.py @@ -0,0 +1,35 @@ +# Copyright 2026 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Tests for GIL-free operation under free-threaded Python.""" + +import sys +import sysconfig + +from absl.testing import absltest +import mujoco # pytype: disable=unused-import + + +class GilDisabledTest(absltest.TestCase): + + @absltest.skipUnless( + sysconfig.get_config_var('Py_GIL_DISABLED'), + 'Not running under free-threaded Python', + ) + def test_gil_is_disabled(self): + self.assertFalse(sys._is_gil_enabled()) # pylint: disable=protected-access + + +if __name__ == '__main__': + absltest.main()