Add -nogil opt-in flag to remove need for PYTHON_GIL=0

Closes #3215
This commit is contained in:
crusaderky 2025-07-18 22:33:09 +01:00 committed by William S Fulton
parent 7a7aba03b6
commit 6b556a6a1c
9 changed files with 42 additions and 20 deletions

View File

@ -122,6 +122,7 @@ jobs:
- SWIGLANG: python
VER: '3.12'
CSTD: gnu99
SWIG_FEATURES: -nogil # Test that -nogil has no effect on non-free-threading Python
- SWIGLANG: python
VER: '3.13'
CSTD: gnu99
@ -131,6 +132,7 @@ jobs:
- SWIGLANG: python
VER: '3.13t' # no-gil testing
CSTD: gnu99
SWIG_FEATURES: -nogil
- SWIGLANG: python
VER: '3.14'
PY_ABI_VER: '3.14'

View File

@ -7,6 +7,9 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.4.0 (in progress)
===========================
2025-07-18: crusaderky
[Python] #3215 Added -nogil flag to mark modules as free-threading compatible.
2025-07-18: jim-easterbrook
[Python] #3219 Python builtin heap types buffer support via the
"python:bf_getbuffer" and "python:bf_releasebuffer" features improvement.

View File

@ -213,6 +213,7 @@ SWIGGO_CGO Defined when using Go for cgo
SWIGGO_GCCGO Defined when using Go for gccgo
SWIGGO_INTGO_SIZE Size of the Go type int when using Go (32 or 64)
SWIGPYTHON_BUILTIN Defined when using Python with -builtin
SWIGPYTHON_NOGIL Defined when using Python with -nogil
SWIG_RUBY_AUTORENAME Defined when using Ruby with -autorename
</pre></div>

View File

@ -963,6 +963,7 @@ swig -python -help
<tr><td>-interface &lt;mod&gt;</td><td>Set low-level C/C++ module name to &lt;mod&gt; (default: module name prefixed by '_')</td></tr>
<tr><td>-keyword </td><td>Use keyword arguments</td></tr>
<tr><td>-nofastunpack </td><td>Use traditional UnpackTuple method to parse the argument functions</td></tr>
<tr><td>-nogil </td><td>Enable free-threading if supported by the Python interpreter</td></tr>
<tr><td>-noh </td><td>Don't generate the output header file</td></tr>
<tr><td>-noproxy </td><td>Don't generate proxy classes</td></tr>
<tr><td>-nortti </td><td>Disable the use of the native C++ RTTI with directors</td></tr>
@ -7641,10 +7642,12 @@ will not be able to run any other threads, even if the wrapped C/C++ code is wai
<a href="https://docs.python.org/3/howto/free-threading-python.html">Free threading Python</a>
disables the Global Interpreter Lock (GIL) for improved parallel execution or multi-threaded
execution.
Python free threading was first released with experimental support for Python 3.13 and requires a special build of Python where the GIL can be disabled.
SWIG supports free threading Python; just use a free threading build of Python.
In particular, the Python header files from the free threading build of Python must be used
when compiling the SWIG generated code.
Python free threading was first released with experimental support for Python 3.13 and requires a
special build of Python where the GIL can be disabled.
SWIG support for free threading Python is opt-in; you must pass the <tt>-nogil</tt> flag to swig to
indicate that the wrapped code is thread-safe. The flag has no effect on Python builds that don't
support free threading. In particular, the Python header files from the free threading build of
Python must be used when compiling the SWIG generated code.
This is needed for the C/C++ compiler to be aware of the <tt>Py_GIL_DISABLED</tt> Python macro,
which identifies the free threading build, and is used by the SWIG generated code.
Please see the <a href="https://py-free-threading.github.io/">Python free-threading guide</a>
@ -7659,6 +7662,14 @@ Users must also write thread-safe code and prevent unsafe shared mutation using
Python code or using C/C++ native locking.
</p>
<p>
Note that free-threading support is separate to the standard multithreading support described
in the previous section. For example, one can still declare that functions should or shouldn't
release the GIL, but that will only have an effect when and if the GIL is available in the
Python interpreter to begin with. If your wrapped code is thread-unsafe, you should use
an explicit locking system for it and should not rely on the GIL to protect it.
</p>
<p>
<b>Compatibility Note:</b> Support for free threading Python was added in SWIG-4.4.0.
</p>

View File

