mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-28254: Add a C-API for controlling the GC state (GH-25687)
Add new C-API functions to control the state of the garbage collector: PyGC_Enable(), PyGC_Disable(), PyGC_IsEnabled(), corresponding to the functions in the gc module. Co-authored-by: Pablo Galindo <Pablogsal@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
baecfbd849
commit
3cc481b9de
7 changed files with 152 additions and 7 deletions
|
@ -173,3 +173,46 @@ if the object is immutable.
|
||||||
this method (don't just call :c:func:`Py_DECREF` on a reference). The
|
this method (don't just call :c:func:`Py_DECREF` on a reference). The
|
||||||
collector will call this method if it detects that this object is involved
|
collector will call this method if it detects that this object is involved
|
||||||
in a reference cycle.
|
in a reference cycle.
|
||||||
|
|
||||||
|
|
||||||
|
Controlling the Garbage Collector State
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
The C-API provides the following functions for controlling
|
||||||
|
garbage collection runs.
|
||||||
|
|
||||||
|
.. c:function:: Py_ssize_t PyGC_Collect(void)
|
||||||
|
|
||||||
|
Perform a full garbage collection, if the garbage collector is enabled.
|
||||||
|
(Note that :func:`gc.collect` runs it unconditionally.)
|
||||||
|
|
||||||
|
Returns the number of collected + unreachable objects which cannot
|
||||||
|
be collected.
|
||||||
|
If the garbage collector is disabled or already collecting,
|
||||||
|
returns ``0`` immediately.
|
||||||
|
Errors during garbage collection are passed to :data:`sys.unraisablehook`.
|
||||||
|
This function does not raise exceptions.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyGC_Enable(void)
|
||||||
|
|
||||||
|
Enable the garbage collector: similar to :func:`gc.enable`.
|
||||||
|
Returns the previous state, 0 for disabled and 1 for enabled.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyGC_Disable(void)
|
||||||
|
|
||||||
|
Disable the garbage collector: similar to :func:`gc.disable`.
|
||||||
|
Returns the previous state, 0 for disabled and 1 for enabled.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyGC_IsEnabled(void)
|
||||||
|
|
||||||
|
Query the state of the garbage collector: similar to :func:`gc.isenabled`.
|
||||||
|
Returns the current state, 0 for disabled and 1 for enabled.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
|
@ -268,6 +268,9 @@ PyFrame_GetLineNumber
|
||||||
PyFrozenSet_New
|
PyFrozenSet_New
|
||||||
PyFrozenSet_Type
|
PyFrozenSet_Type
|
||||||
PyGC_Collect
|
PyGC_Collect
|
||||||
|
PyGC_Disable
|
||||||
|
PyGC_Enable
|
||||||
|
PyGC_IsEnabled
|
||||||
PyGILState_Ensure
|
PyGILState_Ensure
|
||||||
PyGILState_GetThisThreadState
|
PyGILState_GetThisThreadState
|
||||||
PyGILState_Release
|
PyGILState_Release
|
||||||
|
|
|
@ -1697,6 +1697,13 @@ New Features
|
||||||
singleton or the ``False`` singleton.
|
singleton or the ``False`` singleton.
|
||||||
(Contributed by Victor Stinner in :issue:`43753`.)
|
(Contributed by Victor Stinner in :issue:`43753`.)
|
||||||
|
|
||||||
|
* Add new functions to quickly control the garbage collector from C code:
|
||||||
|
:c:func:`PyGC_Enable()`,
|
||||||
|
:c:func:`PyGC_Disable()`,
|
||||||
|
:c:func:`PyGC_IsEnabled()`.
|
||||||
|
These functions allow to activate, deactivate and query the state of the garbage collector from C code without
|
||||||
|
having to import the :mod:`gc` module.
|
||||||
|
|
||||||
Porting to Python 3.10
|
Porting to Python 3.10
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -150,8 +150,12 @@ PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
|
||||||
* ==========================
|
* ==========================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* C equivalent of gc.collect() which ignores the state of gc.enabled. */
|
/* C equivalent of gc.collect(). */
|
||||||
PyAPI_FUNC(Py_ssize_t) PyGC_Collect(void);
|
PyAPI_FUNC(Py_ssize_t) PyGC_Collect(void);
|
||||||
|
/* C API for controlling the state of the garbage collector */
|
||||||
|
PyAPI_FUNC(int) PyGC_Enable(void);
|
||||||
|
PyAPI_FUNC(int) PyGC_Disable(void);
|
||||||
|
PyAPI_FUNC(int) PyGC_IsEnabled(void);
|
||||||
|
|
||||||
/* Test if a type has a GC head */
|
/* Test if a type has a GC head */
|
||||||
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
|
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add new C-API functions to control the state of the garbage collector:
|
||||||
|
:c:func:`PyGC_Enable()`, :c:func:`PyGC_Disable()`, :c:func:`PyGC_IsEnabled()`,
|
||||||
|
corresponding to the functions in the :mod:`gc` module.
|
|
@ -144,6 +144,67 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
test_gc_control(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
int orig_enabled = PyGC_IsEnabled();
|
||||||
|
const char* msg = "ok";
|
||||||
|
int old_state;
|
||||||
|
|
||||||
|
old_state = PyGC_Enable();
|
||||||
|
msg = "Enable(1)";
|
||||||
|
if (old_state != orig_enabled) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(1)";
|
||||||
|
if (!PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_state = PyGC_Disable();
|
||||||
|
msg = "disable(2)";
|
||||||
|
if (!old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(2)";
|
||||||
|
if (PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_state = PyGC_Enable();
|
||||||
|
msg = "enable(3)";
|
||||||
|
if (old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(3)";
|
||||||
|
if (!PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orig_enabled) {
|
||||||
|
old_state = PyGC_Disable();
|
||||||
|
msg = "disable(4)";
|
||||||
|
if (old_state) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
msg = "IsEnabled(4)";
|
||||||
|
if (PyGC_IsEnabled()) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
/* Try to clean up if we can. */
|
||||||
|
if (orig_enabled) {
|
||||||
|
PyGC_Enable();
|
||||||
|
} else {
|
||||||
|
PyGC_Disable();
|
||||||
|
}
|
||||||
|
PyErr_Format(TestError, "GC control failed in %s", msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
|
test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
@ -5544,6 +5605,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"PyDateTime_DATE_GET", test_PyDateTime_DATE_GET, METH_O},
|
{"PyDateTime_DATE_GET", test_PyDateTime_DATE_GET, METH_O},
|
||||||
{"PyDateTime_TIME_GET", test_PyDateTime_TIME_GET, METH_O},
|
{"PyDateTime_TIME_GET", test_PyDateTime_TIME_GET, METH_O},
|
||||||
{"PyDateTime_DELTA_GET", test_PyDateTime_DELTA_GET, METH_O},
|
{"PyDateTime_DELTA_GET", test_PyDateTime_DELTA_GET, METH_O},
|
||||||
|
{"test_gc_control", test_gc_control, METH_NOARGS},
|
||||||
{"test_list_api", test_list_api, METH_NOARGS},
|
{"test_list_api", test_list_api, METH_NOARGS},
|
||||||
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
|
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
|
||||||
{"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS},
|
{"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS},
|
||||||
|
|
|
@ -1484,8 +1484,7 @@ static PyObject *
|
||||||
gc_enable_impl(PyObject *module)
|
gc_enable_impl(PyObject *module)
|
||||||
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
|
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
|
||||||
{
|
{
|
||||||
GCState *gcstate = get_gc_state();
|
PyGC_Enable();
|
||||||
gcstate->enabled = 1;
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1499,8 +1498,7 @@ static PyObject *
|
||||||
gc_disable_impl(PyObject *module)
|
gc_disable_impl(PyObject *module)
|
||||||
/*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
|
/*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
|
||||||
{
|
{
|
||||||
GCState *gcstate = get_gc_state();
|
PyGC_Disable();
|
||||||
gcstate->enabled = 0;
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1514,8 +1512,7 @@ static int
|
||||||
gc_isenabled_impl(PyObject *module)
|
gc_isenabled_impl(PyObject *module)
|
||||||
/*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
|
/*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
|
||||||
{
|
{
|
||||||
GCState *gcstate = get_gc_state();
|
return PyGC_IsEnabled();
|
||||||
return gcstate->enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -2053,6 +2050,32 @@ PyInit_gc(void)
|
||||||
return PyModuleDef_Init(&gcmodule);
|
return PyModuleDef_Init(&gcmodule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* C API for controlling the state of the garbage collector */
|
||||||
|
int
|
||||||
|
PyGC_Enable(void)
|
||||||
|
{
|
||||||
|
GCState *gcstate = get_gc_state();
|
||||||
|
int old_state = gcstate->enabled;
|
||||||
|
gcstate->enabled = 1;
|
||||||
|
return old_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PyGC_Disable(void)
|
||||||
|
{
|
||||||
|
GCState *gcstate = get_gc_state();
|
||||||
|
int old_state = gcstate->enabled;
|
||||||
|
gcstate->enabled = 0;
|
||||||
|
return old_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PyGC_IsEnabled(void)
|
||||||
|
{
|
||||||
|
GCState *gcstate = get_gc_state();
|
||||||
|
return gcstate->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/* Public API to invoke gc.collect() from C */
|
/* Public API to invoke gc.collect() from C */
|
||||||
Py_ssize_t
|
Py_ssize_t
|
||||||
PyGC_Collect(void)
|
PyGC_Collect(void)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue