Repository navigation
Data race between compare_generic and insert_combined_dict under free-threading #132641
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Apr 17, 2025 - addedinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
on Apr 17, 2025 I'd like to work on this issue and submit a PR to fix it. Is anyone already working on this, or may I take it? Thank you.
I built main branch and observed the following races under free-threading in cpython 3.14
Which code did you run to trigger these data races?
It is from JAX TSAN CI job running cpu tests suite. I can try to get a small reproducer.
We are still seeing this frequently in our CI.
I'm trying to come up with a reproducer for this and so far haven't succeeded. However, reading the code, I'm curious about the following:
In
bounded_lru_cache_get_lock_heldwe hold a critical section ofself, i.e., the lru cache object itself:cpython/Modules/_functoolsmodule.c
Line 1482 in 6d5a8c2
Py_BEGIN_CRITICAL_SECTION(self);
We then use that critical section to call_PyDict_GetItemRef_KnownHash_LockHeldwith no other locking. In other words, the read part of the cache seems to assume that the critical section on the lru_cache object itself protects the dictionary state.cpython/Modules/_functoolsmodule.c
Line 1309 in 6d5a8c2
int res = _PyDict_GetItemRef_KnownHash_LockHeld((PyDictObject *)self->cache, key_, hash_, However on the write side of the cache, we start by acquiring a critical section on
selfas well (). However in some code paths we callcpython/Modules/_functoolsmodule.c
Line 1500 in 6d5a8c2
result = bounded_lru_cache_update_lock_held(self, result, key, hash); _PyDict_SetItem_KnownHash(e.g.,), which acquires a critical section on the dictionary object (cpython/Modules/_functoolsmodule.c
Line 1386 in 6d5a8c2
if (_PyDict_SetItem_KnownHash(self->cache, key, (PyObject *)link, ), which I think we should think of as implicitly releasing the outer lock.Line 2693 in 6d5a8c2
Py_BEGIN_CRITICAL_SECTION(op); In other words, we are confused about which critical section is protecting the dictionary state. Is it the critical section on the lru cache itself, or it the critical section on the dict it contains?
- added 9 commits that reference this issue
on May 9, 2025
Bug report
Bug description:
I built main branch and observed the following races under free-threading in cpython 3.14 (Python 3.14.0a7+ experimental free-threading build (heads/main:e42bda94411, Apr 17 2025, 14:08:39) [Clang 18.1.3 (1ubuntu1)])
Race 1
Race 2
Full report: https://github.com/proxy/gist.github.com/vfdev-5/cbb9189043737d023b755191b62951cf
cc @hawkinsp
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
lru_cacheunder free-threading #133787lru_cacheunder free-threading (GH-133787) #133979