Bug report
The implementation of Python thread local variables (threading.local() or _thread._local) has some thread-safety issues. The issues are particularly relevant to the free-threaded build, but some can affect the default build too.
local_clear loop over threads isn't safe
The local_clear() function is called when a thread local variable is destroyed. It loops over all threads in the interpreter and removes itself from their dictionaries.
|
HEAD_LOCK(runtime); |
|
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); |
|
HEAD_UNLOCK(runtime); |
|
while (tstate) { |
|
if (tstate->dict) { |
|
if (PyDict_Pop(tstate->dict, self->key, NULL) < 0) { |
|
// Silently ignore error |
|
PyErr_Clear(); |
|
} |
|
} |
|
HEAD_LOCK(runtime); |
|
tstate = PyThreadState_Next(tstate); |
|
HEAD_UNLOCK(runtime); |
|
} |
This isn't thread-safe because after HEAD_UNLOCK(runtime), the stored tstate might be deleted concurrently. This can happen even in the default build with the GIL enabled because PyThreadState_Delete() doesn't require the GIL to be held. However, it's less likely to occur in practice because threading module created threads hold onto the GIL until they're deleted.
local_clear access to tstate->dict isn't thread-safe
In the free-threaded build, local_clear() may be run concurrently with some other thread's PyThreadState_Clear(). The access to another thread's tstate->dict isn't safe because it may be getting destroyed concurrently.
Linked PRs
Bug report
The implementation of Python thread local variables (
threading.local()or_thread._local) has some thread-safety issues. The issues are particularly relevant to the free-threaded build, but some can affect the default build too.local_clear loop over threads isn't safe
The
local_clear()function is called when a thread local variable is destroyed. It loops over all threads in the interpreter and removes itself from their dictionaries.cpython/Modules/_threadmodule.c
Lines 1568 to 1581 in fd0f814
This isn't thread-safe because after
HEAD_UNLOCK(runtime), the storedtstatemight be deleted concurrently. This can happen even in the default build with the GIL enabled becausePyThreadState_Delete()doesn't require the GIL to be held. However, it's less likely to occur in practice becausethreadingmodule created threads hold onto the GIL until they're deleted.local_clear access to
tstate->dictisn't thread-safeIn the free-threaded build,
local_clear()may be run concurrently with some other thread'sPyThreadState_Clear(). The access to another thread'ststate->dictisn't safe because it may be getting destroyed concurrently.Linked PRs
threading.local#121655threading.local(GH-121655) #122042