]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112529: Stop the world around gc.get_referents (#114823)
authorSam Gross <colesbury@gmail.com>
Tue, 6 Feb 2024 16:45:42 +0000 (11:45 -0500)
committerGitHub <noreply@github.com>
Tue, 6 Feb 2024 16:45:42 +0000 (11:45 -0500)
We do not want to add locking in `tp_traverse` slot implementations.
Instead, stop the world when calling `gc.get_referents`. Note that the the
stop the world call is a no-op in the default build.

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Modules/gcmodule.c

index 3b63dd7a9a83533b8b0db7aede7efdb0f4cc61bf..3a42654b41b2acb780a00c9f5b9fb3835c3221b9 100644 (file)
@@ -230,6 +230,26 @@ referentsvisit(PyObject *obj, void *arg)
     return PyList_Append(list, obj) < 0;
 }
 
+static int
+append_referrents(PyObject *result, PyObject *args)
+{
+    for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
+        PyObject *obj = PyTuple_GET_ITEM(args, i);
+        if (!_PyObject_IS_GC(obj)) {
+            continue;
+        }
+
+        traverseproc traverse = Py_TYPE(obj)->tp_traverse;
+        if (!traverse) {
+            continue;
+        }
+        if (traverse(obj, referentsvisit, result)) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
 /*[clinic input]
 gc.get_referents
 
@@ -242,29 +262,24 @@ static PyObject *
 gc_get_referents_impl(PyObject *module, PyObject *args)
 /*[clinic end generated code: output=d47dc02cefd06fe8 input=b3ceab0c34038cbf]*/
 {
-    Py_ssize_t i;
     if (PySys_Audit("gc.get_referents", "(O)", args) < 0) {
         return NULL;
     }
+    PyInterpreterState *interp = _PyInterpreterState_GET();
     PyObject *result = PyList_New(0);
 
     if (result == NULL)
         return NULL;
 
-    for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
-        traverseproc traverse;
-        PyObject *obj = PyTuple_GET_ITEM(args, i);
+    // NOTE: stop the world is a no-op in default build
+    _PyEval_StopTheWorld(interp);
+    int err = append_referrents(result, args);
+    _PyEval_StartTheWorld(interp);
 
-        if (!_PyObject_IS_GC(obj))
-            continue;
-        traverse = Py_TYPE(obj)->tp_traverse;
-        if (! traverse)
-            continue;
-        if (traverse(obj, referentsvisit, result)) {
-            Py_DECREF(result);
-            return NULL;
-        }
+    if (err < 0) {
+        Py_CLEAR(result);
     }
+
     return result;
 }