From ac9e4efc41231e0a3316e469c77c7f575db5805a Mon Sep 17 00:00:00 2001
From: Sam Gross <colesbury@gmail.com>
Date: Wed, 31 Jan 2024 21:44:04 +0000
Subject: [PATCH 1/3] gh-112529: Stop the world around gc.get_referents

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.
---
 Modules/gcmodule.c | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index ffddef34ecce7a..e82d606038e410 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -235,6 +235,25 @@ 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++) {
+        traverseproc traverse;
+        PyObject *obj = PyTuple_GET_ITEM(args, i);
+
+        if (!_PyObject_IS_GC(obj))
+            continue;
+        traverse = Py_TYPE(obj)->tp_traverse;
+        if (! traverse)
+            continue;
+        if (traverse(obj, referentsvisit, result)) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
 /*[clinic input]
 gc.get_referents
 
@@ -247,29 +266,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;
 }
 

From b76f33b9ceaa86147f518136c7631413e61bbbf9 Mon Sep 17 00:00:00 2001
From: Sam Gross <colesbury@gmail.com>
Date: Thu, 1 Feb 2024 11:33:29 -0500
Subject: [PATCH 2/3] Update Modules/gcmodule.c

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
---
 Modules/gcmodule.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index e82d606038e410..94768e56202d61 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -245,7 +245,7 @@ append_referrents(PyObject *result, PyObject *args)
         if (!_PyObject_IS_GC(obj))
             continue;
         traverse = Py_TYPE(obj)->tp_traverse;
-        if (! traverse)
+        if (!traverse)
             continue;
         if (traverse(obj, referentsvisit, result)) {
             return -1;

From e689f8b772c02b020f41687ab949d3997e5d3cbb Mon Sep 17 00:00:00 2001
From: Sam Gross <colesbury@gmail.com>
Date: Thu, 1 Feb 2024 16:35:38 +0000
Subject: [PATCH 3/3] Add braces to if-statements

---
 Modules/gcmodule.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 94768e56202d61..a2b66b9b78c169 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -239,14 +239,15 @@ static int
 append_referrents(PyObject *result, PyObject *args)
 {
     for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
-        traverseproc traverse;
         PyObject *obj = PyTuple_GET_ITEM(args, i);
-
-        if (!_PyObject_IS_GC(obj))
+        if (!_PyObject_IS_GC(obj)) {
             continue;
-        traverse = Py_TYPE(obj)->tp_traverse;
-        if (!traverse)
+        }
+
+        traverseproc traverse = Py_TYPE(obj)->tp_traverse;
+        if (!traverse) {
             continue;
+        }
         if (traverse(obj, referentsvisit, result)) {
             return -1;
         }