Skip to content

Commit 2a3ff7f

Browse files
Merge branch 'main' into fix-apple-llvm
2 parents e29673e + dd88e77 commit 2a3ff7f

File tree

4 files changed

+222
-10
lines changed

4 files changed

+222
-10
lines changed

Doc/c-api/dict.rst

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ Dictionary objects
7676
7777
The first argument can be a :class:`dict` or a :class:`frozendict`.
7878
79+
.. note::
80+
81+
The operation is atomic on :term:`free threading <free-threaded build>`
82+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
83+
7984
.. versionchanged:: 3.15
8085
Also accept :class:`frozendict`.
8186
@@ -105,6 +110,11 @@ Dictionary objects
105110
``0`` on success or ``-1`` on failure. This function *does not* steal a
106111
reference to *val*.
107112
113+
.. note::
114+
115+
The operation is atomic on :term:`free threading <free-threaded build>`
116+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
117+
108118
109119
.. c:function:: int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val)
110120
@@ -120,6 +130,11 @@ Dictionary objects
120130
If *key* is not in the dictionary, :exc:`KeyError` is raised.
121131
Return ``0`` on success or ``-1`` on failure.
122132
133+
.. note::
134+
135+
The operation is atomic on :term:`free threading <free-threaded build>`
136+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
137+
123138
124139
.. c:function:: int PyDict_DelItemString(PyObject *p, const char *key)
125140
@@ -140,6 +155,11 @@ Dictionary objects
140155
141156
The first argument can be a :class:`dict` or a :class:`frozendict`.
142157
158+
.. note::
159+
160+
The operation is atomic on :term:`free threading <free-threaded build>`
161+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
162+
143163
.. versionadded:: 3.13
144164
145165
.. versionchanged:: 3.15
@@ -162,6 +182,13 @@ Dictionary objects
162182
:meth:`~object.__eq__` methods are silently ignored.
163183
Prefer the :c:func:`PyDict_GetItemWithError` function instead.
164184
185+
.. note::
186+
187+
In the :term:`free-threaded build`, the returned
188+
:term:`borrowed reference` may become invalid if another thread modifies
189+
the dictionary concurrently. Prefer :c:func:`PyDict_GetItemRef`, which
190+
returns a :term:`strong reference`.
191+
165192
.. versionchanged:: 3.10
166193
Calling this API without an :term:`attached thread state` had been allowed for historical
167194
reason. It is no longer allowed.
@@ -177,6 +204,13 @@ Dictionary objects
177204
occurred. Return ``NULL`` **without** an exception set if the key
178205
wasn't present.
179206
207+
.. note::
208+
209+
In the :term:`free-threaded build`, the returned
210+
:term:`borrowed reference` may become invalid if another thread modifies
211+
the dictionary concurrently. Prefer :c:func:`PyDict_GetItemRef`, which
212+
returns a :term:`strong reference`.
213+
180214
.. versionchanged:: 3.15
181215
Also accept :class:`frozendict`.
182216
@@ -195,6 +229,13 @@ Dictionary objects
195229
Prefer using the :c:func:`PyDict_GetItemWithError` function with your own
196230
:c:func:`PyUnicode_FromString` *key* instead.
197231
232+
.. note::
233+
234+
In the :term:`free-threaded build`, the returned
235+
:term:`borrowed reference` may become invalid if another thread modifies
236+
the dictionary concurrently. Prefer :c:func:`PyDict_GetItemStringRef`,
237+
which returns a :term:`strong reference`.
238+
198239
.. versionchanged:: 3.15
199240
Also accept :class:`frozendict`.
200241
@@ -221,6 +262,14 @@ Dictionary objects
221262
222263
.. versionadded:: 3.4
223264
265+
.. note::
266+
267+
In the :term:`free-threaded build`, the returned
268+
:term:`borrowed reference` may become invalid if another thread modifies
269+
the dictionary concurrently. Prefer :c:func:`PyDict_SetDefaultRef`,
270+
which returns a :term:`strong reference`.
271+
272+
224273
225274
.. c:function:: int PyDict_SetDefaultRef(PyObject *p, PyObject *key, PyObject *default_value, PyObject **result)
226275
@@ -240,6 +289,11 @@ Dictionary objects
240289
These may refer to the same object: in that case you hold two separate
241290
references to it.
242291
292+
.. note::
293+
294+
The operation is atomic on :term:`free threading <free-threaded build>`
295+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
296+
243297
.. versionadded:: 3.13
244298
245299
@@ -257,6 +311,11 @@ Dictionary objects
257311
Similar to :meth:`dict.pop`, but without the default value and
258312
not raising :exc:`KeyError` if the key is missing.
259313
314+
.. note::
315+
316+
The operation is atomic on :term:`free threading <free-threaded build>`
317+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
318+
260319
.. versionadded:: 3.13
261320
262321
@@ -403,6 +462,13 @@ Dictionary objects
403462
only be added if there is not a matching key in *a*. Return ``0`` on
404463
success or ``-1`` if an exception was raised.
405464
465+
.. note::
466+
467+
In the :term:`free-threaded build`, when *b* is a
468+
:class:`dict` (with the standard iterator), both *a* and *b* are locked
469+
for the duration of the operation. When *b* is a non-dict mapping, only
470+
*a* is locked; *b* may be concurrently modified by another thread.
471+
406472
407473
.. c:function:: int PyDict_Update(PyObject *a, PyObject *b)
408474
@@ -412,6 +478,13 @@ Dictionary objects
412478
argument has no "keys" attribute. Return ``0`` on success or ``-1`` if an
413479
exception was raised.
414480
481+
.. note::
482+
483+
In the :term:`free-threaded build`, when *b* is a
484+
:class:`dict` (with the standard iterator), both *a* and *b* are locked
485+
for the duration of the operation. When *b* is a non-dict mapping, only
486+
*a* is locked; *b* may be concurrently modified by another thread.
487+
415488
416489
.. c:function:: int PyDict_MergeFromSeq2(PyObject *a, PyObject *seq2, int override)
417490
@@ -427,13 +500,27 @@ Dictionary objects
427500
if override or key not in a:
428501
a[key] = value
429502
503+
.. note::
504+
505+
In the :term:`free-threaded <free threading>` build, only *a* is locked.
506+
The iteration over *seq2* is not synchronized; *seq2* may be concurrently
507+
modified by another thread.
508+
509+
430510
.. c:function:: int PyDict_AddWatcher(PyDict_WatchCallback callback)
431511
432512
Register *callback* as a dictionary watcher. Return a non-negative integer
433513
id which must be passed to future calls to :c:func:`PyDict_Watch`. In case
434514
of error (e.g. no more watcher IDs available), return ``-1`` and set an
435515
exception.
436516
517+
.. note::
518+
519+
This function is not internally synchronized. In the
520+
:term:`free-threaded <free threading>` build, callers should ensure no
521+
concurrent calls to :c:func:`PyDict_AddWatcher` or
522+
:c:func:`PyDict_ClearWatcher` are in progress.
523+
437524
.. versionadded:: 3.12
438525
439526
.. c:function:: int PyDict_ClearWatcher(int watcher_id)
@@ -442,6 +529,13 @@ Dictionary objects
442529
:c:func:`PyDict_AddWatcher`. Return ``0`` on success, ``-1`` on error (e.g.
443530
if the given *watcher_id* was never registered.)
444531
532+
.. note::
533+
534+
This function is not internally synchronized. In the
535+
:term:`free-threaded <free threading>` build, callers should ensure no
536+
concurrent calls to :c:func:`PyDict_AddWatcher` or
537+
:c:func:`PyDict_ClearWatcher` are in progress.
538+
445539
.. versionadded:: 3.12
446540
447541
.. c:function:: int PyDict_Watch(int watcher_id, PyObject *dict)

