summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Modules/gcmodule.c
diff options
context:
space:
mode:
authorshadchin <[email protected]>2022-04-18 12:39:32 +0300
committershadchin <[email protected]>2022-04-18 12:39:32 +0300
commitd4be68e361f4258cf0848fc70018dfe37a2acc24 (patch)
tree153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Modules/gcmodule.c
parent260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff)
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Modules/gcmodule.c')
-rw-r--r--contrib/tools/python3/src/Modules/gcmodule.c288
1 files changed, 152 insertions, 136 deletions
diff --git a/contrib/tools/python3/src/Modules/gcmodule.c b/contrib/tools/python3/src/Modules/gcmodule.c
index 3cf1a00b003..805a159d53d 100644
--- a/contrib/tools/python3/src/Modules/gcmodule.c
+++ b/contrib/tools/python3/src/Modules/gcmodule.c
@@ -31,7 +31,6 @@
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pydtrace.h"
-#include "pytime.h" // _PyTime_GetMonotonicClock()
typedef struct _gc_runtime_state GCState;
@@ -128,6 +127,15 @@ gc_decref(PyGC_Head *g)
#define GEN_HEAD(gcstate, n) (&(gcstate)->generations[n].head)
+
+static GCState *
+get_gc_state(void)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return &interp->gc;
+}
+
+
void
_PyGC_InitState(GCState *gcstate)
{
@@ -153,15 +161,20 @@ _PyGC_InitState(GCState *gcstate)
PyStatus
-_PyGC_Init(PyThreadState *tstate)
+_PyGC_Init(PyInterpreterState *interp)
{
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = &interp->gc;
+
+ gcstate->garbage = PyList_New(0);
if (gcstate->garbage == NULL) {
- gcstate->garbage = PyList_New(0);
- if (gcstate->garbage == NULL) {
- return _PyStatus_NO_MEMORY();
- }
+ return _PyStatus_NO_MEMORY();
}
+
+ gcstate->callbacks = PyList_New(0);
+ if (gcstate->callbacks == NULL) {
+ return _PyStatus_NO_MEMORY();
+ }
+
return _PyStatus_OK();
}
@@ -1023,15 +1036,15 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
* Clearing the free lists may give back memory to the OS earlier.
*/
static void
-clear_freelists(void)
+clear_freelists(PyInterpreterState *interp)
{
- _PyFrame_ClearFreeList();
- _PyTuple_ClearFreeList();
- _PyFloat_ClearFreeList();
- _PyList_ClearFreeList();
- _PyDict_ClearFreeList();
- _PyAsyncGen_ClearFreeLists();
- _PyContext_ClearFreeList();
+ _PyFrame_ClearFreeList(interp);
+ _PyTuple_ClearFreeList(interp);
+ _PyFloat_ClearFreeList(interp);
+ _PyList_ClearFreeList(interp);
+ _PyDict_ClearFreeList(interp);
+ _PyAsyncGen_ClearFreeLists(interp);
+ _PyContext_ClearFreeList(interp);
}
// Show stats for objects in each generations
@@ -1043,7 +1056,7 @@ show_stats_each_generations(GCState *gcstate)
for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) {
pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos,
- " %"PY_FORMAT_SIZE_T"d",
+ " %zd",
gc_list_size(GEN_HEAD(gcstate, i)));
}
@@ -1167,8 +1180,9 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
/* This is the main function. Read this to understand how the
* collection process works. */
static Py_ssize_t
-collect(PyThreadState *tstate, int generation,
- Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable, int nofail)
+gc_collect_main(PyThreadState *tstate, int generation,
+ Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
+ int nofail)
{
int i;
Py_ssize_t m = 0; /* # objects collected */
@@ -1181,6 +1195,19 @@ collect(PyThreadState *tstate, int generation,
_PyTime_t t1 = 0; /* initialize to prevent a compiler warning */
GCState *gcstate = &tstate->interp->gc;
+ // gc_collect_main() must not be called before _PyGC_Init
+ // or after _PyGC_Fini()
+ assert(gcstate->garbage != NULL);
+ assert(!_PyErr_Occurred(tstate));
+
+#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
+ if (tstate->interp->config._isolated_interpreter) {
+ // bpo-40533: The garbage collector must not be run on parallel on
+ // Python objects shared by multiple interpreters.
+ return 0;
+ }
+#endif
+
if (gcstate->debug & DEBUG_STATS) {
PySys_WriteStderr("gc: collecting generation %d...\n", generation);
show_stats_each_generations(gcstate);
@@ -1282,8 +1309,7 @@ collect(PyThreadState *tstate, int generation,
if (gcstate->debug & DEBUG_STATS) {
double d = _PyTime_AsSecondsDouble(_PyTime_GetMonotonicClock() - t1);
PySys_WriteStderr(
- "gc: done, %" PY_FORMAT_SIZE_T "d unreachable, "
- "%" PY_FORMAT_SIZE_T "d uncollectable, %.4fs elapsed\n",
+ "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n",
n+m, n, d);
}
@@ -1297,7 +1323,7 @@ collect(PyThreadState *tstate, int generation,
/* Clear free list only during the collection of the highest
* generation */
if (generation == NUM_GENERATIONS-1) {
- clear_freelists();
+ clear_freelists(tstate->interp);
}
if (_PyErr_Occurred(tstate)) {
@@ -1379,19 +1405,19 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
* progress callbacks.
*/
static Py_ssize_t
-collect_with_callback(PyThreadState *tstate, int generation)
+gc_collect_with_callback(PyThreadState *tstate, int generation)
{
assert(!_PyErr_Occurred(tstate));
Py_ssize_t result, collected, uncollectable;
invoke_gc_callback(tstate, "start", generation, 0, 0);
- result = collect(tstate, generation, &collected, &uncollectable, 0);
+ result = gc_collect_main(tstate, generation, &collected, &uncollectable, 0);
invoke_gc_callback(tstate, "stop", generation, collected, uncollectable);
assert(!_PyErr_Occurred(tstate));
return result;
}
static Py_ssize_t
-collect_generations(PyThreadState *tstate)
+gc_collect_generations(PyThreadState *tstate)
{
GCState *gcstate = &tstate->interp->gc;
/* Find the oldest generation (highest numbered) where the count
@@ -1439,7 +1465,7 @@ collect_generations(PyThreadState *tstate)
if (i == NUM_GENERATIONS - 1
&& gcstate->long_lived_pending < gcstate->long_lived_total / 4)
continue;
- n = collect_with_callback(tstate, i);
+ n = gc_collect_with_callback(tstate, i);
break;
}
}
@@ -1458,9 +1484,7 @@ static PyObject *
gc_enable_impl(PyObject *module)
/*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
- gcstate->enabled = 1;
+ PyGC_Enable();
Py_RETURN_NONE;
}
@@ -1474,9 +1498,7 @@ static PyObject *
gc_disable_impl(PyObject *module)
/*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
- gcstate->enabled = 0;
+ PyGC_Disable();
Py_RETURN_NONE;
}
@@ -1490,9 +1512,7 @@ static int
gc_isenabled_impl(PyObject *module)
/*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
- return gcstate->enabled;
+ return PyGC_IsEnabled();
}
/*[clinic input]
@@ -1528,7 +1548,7 @@ gc_collect_impl(PyObject *module, int generation)
}
else {
gcstate->collecting = 1;
- n = collect_with_callback(tstate, generation);
+ n = gc_collect_with_callback(tstate, generation);
gcstate->collecting = 0;
}
return n;
@@ -1556,8 +1576,7 @@ static PyObject *
gc_set_debug_impl(PyObject *module, int flags)
/*[clinic end generated code: output=7c8366575486b228 input=5e5ce15e84fbed15]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
gcstate->debug = flags;
Py_RETURN_NONE;
}
@@ -1572,8 +1591,7 @@ static int
gc_get_debug_impl(PyObject *module)
/*[clinic end generated code: output=91242f3506cd1e50 input=91a101e1c3b98366]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
return gcstate->debug;
}
@@ -1586,8 +1604,7 @@ PyDoc_STRVAR(gc_set_thresh__doc__,
static PyObject *
gc_set_threshold(PyObject *self, PyObject *args)
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
if (!PyArg_ParseTuple(args, "i|ii:set_threshold",
&gcstate->generations[0].threshold,
&gcstate->generations[1].threshold,
@@ -1610,8 +1627,7 @@ static PyObject *
gc_get_threshold_impl(PyObject *module)
/*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
return Py_BuildValue("(iii)",
gcstate->generations[0].threshold,
gcstate->generations[1].threshold,
@@ -1628,8 +1644,7 @@ static PyObject *
gc_get_count_impl(PyObject *module)
/*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
return Py_BuildValue("(iii)",
gcstate->generations[0].count,
gcstate->generations[1].count,
@@ -1672,9 +1687,6 @@ Return the list of objects that directly refer to any of objs.");
static PyObject *
gc_get_referrers(PyObject *self, PyObject *args)
{
- PyThreadState *tstate = _PyThreadState_GET();
- int i;
-
if (PySys_Audit("gc.get_referrers", "(O)", args) < 0) {
return NULL;
}
@@ -1684,8 +1696,8 @@ gc_get_referrers(PyObject *self, PyObject *args)
return NULL;
}
- GCState *gcstate = &tstate->interp->gc;
- for (i = 0; i < NUM_GENERATIONS; i++) {
+ GCState *gcstate = get_gc_state();
+ for (int i = 0; i < NUM_GENERATIONS; i++) {
if (!(gc_referrers_for(args, GEN_HEAD(gcstate, i), result))) {
Py_DECREF(result);
return NULL;
@@ -1811,11 +1823,10 @@ gc_get_stats_impl(PyObject *module)
{
int i;
struct gc_generation_stats stats[NUM_GENERATIONS], *st;
- PyThreadState *tstate = _PyThreadState_GET();
/* To get consistent values despite allocations while constructing
the result list, we use a snapshot of the running stats. */
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
for (i = 0; i < NUM_GENERATIONS; i++) {
stats[i] = gcstate->generation_stats[i];
}
@@ -1906,8 +1917,7 @@ static PyObject *
gc_freeze_impl(PyObject *module)
/*[clinic end generated code: output=502159d9cdc4c139 input=b602b16ac5febbe5]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
for (int i = 0; i < NUM_GENERATIONS; ++i) {
gc_list_merge(GEN_HEAD(gcstate, i), &gcstate->permanent_generation.head);
gcstate->generations[i].count = 0;
@@ -1927,8 +1937,7 @@ static PyObject *
gc_unfreeze_impl(PyObject *module)
/*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
gc_list_merge(&gcstate->permanent_generation.head,
GEN_HEAD(gcstate, NUM_GENERATIONS-1));
Py_RETURN_NONE;
@@ -1944,8 +1953,7 @@ static Py_ssize_t
gc_get_freeze_count_impl(PyObject *module)
/*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
return gc_list_size(&gcstate->permanent_generation.head);
}
@@ -1996,63 +2004,79 @@ static PyMethodDef GcMethods[] = {
{NULL, NULL} /* Sentinel */
};
-static struct PyModuleDef gcmodule = {
- PyModuleDef_HEAD_INIT,
- "gc", /* m_name */
- gc__doc__, /* m_doc */
- -1, /* m_size */
- GcMethods, /* m_methods */
- NULL, /* m_reload */
- NULL, /* m_traverse */
- NULL, /* m_clear */
- NULL /* m_free */
-};
-
-PyMODINIT_FUNC
-PyInit_gc(void)
+static int
+gcmodule_exec(PyObject *module)
{
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
-
- PyObject *m = PyModule_Create(&gcmodule);
-
- if (m == NULL) {
- return NULL;
- }
+ GCState *gcstate = get_gc_state();
- if (gcstate->garbage == NULL) {
- gcstate->garbage = PyList_New(0);
- if (gcstate->garbage == NULL) {
- return NULL;
- }
- }
- Py_INCREF(gcstate->garbage);
- if (PyModule_AddObject(m, "garbage", gcstate->garbage) < 0) {
- return NULL;
- }
-
- if (gcstate->callbacks == NULL) {
- gcstate->callbacks = PyList_New(0);
- if (gcstate->callbacks == NULL) {
- return NULL;
- }
+ /* garbage and callbacks are initialized by _PyGC_Init() early in
+ * interpreter lifecycle. */
+ assert(gcstate->garbage != NULL);
+ if (PyModule_AddObjectRef(module, "garbage", gcstate->garbage) < 0) {
+ return -1;
}
- Py_INCREF(gcstate->callbacks);
- if (PyModule_AddObject(m, "callbacks", gcstate->callbacks) < 0) {
- return NULL;
+ assert(gcstate->callbacks != NULL);
+ if (PyModule_AddObjectRef(module, "callbacks", gcstate->callbacks) < 0) {
+ return -1;
}
-#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) { return NULL; }
+#define ADD_INT(NAME) if (PyModule_AddIntConstant(module, #NAME, NAME) < 0) { return -1; }
ADD_INT(DEBUG_STATS);
ADD_INT(DEBUG_COLLECTABLE);
ADD_INT(DEBUG_UNCOLLECTABLE);
ADD_INT(DEBUG_SAVEALL);
ADD_INT(DEBUG_LEAK);
#undef ADD_INT
- return m;
+ return 0;
+}
+
+static PyModuleDef_Slot gcmodule_slots[] = {
+ {Py_mod_exec, gcmodule_exec},
+ {0, NULL}
+};
+
+static struct PyModuleDef gcmodule = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gc",
+ .m_doc = gc__doc__,
+ .m_size = 0, // per interpreter state, see: get_gc_state()
+ .m_methods = GcMethods,
+ .m_slots = gcmodule_slots
+};
+
+PyMODINIT_FUNC
+PyInit_gc(void)
+{
+ 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;
}
-/* API to invoke gc.collect() from C */
+int
+PyGC_IsEnabled(void)
+{
+ GCState *gcstate = get_gc_state();
+ return gcstate->enabled;
+}
+
+/* Public API to invoke gc.collect() from C */
Py_ssize_t
PyGC_Collect(void)
{
@@ -2072,7 +2096,7 @@ PyGC_Collect(void)
PyObject *exc, *value, *tb;
gcstate->collecting = 1;
_PyErr_Fetch(tstate, &exc, &value, &tb);
- n = collect_with_callback(tstate, NUM_GENERATIONS - 1);
+ n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1);
_PyErr_Restore(tstate, exc, value, tb);
gcstate->collecting = 0;
}
@@ -2081,41 +2105,30 @@ PyGC_Collect(void)
}
Py_ssize_t
-_PyGC_CollectIfEnabled(void)
+_PyGC_CollectNoFail(PyThreadState *tstate)
{
- return PyGC_Collect();
-}
-
-Py_ssize_t
-_PyGC_CollectNoFail(void)
-{
- PyThreadState *tstate = _PyThreadState_GET();
- assert(!_PyErr_Occurred(tstate));
-
- GCState *gcstate = &tstate->interp->gc;
- Py_ssize_t n;
-
/* Ideally, this function is only called on interpreter shutdown,
and therefore not recursively. Unfortunately, when there are daemon
threads, a daemon thread can start a cyclic garbage collection
during interpreter shutdown (and then never finish it).
See http://bugs.python.org/issue8713#msg195178 for an example.
*/
+ GCState *gcstate = &tstate->interp->gc;
if (gcstate->collecting) {
- n = 0;
- }
- else {
- gcstate->collecting = 1;
- n = collect(tstate, NUM_GENERATIONS - 1, NULL, NULL, 1);
- gcstate->collecting = 0;
+ return 0;
}
+
+ Py_ssize_t n;
+ gcstate->collecting = 1;
+ n = gc_collect_main(tstate, NUM_GENERATIONS - 1, NULL, NULL, 1);
+ gcstate->collecting = 0;
return n;
}
void
-_PyGC_DumpShutdownStats(PyThreadState *tstate)
+_PyGC_DumpShutdownStats(PyInterpreterState *interp)
{
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = &interp->gc;
if (!(gcstate->debug & DEBUG_SAVEALL)
&& gcstate->garbage != NULL && PyList_GET_SIZE(gcstate->garbage) > 0) {
const char *message;
@@ -2162,13 +2175,13 @@ gc_fini_untrack(PyGC_Head *list)
void
-_PyGC_Fini(PyThreadState *tstate)
+_PyGC_Fini(PyInterpreterState *interp)
{
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = &interp->gc;
Py_CLEAR(gcstate->garbage);
Py_CLEAR(gcstate->callbacks);
- if (!_Py_IsMainInterpreter(tstate)) {
+ if (!_Py_IsMainInterpreter(interp)) {
// bpo-46070: Explicitly untrack all objects currently tracked by the
// GC. Otherwise, if an object is used later by another interpreter,
// calling PyObject_GC_UnTrack() on the object crashs if the previous
@@ -2275,7 +2288,7 @@ _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
!_PyErr_Occurred(tstate))
{
gcstate->collecting = 1;
- collect_generations(tstate);
+ gc_collect_generations(tstate);
gcstate->collecting = 0;
}
PyObject *op = FROM_GC(g);
@@ -2298,8 +2311,10 @@ PyObject *
_PyObject_GC_New(PyTypeObject *tp)
{
PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp));
- if (op != NULL)
- op = PyObject_INIT(op, tp);
+ if (op == NULL) {
+ return NULL;
+ }
+ _PyObject_Init(op, tp);
return op;
}
@@ -2315,8 +2330,10 @@ _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems)
}
size = _PyObject_VAR_SIZE(tp, nitems);
op = (PyVarObject *) _PyObject_GC_Malloc(size);
- if (op != NULL)
- op = PyObject_INIT_VAR(op, tp, nitems);
+ if (op == NULL) {
+ return NULL;
+ }
+ _PyObject_InitVar(op, tp, nitems);
return op;
}
@@ -2330,7 +2347,7 @@ _PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems)
}
PyGC_Head *g = AS_GC(op);
- g = (PyGC_Head *)PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize);
+ g = (PyGC_Head *)PyObject_Realloc(g, sizeof(PyGC_Head) + basicsize);
if (g == NULL)
return (PyVarObject *)PyErr_NoMemory();
op = (PyVarObject *) FROM_GC(g);
@@ -2345,12 +2362,11 @@ PyObject_GC_Del(void *op)
if (_PyObject_GC_IS_TRACKED(op)) {
gc_list_remove(g);
}
- PyThreadState *tstate = _PyThreadState_GET();
- GCState *gcstate = &tstate->interp->gc;
+ GCState *gcstate = get_gc_state();
if (gcstate->generations[0].count > 0) {
gcstate->generations[0].count--;
}
- PyObject_FREE(g);
+ PyObject_Free(g);
}
int