Skip to content

Commit f1776aa

Browse files
committed
gh-112069: Make _PySet_NextEntry to be thread-safe.
1 parent 1aa8bbe commit f1776aa

File tree

8 files changed

+58
-15
lines changed

8 files changed

+58
-15
lines changed

Include/internal/pycore_setobject.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ PyAPI_FUNC(int) _PySet_NextEntry(
1515
PyObject **key,
1616
Py_hash_t *hash);
1717

18+
// Export for 'Python/compile.c'
19+
PyAPI_FUNC(int) _PyFrozenSet_NextEntry(
20+
PyObject *set,
21+
Py_ssize_t *pos,
22+
PyObject **key,
23+
Py_hash_t *hash);
24+
1825
// Export for '_pickle' shared extension
1926
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
2027

Modules/_abc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -862,15 +862,15 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
862862

863863
// Make a local copy of the registry to protect against concurrent
864864
// modifications of _abc_registry.
865-
PyObject *registry = PySet_New(registry_shared);
865+
PyObject *registry = PyFrozenSet_New(registry_shared);
866866
if (registry == NULL) {
867867
return -1;
868868
}
869869
PyObject *key;
870870
Py_ssize_t pos = 0;
871871
Py_hash_t hash;
872872

873-
while (_PySet_NextEntry(registry, &pos, &key, &hash)) {
873+
while (_PyFrozenSet_NextEntry(registry, &pos, &key, &hash)) {
874874
PyObject *rkey;
875875
if (PyWeakref_GetRef(key, &rkey) < 0) {
876876
// Someone inject non-weakref type in the registry.

Objects/codeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2347,7 +2347,7 @@ _PyCode_ConstantKey(PyObject *op)
23472347
return NULL;
23482348

23492349
i = 0;
2350-
while (_PySet_NextEntry(op, &pos, &item, &hash)) {
2350+
while (_PyFrozenSet_NextEntry(op, &pos, &item, &hash)) {
23512351
PyObject *item_key;
23522352

23532353
item_key = _PyCode_ConstantKey(item);

Objects/dictobject.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2979,7 +2979,15 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
29792979
return NULL;
29802980
}
29812981

2982-
while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
2982+
int (*next_entry_ptr)(PyObject *, Py_ssize_t *, PyObject **, Py_hash_t *);
2983+
if (PyFrozenSet_CheckExact(iterable)) {
2984+
next_entry_ptr = &_PyFrozenSet_NextEntry;
2985+
}
2986+
else {
2987+
next_entry_ptr = &_PySet_NextEntry;
2988+
}
2989+
2990+
while ((*next_entry_ptr)(iterable, &pos, &key, &hash)) {
29832991
if (insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value))) {
29842992
Py_DECREF(mp);
29852993
return NULL;

Objects/listobject.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,12 +1282,19 @@ list_extend_set(PyListObject *self, PySetObject *other)
12821282
if (list_resize(self, m + n) < 0) {
12831283
return -1;
12841284
}
1285+
int (*next_entry_ptr)(PyObject *, Py_ssize_t *, PyObject **, Py_hash_t *);
1286+
if (PyFrozenSet_CheckExact(other)) {
1287+
next_entry_ptr = &_PyFrozenSet_NextEntry;
1288+
}
1289+
else {
1290+
next_entry_ptr = &_PySet_NextEntry;
1291+
}
12851292
/* populate the end of self with iterable's items */
12861293
Py_ssize_t setpos = 0;
12871294
Py_hash_t hash;
12881295
PyObject *key;
12891296
PyObject **dest = self->ob_item + m;
1290-
while (_PySet_NextEntry((PyObject *)other, &setpos, &key, &hash)) {
1297+
while ((*next_entry_ptr)((PyObject *)other, &setpos, &key, &hash)) {
12911298
Py_INCREF(key);
12921299
FT_ATOMIC_STORE_PTR_RELEASE(*dest, key);
12931300
dest++;

Objects/setobject.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ set_clear_internal(PySetObject *so)
473473
* while (set_next(yourset, &pos, &entry)) {
474474
* Refer to borrowed reference in entry->key.
475475
* }
476-
*
476+
*f
477477
* CAUTION: In general, it isn't safe to use set_next in a loop that
478478
* mutates the table.
479479
*/
@@ -2661,21 +2661,41 @@ PySet_Add(PyObject *anyset, PyObject *key)
26612661
return rv;
26622662
}
26632663

2664-
// TODO: Make thread-safe in free-threaded builds
26652664
int
2666-
_PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
2665+
_PySet_NextEntry_lock_held(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
26672666
{
26682667
setentry *entry;
2668+
int ret = set_next((PySetObject *)set, pos, &entry);
2669+
if (ret == 0) {
2670+
return 0;
2671+
}
2672+
*key = entry->key;
2673+
*hash = entry->hash;
2674+
return 1;
2675+
}
26692676

2677+
int
2678+
_PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
2679+
{
26702680
if (!PyAnySet_Check(set)) {
26712681
PyErr_BadInternalCall();
26722682
return -1;
26732683
}
2674-
if (set_next((PySetObject *)set, pos, &entry) == 0)
2675-
return 0;
2676-
*key = entry->key;
2677-
*hash = entry->hash;
2678-
return 1;
2684+
int ret;
2685+
Py_BEGIN_CRITICAL_SECTION(set);
2686+
ret = _PySet_NextEntry_lock_held(set, pos, key, hash);
2687+
Py_END_CRITICAL_SECTION();
2688+
return ret;
2689+
}
2690+
2691+
int
2692+
_PyFrozenSet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
2693+
{
2694+
if (!PyFrozenSet_CheckExact(set)) {
2695+
PyErr_BadInternalCall();
2696+
return -1;
2697+
}
2698+
return _PySet_NextEntry_lock_held(set, pos, key, hash);
26792699
}
26802700

26812701
PyObject *

Python/compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ merge_consts_recursive(PyObject *const_cache, PyObject *o)
920920
Py_ssize_t i = 0, pos = 0;
921921
PyObject *item;
922922
Py_hash_t hash;
923-
while (_PySet_NextEntry(o, &pos, &item, &hash)) {
923+
while (_PyFrozenSet_NextEntry(o, &pos, &item, &hash)) {
924924
PyObject *k = merge_consts_recursive(const_cache, item);
925925
if (k == NULL) {
926926
Py_DECREF(tuple);

Python/pylifecycle.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2910,7 +2910,8 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp)
29102910
Py_ssize_t i = 0;
29112911
PyObject *item;
29122912
Py_hash_t hash;
2913-
while (_PySet_NextEntry(stdlib_module_names, &i, &item, &hash)) {
2913+
// if stdlib_module_names is not NULL, it is always a frozenset.
2914+
while (_PyFrozenSet_NextEntry(stdlib_module_names, &i, &item, &hash)) {
29142915
if (PyUnicode_Check(item)
29152916
&& PyUnicode_Compare(key, item) == 0)
29162917
{

0 commit comments

Comments
 (0)