howto-free-threading-extensions-pysci
howto-free-threading-extensions-pysci
Threading
Release 3.13.2
Contents
2 Module Initialization 2
2.1 Multi-Phase Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Single-Phase Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
4 Borrowed References 3
Starting with the 3.13 release, CPython has experimental support for running with the global interpreter lock (GIL)
disabled in a configuration called free threading. This document describes how to adapt C API extensions to support
free threading.
#ifdef Py_GIL_DISABLED
/* code that only runs in the free-threaded build */
#endif
1
2 Module Initialization
Extension modules need to explicitly indicate that they support running with the GIL disabled; otherwise importing
the extension will raise a warning and enable the GIL at runtime.
There are two ways to indicate that an extension module supports running with the GIL disabled depending on whether
the extension uses multi-phase or single-phase initialization.
PyMODINIT_FUNC
PyInit_mymodule(void)
{
PyObject *m = PyModule_Create(&moduledef);
if (m == NULL) {
return NULL;
}
#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
return m;
}
2
3 General API Guidelines
Most of the C API is thread-safe, but there are some exceptions.
• Struct Fields: Accessing fields in Python C API objects or structs directly is not thread-safe if the field may
be concurrently modified.
• Macros: Accessor macros like PyList_GET_ITEM and PyList_SET_ITEM do not perform any error check-
ing or locking. These macros are not thread-safe if the container object may be modified concurrently.
• Borrowed References: C API functions that return borrowed references may not be thread-safe if the con-
taining object is modified concurrently. See the section on borrowed references for more information.
PyDict_Next
A notable exception is PyDict_Next(), which does not lock the dictionary. You should use
Py_BEGIN_CRITICAL_SECTION to protect the dictionary while iterating over it if the dictionary may be
concurrently modified:
Py_BEGIN_CRITICAL_SECTION(dict);
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(dict, &pos, &key, &value)) {
...
}
Py_END_CRITICAL_SECTION();
4 Borrowed References
Some C API functions return borrowed references. These APIs are not thread-safe if the containing object is modified
concurrently. For example, it’s not safe to use PyList_GetItem() if the list may be modified concurrently.
The following table lists some borrowed reference APIs and their replacements that return strong references.
Not all APIs that return borrowed references are problematic. For example, PyTuple_GetItem() is safe because
tuples are immutable. Similarly, not all uses of the above APIs are problematic. For example, PyDict_GetItem()
is often used for parsing keyword argument dictionaries in function calls; those keyword argument dictionaries are
effectively private (not accessible by other threads), so using borrowed references in that context is safe.
Some of these functions were added in Python 3.13. You can use the pythoncapi-compat package to provide imple-
mentations of these functions for older Python versions.
3
5 Memory Allocation APIs
Python’s memory management C API provides functions in three different allocation domains: “raw”, “mem”, and
“object”. For thread-safety, the free-threaded build requires that only Python objects are allocated using the object
domain, and that all Python object are allocated using that domain. This differs from the prior Python versions, where
this was only a best practice and not a hard requirement.
® Note
Search for uses of PyObject_Malloc() in your extension and check that the allocated memory is used for
Python objects. Use PyMem_Malloc() to allocate buffers instead of PyObject_Malloc().
4
® Note
You will need to build separate wheels specifically for the free-threaded build. If you currently use the stable
ABI, you can continue to build a single wheel for multiple non-free-threaded Python versions.
8.2 Windows
Due to a limitation of the official Windows installer, you will need to manually define Py_GIL_DISABLED=1 when
building extensions from source.
µ See also
Porting Extension Modules to Support Free-Threading: A community-maintained porting guide for extension
authors.