Skip to content

Commit b547cb3

Browse files
committed
gh-132641: Fix race in lru_cache due to inconsistent critical section use.
The bounded_lru_cache code was using a critical section on the lru cache object to protect dictionary accesses in some code paths, but using the critical section on the dictionary itself to protect accesses in other code paths. This led to races since not all threads agreed on which lock they needed to be holding. Instead, always use a critical section on the underlying dictionary, rather than the lru cache object itself. Fixes #132641
1 parent bbe9c31 commit b547cb3

File tree

2 files changed

+5
-4
lines changed

2 files changed

+5
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a race in functools.lru_cache under free-threading.

Modules/_functoolsmodule.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ static int
12951295
bounded_lru_cache_get_lock_held(lru_cache_object *self, PyObject *args, PyObject *kwds,
12961296
PyObject **result, PyObject **key, Py_hash_t *hash)
12971297
{
1298-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
1298+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self->cache);
12991299
lru_list_elem *link;
13001300

13011301
PyObject *key_ = *key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed);
@@ -1330,7 +1330,7 @@ static PyObject *
13301330
bounded_lru_cache_update_lock_held(lru_cache_object *self,
13311331
PyObject *result, PyObject *key, Py_hash_t hash)
13321332
{
1333-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
1333+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self->cache);
13341334
lru_list_elem *link;
13351335
PyObject *testresult;
13361336
int res;
@@ -1479,7 +1479,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
14791479
Py_hash_t hash;
14801480
int res;
14811481

1482-
Py_BEGIN_CRITICAL_SECTION(self);
1482+
Py_BEGIN_CRITICAL_SECTION(self->cache);
14831483
res = bounded_lru_cache_get_lock_held(self, args, kwds, &result, &key, &hash);
14841484
Py_END_CRITICAL_SECTION();
14851485

@@ -1492,7 +1492,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
14921492

14931493
result = PyObject_Call(self->func, args, kwds);
14941494

1495-
Py_BEGIN_CRITICAL_SECTION(self);
1495+
Py_BEGIN_CRITICAL_SECTION(self->cache);
14961496
/* Note: key will be stolen in the below function, and
14971497
result may be stolen or sometimes re-returned as a passthrough.
14981498
Treat both as being stolen.

0 commit comments

Comments
 (0)