]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127521: Mark list as "shared" before resizing if necessary (#127524)
authorSam Gross <colesbury@gmail.com>
Mon, 2 Dec 2024 19:38:26 +0000 (19:38 +0000)
committerGitHub <noreply@github.com>
Mon, 2 Dec 2024 19:38:26 +0000 (14:38 -0500)
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.

Objects/listobject.c

index 4b24f4a428e18be0b5858d0cc42aede44799f18d..8abe9e8933420bb14cfd4b735c566b21410b09ce 100644 (file)
@@ -68,6 +68,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
@@ -117,6 +131,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) {
@@ -804,6 +820,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;
@@ -3069,6 +3088,7 @@ keyfunc_fail:
             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;