gh-119525: Fix deadlock with `_PyType_Lookup` and the GIL (#119527)

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`.
This commit is contained in:
Sam Gross 2024-05-29 15:26:04 -04:00 committed by GitHub
parent df93f5d4bf
commit c22323cd1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 9 additions and 4 deletions

View File

@ -0,0 +1,2 @@
Fix deadlock involving ``_PyType_Lookup()`` cache in the free-threaded build
when the GIL is dynamically enabled at runtime.

View File

@ -5169,7 +5169,7 @@ is_dunder_name(PyObject *name)
return 0; return 0;
} }
static void static PyObject *
update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int version_tag, PyObject *value) update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int version_tag, PyObject *value)
{ {
_Py_atomic_store_uint32_relaxed(&entry->version, version_tag); _Py_atomic_store_uint32_relaxed(&entry->version, version_tag);
@ -5180,7 +5180,7 @@ update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int versio
// exact unicode object or Py_None so it's safe to do so. // exact unicode object or Py_None so it's safe to do so.
PyObject *old_name = entry->name; PyObject *old_name = entry->name;
_Py_atomic_store_ptr_relaxed(&entry->name, Py_NewRef(name)); _Py_atomic_store_ptr_relaxed(&entry->name, Py_NewRef(name));
Py_DECREF(old_name); return old_name;
} }
#if Py_GIL_DISABLED #if Py_GIL_DISABLED
@ -5200,10 +5200,12 @@ update_cache_gil_disabled(struct type_cache_entry *entry, PyObject *name,
return; return;
} }
update_cache(entry, name, version_tag, value); PyObject *old_value = update_cache(entry, name, version_tag, value);
// Then update sequence to the next valid value // Then update sequence to the next valid value
_PySeqLock_UnlockWrite(&entry->sequence); _PySeqLock_UnlockWrite(&entry->sequence);
Py_DECREF(old_value);
} }
#endif #endif
@ -5315,7 +5317,8 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
#if Py_GIL_DISABLED #if Py_GIL_DISABLED
update_cache_gil_disabled(entry, name, version, res); update_cache_gil_disabled(entry, name, version, res);
#else #else
update_cache(entry, name, version, res); PyObject *old_value = update_cache(entry, name, version, res);
Py_DECREF(old_value);
#endif #endif
} }
return res; return res;