Doc/c-api/set.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ the constructor functions work with any iterable Python object.
8989
actually iterable. The constructor is also useful for copying a set
9090
(``c=set(s)``).
9191
92+
.. note::
93+
94+
The operation is atomic on :term:`free threading <free-threaded build>`
95+
when *iterable* is a :class:`set`, :class:`frozenset`, :class:`dict` or :class:`frozendict`.
96+
9297
9398
.. c:function:: PyObject* PyFrozenSet_New(PyObject *iterable)
9499
@@ -97,6 +102,11 @@ the constructor functions work with any iterable Python object.
97102
set on success or ``NULL`` on failure. Raise :exc:`TypeError` if *iterable* is
98103
not actually iterable.
99104
105+
.. note::
106+
107+
The operation is atomic on :term:`free threading <free-threaded build>`
108+
when *iterable* is a :class:`set`, :class:`frozenset`, :class:`dict` or :class:`frozendict`.
109+
100110
101111
The following functions and macros are available for instances of :class:`set`
102112
or :class:`frozenset` or instances of their subtypes.
@@ -124,6 +134,10 @@ or :class:`frozenset` or instances of their subtypes.
124134
the *key* is unhashable. Raise :exc:`SystemError` if *anyset* is not a
125135
:class:`set`, :class:`frozenset`, or an instance of a subtype.
126136
137+
.. note::
138+
139+
The operation is atomic on :term:`free threading <free-threaded build>`
140+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
127141
128142
.. c:function:: int PySet_Add(PyObject *set, PyObject *key)
129143
@@ -135,6 +149,12 @@ or :class:`frozenset` or instances of their subtypes.
135149
:exc:`SystemError` if *set* is not an instance of :class:`set` or its
136150
subtype.
137151
152+
.. note::
153+
154+
The operation is atomic on :term:`free threading <free-threaded build>`
155+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
156+
157+
138158
139159
The following functions are available for instances of :class:`set` or its
140160
subtypes but not for instances of :class:`frozenset` or its subtypes.
@@ -149,6 +169,11 @@ subtypes but not for instances of :class:`frozenset` or its subtypes.
149169
temporary frozensets. Raise :exc:`SystemError` if *set* is not an
150170
instance of :class:`set` or its subtype.
151171
172+
.. note::
173+
174+
The operation is atomic on :term:`free threading <free-threaded build>`
175+
when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`.
176+
152177
153178
.. c:function:: PyObject* PySet_Pop(PyObject *set)
154179
@@ -164,6 +189,12 @@ subtypes but not for instances of :class:`frozenset` or its subtypes.
164189
success. Return ``-1`` and raise :exc:`SystemError` if *set* is not an instance of
165190
:class:`set` or its subtype.
166191
192+
.. note::
193+
194+
In the :term:`free-threaded build`, the set is emptied before its entries
195+
are cleared, so other threads will observe an empty set rather than
196+
intermediate states.
197+
167198
168199
Deprecated API
169200
^^^^^^^^^^^^^^

Doc/data/threadsafety.dat

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,71 @@
1414
# The function name must match the C domain identifier used in the documentation.
1515

1616
# Synchronization primitives (Doc/c-api/synchronization.rst)
17-
PyMutex_Lock:shared:
18-
PyMutex_Unlock:shared:
17+
PyMutex_Lock:atomic:
18+
PyMutex_Unlock:atomic:
1919
PyMutex_IsLocked:atomic:
2020

21+
22+
# Dictionary objects (Doc/c-api/dict.rst)
23+
24+
# Type checks - read ob_type pointer, always safe
25+
PyDict_Check:atomic:
26+
PyDict_CheckExact:atomic:
27+
28+
# Creation - pure allocation, no shared state
29+
PyDict_New:atomic:
30+
31+
# Lock-free lookups - use _Py_dict_lookup_threadsafe(), no locking.
32+
# Atomic with simple types.
33+
PyDict_Contains:shared:
34+
PyDict_ContainsString:atomic:
35+
PyDict_GetItemRef:shared:
36+
PyDict_GetItemStringRef:atomic:
37+
PyDict_Size:atomic:
38+
PyDict_GET_SIZE:atomic:
39+
40+
# Borrowed-reference lookups - lock-free dict access but returned
41+
# borrowed reference is unsafe in free-threaded builds without
42+
# external synchronization
43+
PyDict_GetItem:compatible:
44+
PyDict_GetItemWithError:compatible:
45+
PyDict_GetItemString:compatible:
46+
PyDict_SetDefault:compatible:
47+
48+
# Iteration - no locking; returns borrowed refs
49+
PyDict_Next:compatible:
50+
51+
# Single-item mutations - protected by per-object critical section
52+
PyDict_SetItem:shared:
53+
PyDict_SetItemString:atomic:
54+
PyDict_DelItem:shared:
55+
PyDict_DelItemString:atomic:
56+
PyDict_SetDefaultRef:shared:
57+
PyDict_Pop:shared:
58+
PyDict_PopString:atomic:
59+
60+
# Bulk reads - hold per-object lock for duration
61+
PyDict_Clear:atomic:
62+
PyDict_Copy:atomic:
63+
PyDict_Keys:atomic:
64+
PyDict_Values:atomic:
65+
PyDict_Items:atomic:
66+
67+
# Merge/update - lock target dict; also lock source when it is a dict
68+
PyDict_Update:shared:
69+
PyDict_Merge:shared:
70+
PyDict_MergeFromSeq2:shared:
71+
72+
# Watcher registration - no synchronization on interpreter state
73+
PyDict_AddWatcher:compatible:
74+
PyDict_ClearWatcher:compatible:
75+
76+
# Per-dict watcher tags - non-atomic RMW on _ma_watcher_tag;
77+
# safe on distinct dicts only
78+
PyDict_Watch:distinct:
79+
PyDict_Unwatch:distinct:
80+
81+
2182
# List objects (Doc/c-api/list.rst)
2283

2384
# Type checks - read ob_type pointer, always safe
@@ -125,6 +186,29 @@ PyByteArray_GET_SIZE:atomic:
125186
PyByteArray_AsString:compatible:
126187
PyByteArray_AS_STRING:compatible:
127188

189+
# Creation - may iterate the iterable argument, calling arbitrary code.
190+
# Atomic for sets, frozensets, dicts, and frozendicts.
191+
PySet_New:shared:
192+
PyFrozenSet_New:shared:
193+
194+
# Size - uses atomic load on free-threaded builds
195+
PySet_Size:atomic:
196+
PySet_GET_SIZE:atomic:
197+
198+
# Contains - lock-free, atomic with simple types
199+
PySet_Contains:shared:
200+
201+
# Mutations - hold per-object lock for duration
202+
# atomic with simple types
203+
PySet_Add:shared:
204+
PySet_Discard:shared:
205+
206+
# Pop - hold per-object lock for duration
207+
PySet_Pop:atomic:
208+
209+
# Clear - empties the set before clearing
210+
PySet_Clear:atomic:
211+
128212
# Capsule objects (Doc/c-api/capsule.rst)
129213

130214
# Type check - read ob_type pointer, always safe

0 commit comments

Comments
 (0)