From e46b077f6abeaf01f48f194df284f1b800962514 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 2 Dec 2024 19:38:26 +0000 Subject: [PATCH] gh-127521: Mark list as "shared" before resizing if necessary (GH-127524) In the free threading build, if a non-owning thread resizes a list, it must use QSBR to free the old list array because there may be a concurrent access (without a lock) from the owning thread. To match the pattern in dictobject.c, we just mark the list as "shared" before resizing if it's from a non-owning thread and not already marked as shared. (cherry picked from commit c7dec02de2ed4baf3cd22ad094350265b52c18af) Co-authored-by: Sam Gross --- Objects/listobject.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Objects/listobject.c b/Objects/listobject.c index dc9df3c3614fb4..89abbda155a8b6 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -77,6 +77,20 @@ free_list_items(PyObject** items, bool use_qsbr) #endif } +static void +ensure_shared_on_resize(PyListObject *self) +{ +#ifdef Py_GIL_DISABLED + // Ensure that the list array is freed using QSBR if we are not the + // owning thread. + if (!_Py_IsOwnedByCurrentThread((PyObject *)self) && + !_PyObject_GC_IS_SHARED(self)) + { + _PyObject_GC_SET_SHARED(self); + } +#endif +} + /* Ensure ob_item has room for at least newsize elements, and set * ob_size to newsize. If newsize > ob_size on entry, the content * of the new slots at exit is undefined heap trash; it's the caller's @@ -126,6 +140,8 @@ list_resize(PyListObject *self, Py_ssize_t newsize) if (newsize == 0) new_allocated = 0; + ensure_shared_on_resize(self); + #ifdef Py_GIL_DISABLED _PyListArray *array = list_allocate_array(new_allocated); if (array == NULL) { @@ -842,6 +858,9 @@ list_clear_impl(PyListObject *a, bool is_resize) Py_XDECREF(items[i]); } #ifdef Py_GIL_DISABLED + if (is_resize) { + ensure_shared_on_resize(a); + } bool use_qsbr = is_resize && _PyObject_GC_IS_SHARED(a); #else bool use_qsbr = false; @@ -3107,6 +3126,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse) Py_XDECREF(final_ob_item[i]); } #ifdef Py_GIL_DISABLED + ensure_shared_on_resize(self); bool use_qsbr = _PyObject_GC_IS_SHARED(self); #else bool use_qsbr = false;