Description
Bug report
Bug description:
The Python C API provides the function PyGC_Disable() to temporarily disable garbage collection. Calling it causes PyGC_Collect()
to become a no-op. So far so good.
Unfortunately, CPython >= 3.12 no longer respects this flag beyond direct calls to PyGC_Collect()
. Let's consider, for example, the operation PyErr_CheckSignals()
at line 1773. This calls _Py_RunGC
, which ignores the GC enabled
flag. And this indeed happens! In fact, I just spent quite a bit of time debugging an issue where the GC was supposed to be disabled for a brief moment, and yet it runs on Python 3.12. (Whether disabling the GC for a brief period of time is a good design pattern is another discussion. I would like to steer the discussion away from this and focus on documented API behavior.)
The PyErr_CheckSignals()
function is called from 135 locations in the CPython codebase including very common ones like PyObject_Str()
, so making any fixes in callers of this API does not look feasible.
The flag-ignoring _Py_RunGC()
function is only called by two places: besides the mentioned PyErr_CheckSignals()
, there is also _Py_HandlePending()
in Python/ceval_gil.c
.
To restore the documented behavior, I see three options:
_Py_RunGC()
could be modified to exit immediately if theenabled
flag is set to zero.- The implementation of
_Py_HandlePending()
andPyErr_CheckSignals()
could be modified to checkPyGC_IsEnabled()
before calling_Py_RunGC()
. - Something is setting
_PY_GC_SCHEDULED_BIT
, and that causes the implementation to enter_Py_RunGC()
. I'm not really sure about how that works, but perhaps this flag could be cleared inPyGC_Disable()
, and Python could then avoid setting the flag.
(Tagging @pablogsal and @swtaarrs, who commited changes to the relevant code.)
CPython versions tested on:
3.12
Operating systems tested on:
Linux