Bug report
Bug description:
In the free-threaded build, reading __parameters__ on a shared types.GenericAlias (e.g. list[T]) from multiple threads races on the lazy creation of the cached parameters tuple.
ga_parameters checks and fills alias->parameters with plain accesses.
| staticPyObject* |
| ga_parameters(PyObject*self, void*unused) |
| { |
| gaobject*alias= (gaobject*)self; |
| if (alias->parameters==NULL) { |
| alias->parameters=_Py_make_parameters(alias->args); |
| if (alias->parameters==NULL) { |
| returnNULL; |
| } |
| } |
| returnPy_NewRef(alias->parameters); |
Two threads that both observe parameters == NULL both compute and store the tuple, racing on the cache pointer.
Reproducer:
fromthreadingimportThreadfromtypingimportTypeVarT=TypeVar('T')
slot= [list[T]]
defthread1():
for_inrange(20000):
try:
_=slot[0].__parameters__exceptException:
passdefthread2():
for_inrange(20000):
slot[0] =list[T] # fresh GenericAlias (parameters == NULL)threads= [Thread(target=thread1) for_inrange(6)]
threads+= [Thread(target=thread2) for_inrange(2)]
fortinthreads: t.start()
fortinthreads: t.join()TSAN Report:
==================
WARNING: ThreadSanitizer: data race (pid=48591)
Write of size 8 at 0x7fffd00d0580 by thread T4:
#0 ga_parameters /cpython/Objects/genericaliasobject.c:848:27 #1 getset_get /cpython/Objects/descrobject.c:194:16 #2 _PyObject_GenericGetAttrWithDict /cpython/Objects/object.c:1926:19 #3 PyObject_GenericGetAttr /cpython/Objects/object.c:2012:12 #4 ga_getattro /cpython/Objects/genericaliasobject.c:708:12 #5 _PyObject_GetAttrStackRef /cpython/Objects/object.c:1369:18 #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:8786:28 ...
Previous read of size 8 at 0x7fffd00d0580 by thread T3:
#0 ga_parameters /cpython/Objects/genericaliasobject.c:847:16 #1 getset_get /cpython/Objects/descrobject.c:194:16 #2 _PyObject_GenericGetAttrWithDict /cpython/Objects/object.c:1926:19 #3 PyObject_GenericGetAttr /cpython/Objects/object.c:2012:12 #4 ga_getattro /cpython/Objects/genericaliasobject.c:708:12 #5 _PyObject_GetAttrStackRef /cpython/Objects/object.c:1369:18 #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:8786:28 ...
SUMMARY: ThreadSanitizer: data race /cpython/Objects/genericaliasobject.c:848:27 in ga_parameters
==================
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Bug report
Bug description:
In the free-threaded build, reading
__parameters__on a sharedtypes.GenericAlias(e.g.list[T]) from multiple threads races on the lazy creation of the cachedparameterstuple.ga_parameterschecks and fillsalias->parameterswith plain accesses.cpython/Objects/genericaliasobject.c
Lines 843 to 853 in a74280e
Two threads that both observe
parameters == NULLboth compute and store the tuple, racing on the cache pointer.Reproducer:
TSAN Report:
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
GenericAliasparameter initialization on FT #153318GenericAliasparameter initialization on FT (GH-153318) #153345GenericAliasparameter initialization on FT (GH-153318) #153346