Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit02b272b

Browse files
gh-119241: Add HOWTO for free-threaded C API extensions (#119877)
Some sections adapted fromhttps://github.com/Quansight-Labs/free-threaded-compatibility/written by Nathan Goldbaum.Co-authored-by: Nathan Goldbaum <nathan.goldbaum@gmail.com>
1 parentdacc5ac commit02b272b

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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.

‎Doc/howto/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Python Library Reference.
3232
isolating-extensions.rst
3333
timerfd.rst
3434
mro.rst
35+
free-threading-extensions.rst
3536

3637
General:
3738

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp