Skip to content

Commit 0fc508e

Browse files
Fix a data race when iterating a shared types.GenericAlias iterator
1 parent adebb68 commit 0fc508e

3 files changed

Lines changed: 43 additions & 3 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import threading
2+
import unittest
3+
4+
from test.support import threading_helper
5+
6+
7+
NTHREADS = 10
8+
9+
10+
@threading_helper.requires_working_threading()
11+
class TestGenericAlias(unittest.TestCase):
12+
def worker(self, it, barrier):
13+
barrier.wait()
14+
try:
15+
next(it)
16+
except StopIteration:
17+
pass
18+
19+
def test_shared_generic_alias_iter(self):
20+
# See https://github.com/python/cpython/issues/154043
21+
bar = threading.Barrier(NTHREADS)
22+
number_of_iterations = 50
23+
for _ in range(number_of_iterations):
24+
shared = iter(list[int])
25+
threading_helper.run_concurrently(
26+
worker_func=self.worker, nthreads=NTHREADS, args=(shared, bar)
27+
)
28+
29+
30+
if __name__ == "__main__":
31+
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a data race when iterating a shared :class:`types.GenericAlias` iterator
2+
from multiple threads under the :term:`free-threaded build`.

Objects/genericaliasobject.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,17 +939,24 @@ static PyObject *
939939
ga_iternext(PyObject *op)
940940
{
941941
gaiterobject *gi = (gaiterobject*)op;
942-
if (gi->obj == NULL) {
942+
#ifdef Py_GIL_DISABLED
943+
PyObject *obj = _Py_atomic_exchange_ptr(&gi->obj, NULL);
944+
#else
945+
PyObject* obj = gi->obj;
946+
gi->obj = NULL;
947+
#endif
948+
if (obj == NULL) {
943949
PyErr_SetNone(PyExc_StopIteration);
944950
return NULL;
945951
}
946-
gaobject *alias = (gaobject *)gi->obj;
952+
gaobject *alias = (gaobject *)obj;
947953
PyObject *starred_alias = Py_GenericAlias(alias->origin, alias->args);
948954
if (starred_alias == NULL) {
955+
Py_DECREF(obj);
949956
return NULL;
950957
}
951958
((gaobject *)starred_alias)->starred = true;
952-
Py_SETREF(gi->obj, NULL);
959+
Py_SETREF(obj, NULL);
953960
return starred_alias;
954961
}
955962

0 commit comments

Comments
 (0)