Skip to content

Commit dcfbd3b

Browse files
[3.13] gh-119525: Fix deadlock with _PyType_Lookup and the GIL (GH-119527) (#119746)
The deadlock only affected the free-threaded build and only occurred when the GIL was enabled at runtime. The `Py_DECREF(old_name)` call might temporarily release the GIL while holding the type seqlock. Another thread may spin trying to acquire the seqlock while holding the GIL. The deadlock occurred roughly 1 in ~1,000 runs of `pool_in_threads.py` from `test_multiprocessing_pool_circular_import`. (cherry picked from commit c22323c) Co-authored-by: Sam Gross <colesbury@gmail.com>
1 parent 40a024c commit dcfbd3b

File tree

2 files changed

+9
-4
lines changed

2 files changed

+9
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix deadlock involving ``_PyType_Lookup()`` cache in the free-threaded build
2+
when the GIL is dynamically enabled at runtime.

Objects/typeobject.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -5062,7 +5062,7 @@ is_dunder_name(PyObject *name)
50625062
return 0;
50635063
}
50645064

5065-
static void
5065+
static PyObject *
50665066
update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int version_tag, PyObject *value)
50675067
{
50685068
_Py_atomic_store_uint32_relaxed(&entry->version, version_tag);
@@ -5073,7 +5073,7 @@ update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int versio
50735073
// exact unicode object or Py_None so it's safe to do so.
50745074
PyObject *old_name = entry->name;
50755075
_Py_atomic_store_ptr_relaxed(&entry->name, Py_NewRef(name));
5076-
Py_DECREF(old_name);
5076+
return old_name;
50775077
}
50785078

50795079
#if Py_GIL_DISABLED
@@ -5093,10 +5093,12 @@ update_cache_gil_disabled(struct type_cache_entry *entry, PyObject *name,
50935093
return;
50945094
}
50955095

5096-
update_cache(entry, name, version_tag, value);
5096+
PyObject *old_value = update_cache(entry, name, version_tag, value);
50975097

50985098
// Then update sequence to the next valid value
50995099
_PySeqLock_UnlockWrite(&entry->sequence);
5100+
5101+
Py_DECREF(old_value);
51005102
}
51015103

51025104
#endif
@@ -5208,7 +5210,8 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
52085210
#if Py_GIL_DISABLED
52095211
update_cache_gil_disabled(entry, name, version, res);
52105212
#else
5211-
update_cache(entry, name, version, res);
5213+
PyObject *old_value = update_cache(entry, name, version, res);
5214+
Py_DECREF(old_value);
52125215
#endif
52135216
}
52145217
return res;

0 commit comments

Comments
 (0)