Bug report
Bug description:
In the free-threaded build, rlock_repr reads the recursive lock's owner with plain loads to build the repr string, while acquire/release/_acquire_restore write the owner with atomic stores.
| staticPyObject* |
| rlock_repr(PyObject*op) |
| { |
| rlockobject*self=rlockobject_CAST(op); |
| PyThread_ident_towner=self->lock.thread; |
| intlocked=rlock_locked_impl(self); |
The reader side is not atomic, so a repr() concurrent with acquire() is a data race on the owner field.
Reproducer:
import_threadfromthreadingimportThreadr=_thread.RLock()
defthread1():
for_inrange(20000):
try:
repr(r)
exceptException:
passdefthread2():
for_inrange(20000):
r.acquire()
r.release()
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=40273)
Read of size 8 at 0x7fffb6600f38 by thread T1:
#0 rlock_repr /cpython/./Modules/_threadmodule.c:1291:41 #1 PyObject_Repr /cpython/Objects/object.c:784:11 #2 builtin_repr /cpython/Python/bltinmodule.c:2677:12 #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2712:35
...
Previous atomic write of size 8 at 0x7fffb6600f38 by thread T7:
#0 _Py_atomic_store_ullong_relaxed /cpython/./Include/cpython/pyatomic_gcc.h:518:3 #1 _PyRecursiveMutex_LockTimed /cpython/Python/lock.c:439:9 #2 _thread_RLock_acquire_impl /cpython/./Modules/_threadmodule.c:1102:22 #3 _thread_RLock_acquire /cpython/./Modules/clinic/_threadmodule.c.h:418:20 #4 _PyCallMethodDescriptorFastWithKeywords_StackRef /cpython/Python/ceval.c:883:11 #5 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:4205:35
...
SUMMARY: ThreadSanitizer: data race /cpython/./Modules/_threadmodule.c:1291:41 in rlock_repr
==================
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Bug report
Bug description:
In the free-threaded build,
rlock_reprreads the recursive lock's owner with plain loads to build the repr string, whileacquire/release/_acquire_restorewrite the owner with atomic stores.cpython/Modules/_threadmodule.c
Lines 1287 to 1292 in a74280e
The reader side is not atomic, so a
repr()concurrent withacquire()is a data race on the owner field.Reproducer:
TSAN Report:
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
threading.RLock.__repr__in FT builds #153299threading.RLock.__repr__in FT builds (GH-153299) #153314threading.RLock.__repr__in FT builds (GH-153299) #153315