@ -1303,8 +1303,7 @@ else
PYTHON_SO = @PYTHON_SO@
endif
PYNOGIL = @PYNOGIL@
PYFLAGS = -Walways
PYFLAGS = -Werror
PYCODESTYLE = @PYCODESTYLE@
PYCODESTYLE_FLAGS = --ignore=E252,E30,E402,E501,E731,W291,W391
PYABI3AUDIT = @PYABI3AUDIT@
@ -1368,7 +1367,7 @@ python_run: $(PYSCRIPT)
ifneq (,$(PYCODESTYLE))
$(COMPILETOOL) $(PYCODESTYLE) $(PYCODESTYLE_FLAGS) $(PYSCRIPT)
endif
env PYTHONPATH=$$PWD $(RUNTOOL) $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(PYSCRIPT) $(RUNPIPE)
env PYTHONPATH=$$PWD $(RUNTOOL) $(PYTHON) $(PYFLAGS) $(PYSCRIPT) $(RUNPIPE)
ifneq (,$(SRCDIR))
$(RUNME).py: $(SRCDIR)$(RUNME).py

View File

@ -9,8 +9,7 @@ else
endif
LANGUAGE = python
PYNOGIL = @PYNOGIL@
PYFLAGS = -Walways
PYFLAGS = -Werror
SCRIPTSUFFIX = _runme.py
PYCODESTYLE = @PYCODESTYLE@
PYCODESTYLE_FLAGS = --ignore=E252,E30,E402,E501,E731,E741,W291,W391
@ -184,7 +183,7 @@ endif
# a file is found which has _runme.py appended after the testcase name.
run_testcase = \
if [ -f $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX) ]; then \
env LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH PYTHONPATH=.:$(srcdir):$$PYTHONPATH $(RUNTOOL) $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX); \
env LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH PYTHONPATH=.:$(srcdir):$$PYTHONPATH $(RUNTOOL) $(PYTHON) $(PYFLAGS) $(SCRIPTDIR)/$(SCRIPTPREFIX)$*$(SCRIPTSUFFIX); \
fi
# Clean: remove the generated .py file
@ -207,5 +206,5 @@ hugemod:
perl hugemod.pl $(hugemod_runme)
$(MAKE) hugemod_a.cpptest
$(MAKE) hugemod_b.cpptest
time $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYNOGIL) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYFLAGS) $(hugemod_runme)
time $(PYTHON) $(PYFLAGS) $(hugemod_runme)

View File

@ -206,6 +206,11 @@ SWIG_init(void) {
#if PY_VERSION_HEX >= 0x03000000
static PyModuleDef_Slot SwigSlots[] = {
{ Py_mod_exec, (void *)SWIG_mod_exec },
#ifdef SWIGPYTHON_NOGIL
#ifdef Py_GIL_DISABLED
{ Py_mod_gil, Py_MOD_GIL_NOT_USED },
#endif
#endif
{ 0, NULL }
};

View File

@ -92,6 +92,7 @@ static int extranative = 0;
static int nortti = 0;
static int relativeimport = 0;
static int flat_static_method = 0;
static int nogil = 0;
/* flags for the make_autodoc function */
namespace {
@ -123,6 +124,7 @@ Python Options (available with -python)\n\
-keyword - Use keyword arguments\n";
static const char *usage2 = "\
-nofastunpack - Use traditional UnpackTuple method to parse the argument functions\n\
-nogil - Enable free-threading if supported by the Python interpreter\n\
-noh - Don't generate the output header file\n";
static const char *usage3 = "\
-noproxy - Don't generate proxy classes\n\
@ -414,6 +416,10 @@ public:
builtin = 1;
Preprocessor_define("SWIGPYTHON_BUILTIN", 0);
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-nogil") == 0) {
nogil = 1;
Preprocessor_define("SWIGPYTHON_NOGIL", 0);
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-relativeimport") == 0) {
relativeimport = 1;
Swig_mark_arg(i);
@ -644,6 +650,10 @@ public:
Printf(f_runtime, "#define SWIGPYTHON_FASTPROXY\n");
}
if (nogil) {
Printf(f_runtime, "#define SWIGPYTHON_NOGIL\n");
}
Printf(f_runtime, "\n");
Printf(f_header, "#ifdef SWIG_TypeQuery\n");

View File

@ -2050,13 +2050,6 @@ else
if test -z "$PYVER"; then
PYVER=0
fi
AC_MSG_CHECKING([if $PYTHON3 GIL can be disabled])
PYGIL=`($PYTHON3 -c "import sysconfig; print('yes' if bool(sysconfig.get_config_var('Py_GIL_DISABLED')) else 'no')") 2>/dev/null`
AC_MSG_RESULT($PYGIL)
if test x"$PYGIL" = x"yes"; then
PYNOGIL="-X gil=0"
fi
fi
if test $PYVER -ge 3; then
@ -2185,7 +2178,6 @@ else
*)PYTHON3DYNAMICLINKING="";;
esac
AC_SUBST(PYNOGIL)
AC_SUBST(PY3INCLUDE)
AC_SUBST(PY3LIB)
AC_SUBST(PY3LINK)