From c4a91e0588f2091ff97964135f10b276830c5a3d Mon Sep 17 00:00:00 2001 From: jbower <1978924+jbower-fb@users.noreply.github.com> Date: Tue, 7 Feb 2023 23:23:31 -0800 Subject: [PATCH 01/17] Add PyGC_VisitObjects Co-authored-by: Brett Simmers --- Include/objimpl.h | 7 ++++++ Modules/_testcapimodule.c | 48 +++++++++++++++++++++++++++++++++++++++ Modules/gcmodule.c | 14 ++++++++++++ 3 files changed, 69 insertions(+) diff --git a/Include/objimpl.h b/Include/objimpl.h index dde8df34835328..c946b56a52374f 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -157,6 +157,13 @@ PyAPI_FUNC(int) PyGC_Enable(void); PyAPI_FUNC(int) PyGC_Disable(void); PyAPI_FUNC(int) PyGC_IsEnabled(void); +/* Visit all live GC-capable objects, similar to gc.get_objects(None). + * + * Users should avoid allocating or deallocating objects on the Python heap in + * the callback. */ +typedef void (*gcvisitobjects_t)(PyObject*, void*); +PyAPI_FUNC(void) PyGC_VisitObjects(gcvisitobjects_t callback, void* arg); + /* Test if a type has a GC head */ #define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0d8d1d73fb2390..45dd4d956404e3 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3517,6 +3517,53 @@ err_restore(PyObject *self, PyObject *args) { return NULL; } +struct gc_visit_state { + PyObject *target; + int found; +}; + +static void +gc_visit_callback(PyObject *obj, void* arg) { + struct gc_visit_state *state = (struct gc_visit_state *)arg; + if (obj == state->target) { + state->found = 1; + } +} + +static PyObject * +test_gc_visit_objects(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { + /* Basic sanity test: does it find objects as appropriate? */ + PyObject *obj; + struct gc_visit_state state; + + obj = PyList_New(0); + if (obj == NULL) { + goto error; + } + state.target = obj; + state.found = 0; + + PyGC_VisitObjects(gc_visit_callback, &state); + if (!state.found) { + PyErr_SetString( + PyExc_AssertionError, + "test_gc_visit_objects: Didn't find live list"); + goto error; + } + + int error = 0; + goto done; +error: + error = 1; +done: + Py_XDECREF(obj); + if (error) { + return NULL; + } + Py_RETURN_NONE; +} + + static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); static PyMethodDef TestMethods[] = { @@ -3671,6 +3718,7 @@ static PyMethodDef TestMethods[] = { {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, {"err_set_raised", err_set_raised, METH_O, NULL}, {"err_restore", err_restore, METH_VARARGS, NULL}, + {"test_gc_visit_objects", test_gc_visit_objects, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 5879c5e11fe14a..a2b98662665b70 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2401,3 +2401,17 @@ PyObject_GC_IsFinalized(PyObject *obj) } return 0; } + +void +PyGC_VisitObjects(gcvisitobjects_t callback, void *arg) { + size_t i; + GCState* gcstate = get_gc_state(); + for (i = 0; i < NUM_GENERATIONS; i++) { + PyGC_Head *gc_list, *gc; + gc_list = GEN_HEAD(gcstate, i); + for (gc = GC_NEXT(gc_list); gc != gc_list; gc = GC_NEXT(gc)) { + PyObject *op = FROM_GC(gc); + callback(op, arg); + } + } +} From 57edf6d12f79f01768e49022112d1b4e9dd6da2b Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 18 Feb 2023 00:55:15 +0000 Subject: [PATCH 02/17] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst diff --git a/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst new file mode 100644 index 00000000000000..4e19eb2bcc8815 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst @@ -0,0 +1 @@ +Add a new C-API function for iterating over GC'able objects using a callback: ``PyGC_VisitObjects``. From 447bb538a3e1f9c20cdf68309b60adbc534c092f Mon Sep 17 00:00:00 2001 From: jbower <1978924+jbower-fb@users.noreply.github.com> Date: Fri, 17 Feb 2023 17:23:06 -0800 Subject: [PATCH 03/17] Remove from Limited API --- Include/objimpl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Include/objimpl.h b/Include/objimpl.h index c946b56a52374f..03a5ce6d41c735 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -157,12 +157,15 @@ PyAPI_FUNC(int) PyGC_Enable(void); PyAPI_FUNC(int) PyGC_Disable(void); PyAPI_FUNC(int) PyGC_IsEnabled(void); + +#if !defined(Py_LIMITED_API) /* Visit all live GC-capable objects, similar to gc.get_objects(None). * * Users should avoid allocating or deallocating objects on the Python heap in * the callback. */ typedef void (*gcvisitobjects_t)(PyObject*, void*); PyAPI_FUNC(void) PyGC_VisitObjects(gcvisitobjects_t callback, void* arg); +#endif /* Test if a type has a GC head */ #define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) From d34a900e1e1a5ac18f2392579ca9c345815d0506 Mon Sep 17 00:00:00 2001 From: jbower <1978924+jbower-fb@users.noreply.github.com> Date: Wed, 22 Feb 2023 20:14:19 -0800 Subject: [PATCH 04/17] Updated to "unstable" API, protect against GC and current object deletion, add ability to terminate iteration early with test. --- Include/objimpl.h | 19 ++++++++++++----- Modules/_testcapimodule.c | 44 ++++++++++++++++++++++++++++++--------- Modules/gcmodule.c | 13 ++++++++++-- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Include/objimpl.h b/Include/objimpl.h index 03a5ce6d41c735..ef871c5ea93ebe 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -159,12 +159,21 @@ PyAPI_FUNC(int) PyGC_IsEnabled(void); #if !defined(Py_LIMITED_API) -/* Visit all live GC-capable objects, similar to gc.get_objects(None). +/* Visit all live GC-capable objects, similar to gc.get_objects(None). The + * supplied callback is called on every such object with the void* arg set + * to the supplied arg. Returning 0 from the callback ends iteration, returning + * 1 allows iteration to continue. Returning any other value may result in + * undefined behaviour. * - * Users should avoid allocating or deallocating objects on the Python heap in - * the callback. */ -typedef void (*gcvisitobjects_t)(PyObject*, void*); -PyAPI_FUNC(void) PyGC_VisitObjects(gcvisitobjects_t callback, void* arg); + * If new objects are (de)allocated by the callback it is undefined if they + * will be visited. + + * Garbage collection is disabled during operation. Explicitly running a + * collection in the callback may lead to undefined behaviour e.g. visiting the + * same objects multiple times or not at all. + */ +typedef int (*gcvisitobjects_t)(PyObject*, void*); +PyAPI_FUNC(void) PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void* arg); #endif /* Test if a type has a GC head */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 45dd4d956404e3..f45302e6d55ecd 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3517,24 +3517,25 @@ err_restore(PyObject *self, PyObject *args) { return NULL; } -struct gc_visit_state { +struct gc_visit_state_basic { PyObject *target; int found; }; -static void -gc_visit_callback(PyObject *obj, void* arg) { - struct gc_visit_state *state = (struct gc_visit_state *)arg; +static int +gc_visit_callback_basic(PyObject *obj, void* arg) { + struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg; if (obj == state->target) { state->found = 1; + return 0; } + return 1; } static PyObject * -test_gc_visit_objects(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { - /* Basic sanity test: does it find objects as appropriate? */ +test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { PyObject *obj; - struct gc_visit_state state; + struct gc_visit_state_basic state; obj = PyList_New(0); if (obj == NULL) { @@ -3543,11 +3544,11 @@ test_gc_visit_objects(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { state.target = obj; state.found = 0; - PyGC_VisitObjects(gc_visit_callback, &state); + PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state); if (!state.found) { PyErr_SetString( PyExc_AssertionError, - "test_gc_visit_objects: Didn't find live list"); + "test_gc_visit_objects_basic: Didn't find live list"); goto error; } @@ -3563,6 +3564,28 @@ test_gc_visit_objects(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { Py_RETURN_NONE; } +static int +gc_visit_callback_exit_early(PyObject *obj, void* arg) { + int *visited_i = (int *)arg; + (*visited_i)++; + if (*visited_i == 2) { + return 0; + } + return 1; +} + +static PyObject * +test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { + int visited_i = 0; + PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i); + if (visited_i != 2) { + PyErr_SetString( + PyExc_AssertionError, + "test_gc_visit_objects_exit_early: did not exit when expected"); + } + Py_RETURN_NONE; +} + static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); @@ -3718,7 +3741,8 @@ static PyMethodDef TestMethods[] = { {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, {"err_set_raised", err_set_raised, METH_O, NULL}, {"err_restore", err_restore, METH_VARARGS, NULL}, - {"test_gc_visit_objects", test_gc_visit_objects, METH_NOARGS, NULL}, + {"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL}, + {"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index a2b98662665b70..92d27fa15f1d84 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2403,15 +2403,24 @@ PyObject_GC_IsFinalized(PyObject *obj) } void -PyGC_VisitObjects(gcvisitobjects_t callback, void *arg) { +PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { size_t i; GCState* gcstate = get_gc_state(); + int origenstate = gcstate->enabled; + gcstate->enabled = 0; for (i = 0; i < NUM_GENERATIONS; i++) { PyGC_Head *gc_list, *gc; gc_list = GEN_HEAD(gcstate, i); for (gc = GC_NEXT(gc_list); gc != gc_list; gc = GC_NEXT(gc)) { PyObject *op = FROM_GC(gc); - callback(op, arg); + Py_INCREF(op); + int res = callback(op, arg); + Py_DECREF(op); + if (!res) { + goto done; + } } } +done:; + gcstate->enabled = origenstate; } From 9b0c461f1fad04bcbe44ffce1a023928b9cb875a Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:53:55 -0800 Subject: [PATCH 05/17] Fix formatting in Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index f45302e6d55ecd..a64cdf9c0caffe 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3523,7 +3523,8 @@ struct gc_visit_state_basic { }; static int -gc_visit_callback_basic(PyObject *obj, void* arg) { +gc_visit_callback_basic(PyObject *obj, void *arg) +{ struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg; if (obj == state->target) { state->found = 1; From 29f7f8883fcaf4c0f5f451c18a6243c34eac5b33 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:54:08 -0800 Subject: [PATCH 06/17] Fix formatting in Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a64cdf9c0caffe..3707fe4cbe04f2 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3534,7 +3534,8 @@ gc_visit_callback_basic(PyObject *obj, void *arg) } static PyObject * -test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { +test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) +{ PyObject *obj; struct gc_visit_state_basic state; From e16f1e3eecb9ddf5f6edfcc7589a073891ed4419 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:54:32 -0800 Subject: [PATCH 07/17] Fix formatting in Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3707fe4cbe04f2..0e86a8e5ba7077 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3567,7 +3567,8 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignor } static int -gc_visit_callback_exit_early(PyObject *obj, void* arg) { +gc_visit_callback_exit_early(PyObject *obj, void *arg) + { int *visited_i = (int *)arg; (*visited_i)++; if (*visited_i == 2) { From 024ee30262775e87213400759990d2895defc0c3 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:55:13 -0800 Subject: [PATCH 08/17] Fix formatting in Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0e86a8e5ba7077..0a66026ffcdf12 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3578,7 +3578,8 @@ gc_visit_callback_exit_early(PyObject *obj, void *arg) } static PyObject * -test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { +test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) +{ int visited_i = 0; PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i); if (visited_i != 2) { From bd65abb099e2ab8d36ec271b388117f6eea9bf5d Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:55:30 -0800 Subject: [PATCH 09/17] Fix formatting in Modules/gcmodule.c Co-authored-by: Erlend E. Aasland --- Modules/gcmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 92d27fa15f1d84..54af6532e6982f 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2403,7 +2403,8 @@ PyObject_GC_IsFinalized(PyObject *obj) } void -PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { +PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) +{ size_t i; GCState* gcstate = get_gc_state(); int origenstate = gcstate->enabled; From f03e581550e91f3148bfa339a96702acfe82975c Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:55:47 -0800 Subject: [PATCH 10/17] Fix formatting in Modules/gcmodule.c Co-authored-by: Erlend E. Aasland --- Modules/gcmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 54af6532e6982f..6e41da695ba522 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2406,7 +2406,7 @@ void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { size_t i; - GCState* gcstate = get_gc_state(); + GCState *gcstate = get_gc_state(); int origenstate = gcstate->enabled; gcstate->enabled = 0; for (i = 0; i < NUM_GENERATIONS; i++) { From 16ea0d6f6052df3a41793fdac3a5d040a2b87a53 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 11:55:57 -0800 Subject: [PATCH 11/17] Fix formatting in Modules/gcmodule.c Co-authored-by: Erlend E. Aasland --- Modules/gcmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 6e41da695ba522..d977408bdee310 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2418,7 +2418,7 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) int res = callback(op, arg); Py_DECREF(op); if (!res) { - goto done; + goto done; } } } From 369e1994da1db8bb2ece9cf9495ccf9ed07f959b Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:43:07 -0800 Subject: [PATCH 12/17] Simplify test in Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0a66026ffcdf12..1f711fa57f0e79 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3541,27 +3541,18 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignor obj = PyList_New(0); if (obj == NULL) { - goto error; + return NULL; } state.target = obj; state.found = 0; - + PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state); + Py_DECREF(obj); if (!state.found) { PyErr_SetString( - PyExc_AssertionError, - "test_gc_visit_objects_basic: Didn't find live list"); - goto error; - } - - int error = 0; - goto done; -error: - error = 1; -done: - Py_XDECREF(obj); - if (error) { - return NULL; + PyExc_AssertionError, + "test_gc_visit_objects_basic: Didn't find live list"); + return NULL; } Py_RETURN_NONE; } From 219154af15225ed0363da3e1dff32df7d1eeb351 Mon Sep 17 00:00:00 2001 From: jbower <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:21:33 -0800 Subject: [PATCH 13/17] Address minor review comments and add documentation. --- Doc/c-api/gcsupport.rst | 33 +++++++++++++++++++ ...-02-18-00-55-14.gh-issue-102013.83mrtI.rst | 2 +- Modules/gcmodule.c | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 8c90d1e8991c10..358f397ed9c9cb 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -228,3 +228,36 @@ garbage collection runs. Returns the current state, 0 for disabled and 1 for enabled. .. versionadded:: 3.10 + + +Querying Garbage Collector State +-------------------------------- + +The C-API provides the following inerface for querying information about +the garbage collector. + +.. c:function:: void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) + + Run supplied callback on all live GC-capable objects. + + .. warning:: + If new objects are (de)allocated by the callback it is undefined if they + will be visited. + + .. warning:: + Garbage collection is disabled during operation. Explicitly running a collection + in the callback may lead to undefined behaviour e.g. visiting the same objects + multiple times or not at all. + + .. versionadded:: 3.12 + +.. c:type:: int (*gcvisitobjects_t)(PyObject *object, void *arg) + + Type of the visitor function to be passed to :c:func:`PyUnstable_GC_VisitObjects`. + ``arg`` is the same as passed to ``PyUnstable_GC_VisitObjects``. Returning ``0`` + allows iteration to continue, returning ``1`` stops further iteration. Returning any + other value has undefined behavior. + + .. versionadded:: 3.12 + + diff --git a/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst index 4e19eb2bcc8815..0350237ebc7390 100644 --- a/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst +++ b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst @@ -1 +1 @@ -Add a new C-API function for iterating over GC'able objects using a callback: ``PyGC_VisitObjects``. +Add a new (unstable) C-API function for iterating over GC'able objects using a callback: ``PyUnstable_VisitObjects``. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index d977408bdee310..4eaa5490b6134c 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2422,6 +2422,6 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) } } } -done:; +done: gcstate->enabled = origenstate; } From 5752f1c430f9e25d929a9ececeda781f2b4f706c Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Sat, 11 Mar 2023 10:54:12 -0800 Subject: [PATCH 14/17] Update Doc/c-api/gcsupport.rst Co-authored-by: Erlend E. Aasland --- Doc/c-api/gcsupport.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 358f397ed9c9cb..4f21c386126fe9 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -233,7 +233,7 @@ garbage collection runs. Querying Garbage Collector State -------------------------------- -The C-API provides the following inerface for querying information about +The C-API provides the following interface for querying information about the garbage collector. .. c:function:: void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) From 1512004ca4baed89fac7407b64703ccbdd8f9470 Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Sat, 11 Mar 2023 10:55:05 -0800 Subject: [PATCH 15/17] Update Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c687f9e18284a6..5608705084f38b 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3333,7 +3333,8 @@ gc_visit_callback_basic(PyObject *obj, void *arg) } static PyObject * -test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) +test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), + PyObject *Py_UNUSED(ignored)) { PyObject *obj; struct gc_visit_state_basic state; From 87a5b56a946b349cdf54352e48b08d5cffee325e Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Sat, 11 Mar 2023 10:55:17 -0800 Subject: [PATCH 16/17] Update Modules/_testcapimodule.c Co-authored-by: Erlend E. Aasland --- Modules/_testcapimodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 5608705084f38b..4d51a39db8b053 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3369,7 +3369,8 @@ gc_visit_callback_exit_early(PyObject *obj, void *arg) } static PyObject * -test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) +test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), + PyObject *Py_UNUSED(ignored)) { int visited_i = 0; PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i); From 295f7dd77e83e04617f7183e2e83d821b4526419 Mon Sep 17 00:00:00 2001 From: jbower <1978924+jbower-fb@users.noreply.github.com> Date: Sat, 11 Mar 2023 11:21:08 -0800 Subject: [PATCH 17/17] Minor doc tweaks from review --- Doc/c-api/gcsupport.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 4f21c386126fe9..cb5d64a50487fe 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -238,13 +238,13 @@ the garbage collector. .. c:function:: void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) - Run supplied callback on all live GC-capable objects. + Run supplied *callback* on all live GC-capable objects. *arg* is passed through to + all invocations of *callback*. .. warning:: If new objects are (de)allocated by the callback it is undefined if they will be visited. - .. warning:: Garbage collection is disabled during operation. Explicitly running a collection in the callback may lead to undefined behaviour e.g. visiting the same objects multiple times or not at all. @@ -254,9 +254,9 @@ the garbage collector. .. c:type:: int (*gcvisitobjects_t)(PyObject *object, void *arg) Type of the visitor function to be passed to :c:func:`PyUnstable_GC_VisitObjects`. - ``arg`` is the same as passed to ``PyUnstable_GC_VisitObjects``. Returning ``0`` - allows iteration to continue, returning ``1`` stops further iteration. Returning any - other value has undefined behavior. + *arg* is the same as the *arg* passed to ``PyUnstable_GC_VisitObjects``. + Return ``0`` to continue iteration, return ``1`` to stop iteration. Other return + values are reserved for now so behavior on returning anything else is undefined. .. versionadded:: 3.12