diff --git a/Lib/test/test_free_threading/test_types.py b/Lib/test/test_free_threading/test_types.py new file mode 100644 index 000000000000000..76fcf1590122f54 --- /dev/null +++ b/Lib/test/test_free_threading/test_types.py @@ -0,0 +1,33 @@ +import unittest +from typing import TypeVar +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) + + +class TestGenericAlias(unittest.TestCase): + def test_parameters_race(self): + # gh-153298 + + T = TypeVar('T') + slot = [list[T]] + + def access(): + for _ in range(2000): + try: + _ = slot[0].__parameters__ + except Exception: + pass + + def refresh(): + for _ in range(2000): + slot[0] = list[T] + + threading_helper.run_concurrently([ + *[access for _ in range(6)], + *[refresh for _ in range(2)], + ]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-08-13-23-11.gh-issue-153298.wvcXxN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-08-13-23-11.gh-issue-153298.wvcXxN.rst new file mode 100644 index 000000000000000..38d548834d5e02e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-08-13-23-11.gh-issue-153298.wvcXxN.rst @@ -0,0 +1,2 @@ +Fixes a data race in :class:`types.GenericAlias` ``__parameters__`` +initialization on free-threading builds. diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index c2083e6fcb77f47..4e85927def7ea78 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -841,7 +841,7 @@ static PyMemberDef ga_members[] = { }; static PyObject * -ga_parameters(PyObject *self, void *unused) +ga_parameters_lock_held(PyObject *self) { gaobject *alias = (gaobject *)self; if (alias->parameters == NULL) { @@ -853,6 +853,16 @@ ga_parameters(PyObject *self, void *unused) return Py_NewRef(alias->parameters); } +static PyObject * +ga_parameters(PyObject *self, void *unused) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = ga_parameters_lock_held(self); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyObject * ga_unpacked_tuple_args(PyObject *self, void *unused) {