|
| 1 | +.. highlight:: c |
| 2 | + |
| 3 | +.. _freethreading-extensions-howto: |
| 4 | + |
| 5 | +****************************************** |
| 6 | +C API Extension Support for Free Threading |
| 7 | +****************************************** |
| 8 | + |
| 9 | +Starting with the 3.13 release, CPython has experimental support for running |
| 10 | +with the :term:`global interpreter lock` (GIL) disabled in a configuration |
| 11 | +called :term:`free threading`. This document describes how to adapt C API |
| 12 | +extensions to support free threading. |
| 13 | + |
| 14 | + |
| 15 | +Identifying the Free-Threaded Build in C |
| 16 | +======================================== |
| 17 | + |
| 18 | +The CPython C API exposes the ``Py_GIL_DISABLED`` macro: in the free-threaded |
| 19 | +build it's defined to ``1``, and in the regular build it's not defined. |
| 20 | +You can use it to enable code that only runs under the free-threaded build:: |
| 21 | + |
| 22 | + #ifdef Py_GIL_DISABLED |
| 23 | + /* code that only runs in the free-threaded build */ |
| 24 | + #endif |
| 25 | + |
| 26 | +Module Initialization |
| 27 | +===================== |
| 28 | + |
| 29 | +Extension modules need to explicitly indicate that they support running with |
| 30 | +the GIL disabled; otherwise importing the extension will raise a warning and |
| 31 | +enable the GIL at runtime. |
| 32 | + |
| 33 | +There are two ways to indicate that an extension module supports running with |
| 34 | +the GIL disabled depending on whether the extension uses multi-phase or |
| 35 | +single-phase initialization. |
| 36 | + |
| 37 | +Multi-Phase Initialization |
| 38 | +.......................... |
| 39 | + |
| 40 | +Extensions that use multi-phase initialization (i.e., |
| 41 | +:c:func:`PyModuleDef_Init`) should add a :c:data:`Py_mod_gil` slot in the |
| 42 | +module definition. If your extension supports older versions of CPython, |
| 43 | +you should guard the slot with a :c:data:`PY_VERSION_HEX` check. |
| 44 | + |
| 45 | +:: |
| 46 | + |
| 47 | + static struct PyModuleDef_Slot module_slots[] = { |
| 48 | + ... |
| 49 | + #if PY_VERSION_HEX >= 0x030D0000 |
| 50 | + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, |
| 51 | + #endif |
| 52 | + {0, NULL} |
| 53 | + }; |
| 54 | + |
| 55 | + static struct PyModuleDef moduledef = { |
| 56 | + PyModuleDef_HEAD_INIT, |
| 57 | + .m_slots = module_slots, |
| 58 | + ... |
| 59 | + }; |
| 60 | + |
| 61 | + |
| 62 | +Single-Phase Initialization |
| 63 | +........................... |
| 64 | + |
| 65 | +Extensions that use single-phase initialization (i.e., |
| 66 | +:c:func:`PyModule_Create`) should call :c:func:`PyUnstable_Module_SetGIL` to |
| 67 | +indicate that they support running with the GIL disabled. The function is |
| 68 | +only defined in the free-threaded build, so you should guard the call with |
| 69 | +``#ifdef Py_GIL_DISABLED`` to avoid compilation errors in the regular build. |
| 70 | + |
| 71 | +:: |
| 72 | + |
| 73 | + static struct PyModuleDef moduledef = { |
| 74 | + PyModuleDef_HEAD_INIT, |
| 75 | + ... |
| 76 | + }; |
| 77 | + |
| 78 | + PyMODINIT_FUNC |
| 79 | + PyInit_mymodule(void) |
| 80 | + { |
| 81 | + PyObject *m = PyModule_Create(&moduledef); |
| 82 | + if (m == NULL) { |
| 83 | + return NULL; |
| 84 | + } |
| 85 | + #ifdef Py_GIL_DISABLED |
| 86 | + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); |
| 87 | + #endif |
| 88 | + return m; |
| 89 | + } |
| 90 | + |
| 91 | + |
| 92 | +General API Guidelines |
| 93 | +====================== |
| 94 | + |
| 95 | +Most of the C API is thread-safe, but there are some exceptions. |
| 96 | + |
| 97 | +* **Struct Fields**: Accessing fields in Python C API objects or structs |
| 98 | + directly is not thread-safe if the field may be concurrently modified. |
| 99 | +* **Macros**: Accessor macros like :c:macro:`PyList_GET_ITEM` and |
| 100 | + :c:macro:`PyList_SET_ITEM` do not perform any error checking or locking. |
| 101 | + These macros are not thread-safe if the container object may be modified |
| 102 | + concurrently. |
| 103 | +* **Borrowed References**: C API functions that return |
| 104 | + :term:`borrowed references <borrowed reference>` may not be thread-safe if |
| 105 | + the containing object is modified concurrently. See the section on |
| 106 | + :ref:`borrowed references <borrowed-references>` for more information. |
| 107 | + |
| 108 | + |
| 109 | +Container Thread Safety |
| 110 | +....................... |
| 111 | + |
| 112 | +Containers like :c:struct:`PyListObject`, |
| 113 | +:c:struct:`PyDictObject`, and :c:struct:`PySetObject` perform internal locking |
| 114 | +in the free-threaded build. For example, the :c:func:`PyList_Append` will |
| 115 | +lock the list before appending an item. |
| 116 | + |
| 117 | + |
| 118 | +Borrowed References |
| 119 | +=================== |
| 120 | + |
| 121 | +.. _borrowed-references: |
| 122 | + |
| 123 | +Some C API functions return :term:`borrowed references <borrowed reference>`. |
| 124 | +These APIs are not thread-safe if the containing object is modified |
| 125 | +concurrently. For example, it's not safe to use :c:func:`PyList_GetItem` |
| 126 | +if the list may be modified concurrently. |
| 127 | + |
| 128 | +The following table lists some borrowed reference APIs and their replacements |
| 129 | +that return :term:`strong references <strong reference>`. |
| 130 | + |
| 131 | ++-----------------------------------+-----------------------------------+ |
| 132 | +| Borrowed reference API | Strong reference API | |
| 133 | ++===================================+===================================+ |
| 134 | +| :c:func:`PyList_GetItem` | :c:func:`PyList_GetItemRef` | |
| 135 | ++-----------------------------------+-----------------------------------+ |
| 136 | +| :c:func:`PyDict_GetItem` | :c:func:`PyDict_GetItemRef` | |
| 137 | ++-----------------------------------+-----------------------------------+ |
| 138 | +| :c:func:`PyDict_GetItemWithError` | :c:func:`PyDict_GetItemRef` | |
| 139 | ++-----------------------------------+-----------------------------------+ |
| 140 | +| :c:func:`PyDict_GetItemString` | :c:func:`PyDict_GetItemStringRef` | |
| 141 | ++-----------------------------------+-----------------------------------+ |
| 142 | +| :c:func:`PyDict_SetDefault` | :c:func:`PyDict_SetDefaultRef` | |
| 143 | ++-----------------------------------+-----------------------------------+ |
| 144 | +| :c:func:`PyDict_Next` | no direct replacement | |
| 145 | ++-----------------------------------+-----------------------------------+ |
| 146 | +| :c:func:`PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | |
| 147 | ++-----------------------------------+-----------------------------------+ |
| 148 | +| :c:func:`PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | |
| 149 | ++-----------------------------------+-----------------------------------+ |
| 150 | +| :c:func:`PyImport_AddModule` | :c:func:`PyImport_AddModuleRef` | |
| 151 | ++-----------------------------------+-----------------------------------+ |
| 152 | + |
| 153 | +Not all APIs that return borrowed references are problematic. For |
| 154 | +example, :c:func:`PyTuple_GetItem` is safe because tuples are immutable. |
| 155 | +Similarly, not all uses of the above APIs are problematic. For example, |
| 156 | +:c:func:`PyDict_GetItem` is often used for parsing keyword argument |
| 157 | +dictionaries in function calls; those keyword argument dictionaries are |
| 158 | +effectively private (not accessible by other threads), so using borrowed |
| 159 | +references in that context is safe. |
| 160 | + |
| 161 | +Some of these functions were added in Python 3.13. You can use the |
| 162 | +`pythoncapi-compat <https://github.com/python/pythoncapi-compat>`_ package |
| 163 | +to provide implementations of these functions for older Python versions. |
| 164 | + |
| 165 | + |
| 166 | +Memory Allocation APIs |
| 167 | +====================== |
| 168 | + |
| 169 | +Python's memory management C API provides functions in three different |
| 170 | +:ref:`allocation domains <allocator-domains>`: "raw", "mem", and "object". |
| 171 | +For thread-safety, the free-threaded build requires that only Python objects |
| 172 | +are allocated using the object domain, and that all Python object are |
| 173 | +allocated using that domain. This differes from the prior Python versions, |
| 174 | +where this was only a best practice and not a hard requirement. |
| 175 | + |
| 176 | +.. note:: |
| 177 | + |
| 178 | + Search for uses of :c:func:`PyObject_Malloc` in your |
| 179 | + extension and check that the allocated memory is used for Python objects. |
| 180 | + Use :c:func:`PyMem_Malloc` to allocate buffers instead of |
| 181 | + :c:func:`PyObject_Malloc`. |
| 182 | + |
| 183 | + |
| 184 | +Thread State and GIL APIs |
| 185 | +========================= |
| 186 | + |
| 187 | +Python provides a set of functions and macros to manage thread state and the |
| 188 | +GIL, such as: |
| 189 | + |
| 190 | +* :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release` |
| 191 | +* :c:func:`PyEval_SaveThread` and :c:func:`PyEval_RestoreThread` |
| 192 | +* :c:macro:`Py_BEGIN_ALLOW_THREADS` and :c:macro:`Py_END_ALLOW_THREADS` |
| 193 | + |
| 194 | +These functions should still be used in the free-threaded build to manage |
| 195 | +thread state even when the :term:`GIL` is disabled. For example, if you |
| 196 | +create a thread outside of Python, you must call :c:func:`PyGILState_Ensure` |
| 197 | +before calling into the Python API to ensure that the thread has a valid |
| 198 | +Python thread state. |
| 199 | + |
| 200 | +You should continue to call :c:func:`PyEval_SaveThread` or |
| 201 | +:c:macro:`Py_BEGIN_ALLOW_THREADS` around blocking operations, such as I/O or |
| 202 | +lock acquisitions, to allow other threads to run the |
| 203 | +:term:`cyclic garbage collector <garbage collection>`. |
| 204 | + |
| 205 | + |
| 206 | +Protecting Internal Extension State |
| 207 | +=================================== |
| 208 | + |
| 209 | +Your extension may have internal state that was previously protected by the |
| 210 | +GIL. You may need to add locking to protect this state. The approach will |
| 211 | +depend on your extension, but some common patterns include: |
| 212 | + |
| 213 | +* **Caches**: global caches are a common source of shared state. Consider |
| 214 | + using a lock to protect the cache or disabling it in the free-threaded build |
| 215 | + if the cache is not critical for performance. |
| 216 | +* **Global State**: global state may need to be protected by a lock or moved |
| 217 | + to thread local storage. C11 and C++11 provide the ``thread_local`` or |
| 218 | + ``_Thread_local`` for |
| 219 | + `thread-local storage <https://en.cppreference.com/w/c/language/storage_duration>`_. |
| 220 | + |
| 221 | + |
| 222 | +Building Extensions for the Free-Threaded Build |
| 223 | +=============================================== |
| 224 | + |
| 225 | +C API extensions need to be built specifically for the free-threaded build. |
| 226 | +The wheels, shared libraries, and binaries are indicated by a ``t`` suffix. |
| 227 | + |
| 228 | +* `pypa/manylinux <https://github.com/pypa/manylinux>`_ supports the |
| 229 | + free-threaded build, with the ``t`` suffix, such as ``python3.13t``. |
| 230 | +* `pypa/cibuildwheel <https://github.com/pypa/cibuildwheel>`_ supports the |
| 231 | + free-threaded build if you set |
| 232 | + `CIBW_FREE_THREADED_SUPPORT <https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support>`_. |
| 233 | + |
| 234 | +Limited C API and Stable ABI |
| 235 | +............................ |
| 236 | + |
| 237 | +The free-threaded build does not currently support the |
| 238 | +:ref:`Limited C API <limited-c-api>` or the stable ABI. If you use |
| 239 | +`setuptools <https://setuptools.pypa.io/en/latest/setuptools.html>`_ to build |
| 240 | +your extension and currently set ``py_limited_api=True`` you can use |
| 241 | +``py_limited_api=not sysconfig.get_config_var("Py_GIL_DISABLED")`` to opt out |
| 242 | +of the limited API when building with the free-threaded build. |
| 243 | + |
| 244 | +.. note:: |
| 245 | + You will need to build separate wheels specifically for the free-threaded |
| 246 | + build. If you currently use the stable ABI, you can continue to build a |
| 247 | + single wheel for multiple non-free-threaded Python versions. |
| 248 | + |
| 249 | + |
| 250 | +Windows |
| 251 | +....... |
| 252 | + |
| 253 | +Due to a limitation of the official Windows installer, you will need to |
| 254 | +manually define ``Py_GIL_DISABLED=1`` when building extensions from source. |
0 commit comments