diff options
author | Sam Gross <colesbury@gmail.com> | 2024-02-06 16:45:42 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-06 16:45:42 (GMT) |
commit | 7fdd4235d790559372bbb1bf0c2384191a9bb5f3 (patch) | |
tree | f97f2018900b70ca3fdf7c0643e20eef6b511803 /Modules/gcmodule.c | |
parent | f7a22a7055d97c05406512577bdfcb6d3f134b91 (diff) | |
download | cpython-7fdd4235d790559372bbb1bf0c2384191a9bb5f3.zip cpython-7fdd4235d790559372bbb1bf0c2384191a9bb5f3.tar.gz cpython-7fdd4235d790559372bbb1bf0c2384191a9bb5f3.tar.bz2 |
gh-112529: Stop the world around gc.get_referents (#114823)
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>
Diffstat (limited to 'Modules/gcmodule.c')
-rw-r--r-- | Modules/gcmodule.c | 41 |
1 files changed, 28 insertions, 13 deletions
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 3b63dd7..3a42654 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -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; } |