diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 551ad833bb6927..69c0db2077e184 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -86,6 +86,8 @@ struct _is { int finalizing; struct _ceval_state ceval; + // bpo-46070: Even if each PyInterpreterState has a GC state, + // _PyGC_GetState() only uses the state of the main interpreter. struct _gc_runtime_state gc; PyObject *modules; diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index c66ff12d7ec5a7..4277a657bbc070 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -18,6 +18,16 @@ PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content); /* Only private in Python 3.10 and 3.9.8+; public in 3.11 */ extern PyObject *_PyType_GetQualName(PyTypeObject *type); +static inline struct _gc_runtime_state* +_PyGC_GetState(void) +{ + // bpo-46070: Even if each PyInterpreterState has a GC state, + // _PyGC_GetState() only uses the state of the main interpreter. + PyInterpreterState *interp = _PyRuntime.interpreters.main; + return &interp->gc; +} + + /* Tell the GC to track this object. * * NB: While the object is tracked by the collector, it must be safe to call the @@ -42,8 +52,8 @@ static inline void _PyObject_GC_TRACK_impl(const char *filename, int lineno, "object is in generation which is garbage collected", filename, lineno, "_PyObject_GC_TRACK"); - PyThreadState *tstate = _PyThreadState_GET(); - PyGC_Head *generation0 = tstate->interp->gc.generation0; + struct _gc_runtime_state *state = _PyGC_GetState(); + PyGC_Head *generation0 = state->generation0; PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 50ab645fc74859..fbee85146315b5 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -53,7 +53,7 @@ extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyTypes_Init(void); extern PyStatus _PyTypes_InitSlotDefs(void); extern PyStatus _PyImportZip_Init(PyThreadState *tstate); -extern PyStatus _PyGC_Init(PyThreadState *tstate); +extern PyStatus _PyGC_Init(void); /* Various internal finalizers */ @@ -74,7 +74,7 @@ extern void PyOS_FiniInterrupts(void); extern void _PyExc_Fini(void); extern void _PyImport_Fini(void); extern void _PyImport_Fini2(void); -extern void _PyGC_Fini(PyThreadState *tstate); +extern void _PyGC_Fini(void); extern void _PyType_Fini(void); extern void _Py_HashRandomization_Fini(void); extern void _PyUnicode_Fini(PyThreadState *tstate); @@ -88,7 +88,7 @@ extern void _PyAST_Fini(void); extern PyStatus _PyGILState_Init(PyThreadState *tstate); extern void _PyGILState_Fini(PyThreadState *tstate); -PyAPI_FUNC(void) _PyGC_DumpShutdownStats(PyThreadState *tstate); +PyAPI_FUNC(void) _PyGC_DumpShutdownStats(void); PyAPI_FUNC(PyStatus) _Py_PreInitializeFromPyArgv( const PyPreConfig *src_config, diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-12-23-13-39.bpo-46070.cQp0eC.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-12-23-13-39.bpo-46070.cQp0eC.rst new file mode 100644 index 00000000000000..c55578ff87f5d8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-12-23-13-39.bpo-46070.cQp0eC.rst @@ -0,0 +1,3 @@ +Fix a random crash involving subinterpreters on Windows. Revert the change +which made the gc module state per interpreter: the gc module state is +shared again by all interpreters. Patch by Victor Stinner. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 2c6ef9252eae86..093a12e07438dd 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -128,6 +128,14 @@ gc_decref(PyGC_Head *g) #define GEN_HEAD(gcstate, n) (&(gcstate)->generations[n].head) + +static GCState * +get_gc_state(void) +{ + return _PyGC_GetState(); +} + + void _PyGC_InitState(GCState *gcstate) { @@ -153,9 +161,9 @@ _PyGC_InitState(GCState *gcstate) PyStatus -_PyGC_Init(PyThreadState *tstate) +_PyGC_Init(void) { - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (gcstate->garbage == NULL) { gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { @@ -1179,7 +1187,7 @@ collect(PyThreadState *tstate, int generation, PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ PyGC_Head *gc; _PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (gcstate->debug & DEBUG_STATS) { PySys_WriteStderr("gc: collecting generation %d...\n", generation); @@ -1341,7 +1349,7 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, assert(!_PyErr_Occurred(tstate)); /* we may get called very early */ - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (gcstate->callbacks == NULL) { return; } @@ -1393,7 +1401,7 @@ collect_with_callback(PyThreadState *tstate, int generation) static Py_ssize_t collect_generations(PyThreadState *tstate) { - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); /* Find the oldest generation (highest numbered) where the count * exceeds the threshold. Objects in the that generation and * generations younger than it will be collected. */ @@ -1458,8 +1466,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 *gcstate = get_gc_state(); gcstate->enabled = 1; Py_RETURN_NONE; } @@ -1474,8 +1481,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 *gcstate = get_gc_state(); gcstate->enabled = 0; Py_RETURN_NONE; } @@ -1490,8 +1496,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; + GCState *gcstate = get_gc_state(); return gcstate->enabled; } @@ -1513,20 +1518,19 @@ static Py_ssize_t gc_collect_impl(PyObject *module, int generation) /*[clinic end generated code: output=b697e633043233c7 input=40720128b682d879]*/ { - PyThreadState *tstate = _PyThreadState_GET(); - if (generation < 0 || generation >= NUM_GENERATIONS) { - _PyErr_SetString(tstate, PyExc_ValueError, "invalid generation"); + PyErr_SetString(PyExc_ValueError, "invalid generation"); return -1; } - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); Py_ssize_t n; if (gcstate->collecting) { /* already collecting, don't do anything */ n = 0; } else { + PyThreadState *tstate = _PyThreadState_GET(); gcstate->collecting = 1; n = collect_with_callback(tstate, generation); gcstate->collecting = 0; @@ -1556,8 +1560,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 +1575,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 +1588,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 +1611,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 +1628,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 +1671,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 +1680,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; @@ -1749,10 +1745,9 @@ static PyObject * gc_get_objects_impl(PyObject *module, Py_ssize_t generation) /*[clinic end generated code: output=48b35fea4ba6cb0e input=ef7da9df9806754c]*/ { - PyThreadState *tstate = _PyThreadState_GET(); int i; PyObject* result; - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (PySys_Audit("gc.get_objects", "n", generation) < 0) { return NULL; @@ -1766,16 +1761,16 @@ gc_get_objects_impl(PyObject *module, Py_ssize_t generation) /* If generation is passed, we extract only that generation */ if (generation != -1) { if (generation >= NUM_GENERATIONS) { - _PyErr_Format(tstate, PyExc_ValueError, - "generation parameter must be less than the number of " - "available generations (%i)", - NUM_GENERATIONS); + PyErr_Format(PyExc_ValueError, + "generation parameter must be less than the number of " + "available generations (%i)", + NUM_GENERATIONS); goto error; } if (generation < 0) { - _PyErr_SetString(tstate, PyExc_ValueError, - "generation parameter cannot be negative"); + PyErr_SetString(PyExc_ValueError, + "generation parameter cannot be negative"); goto error; } @@ -1811,11 +1806,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 +1900,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 +1920,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 +1936,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); } @@ -2011,8 +2002,7 @@ static struct PyModuleDef gcmodule = { PyMODINIT_FUNC PyInit_gc(void) { - PyThreadState *tstate = _PyThreadState_GET(); - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); PyObject *m = PyModule_Create(&gcmodule); @@ -2056,9 +2046,7 @@ PyInit_gc(void) Py_ssize_t PyGC_Collect(void) { - PyThreadState *tstate = _PyThreadState_GET(); - GCState *gcstate = &tstate->interp->gc; - + GCState *gcstate = get_gc_state(); if (!gcstate->enabled) { return 0; } @@ -2071,6 +2059,7 @@ PyGC_Collect(void) else { PyObject *exc, *value, *tb; gcstate->collecting = 1; + PyThreadState *tstate = _PyThreadState_GET(); _PyErr_Fetch(tstate, &exc, &value, &tb); n = collect_with_callback(tstate, NUM_GENERATIONS - 1); _PyErr_Restore(tstate, exc, value, tb); @@ -2089,10 +2078,9 @@ _PyGC_CollectIfEnabled(void) Py_ssize_t _PyGC_CollectNoFail(void) { - PyThreadState *tstate = _PyThreadState_GET(); - assert(!_PyErr_Occurred(tstate)); + assert(!PyErr_Occurred()); - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); Py_ssize_t n; /* Ideally, this function is only called on interpreter shutdown, @@ -2105,6 +2093,7 @@ _PyGC_CollectNoFail(void) n = 0; } else { + PyThreadState *tstate = _PyThreadState_GET(); gcstate->collecting = 1; n = collect(tstate, NUM_GENERATIONS - 1, NULL, NULL, 1); gcstate->collecting = 0; @@ -2113,9 +2102,9 @@ _PyGC_CollectNoFail(void) } void -_PyGC_DumpShutdownStats(PyThreadState *tstate) +_PyGC_DumpShutdownStats(void) { - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (!(gcstate->debug & DEBUG_SAVEALL) && gcstate->garbage != NULL && PyList_GET_SIZE(gcstate->garbage) > 0) { const char *message; @@ -2150,9 +2139,9 @@ _PyGC_DumpShutdownStats(PyThreadState *tstate) } void -_PyGC_Fini(PyThreadState *tstate) +_PyGC_Fini(void) { - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); } @@ -2222,10 +2211,9 @@ PyObject_IS_GC(PyObject *obj) static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize) { - PyThreadState *tstate = _PyThreadState_GET(); - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) { - return _PyErr_NoMemory(tstate); + return PyErr_NoMemory(); } size_t size = sizeof(PyGC_Head) + basicsize; @@ -2237,7 +2225,7 @@ _PyObject_GC_Alloc(int use_calloc, size_t basicsize) g = (PyGC_Head *)PyObject_Malloc(size); } if (g == NULL) { - return _PyErr_NoMemory(tstate); + return PyErr_NoMemory(); } assert(((uintptr_t)g & 3) == 0); // g must be aligned 4bytes boundary @@ -2248,8 +2236,9 @@ _PyObject_GC_Alloc(int use_calloc, size_t basicsize) gcstate->enabled && gcstate->generations[0].threshold && !gcstate->collecting && - !_PyErr_Occurred(tstate)) + !PyErr_Occurred()) { + PyThreadState *tstate = _PyThreadState_GET(); gcstate->collecting = 1; collect_generations(tstate); gcstate->collecting = 0; @@ -2321,8 +2310,7 @@ 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--; } diff --git a/Objects/object.c b/Objects/object.c index 623ee52eb1e22d..71df28e4a4fcc9 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2029,8 +2029,7 @@ Py_ReprLeave(PyObject *obj) void _PyTrash_deposit_object(PyObject *op) { - PyThreadState *tstate = _PyThreadState_GET(); - struct _gc_runtime_state *gcstate = &tstate->interp->gc; + struct _gc_runtime_state *gcstate = _PyGC_GetState(); _PyObject_ASSERT(op, _PyObject_IS_GC(op)); _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op)); @@ -2057,8 +2056,7 @@ _PyTrash_thread_deposit_object(PyObject *op) void _PyTrash_destroy_chain(void) { - PyThreadState *tstate = _PyThreadState_GET(); - struct _gc_runtime_state *gcstate = &tstate->interp->gc; + struct _gc_runtime_state *gcstate = _PyGC_GetState(); while (gcstate->trash_delete_later) { PyObject *op = gcstate->trash_delete_later; diff --git a/Python/import.c b/Python/import.c index 8358d70f468dd9..d85e0439936bf4 100644 --- a/Python/import.c +++ b/Python/import.c @@ -575,7 +575,7 @@ _PyImport_Cleanup(PyThreadState *tstate) _PyGC_CollectNoFail(); /* Dump GC stats before it's too late, since it uses the warnings machinery. */ - _PyGC_DumpShutdownStats(tstate); + _PyGC_DumpShutdownStats(); /* Now, if there are any modules left alive, clear their globals to minimize potential leaks. All C extension modules actually end diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 60f091cbbea145..c2ef18835206ec 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -579,12 +579,12 @@ pycore_init_types(PyThreadState *tstate) PyStatus status; int is_main_interp = _Py_IsMainInterpreter(tstate); - status = _PyGC_Init(tstate); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - if (is_main_interp) { + status = _PyGC_Init(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + status = _PyTypes_Init(); if (_PyStatus_EXCEPTION(status)) { return status; @@ -1317,9 +1317,9 @@ finalize_interp_clear(PyThreadState *tstate) PyGrammar_RemoveAccelerators(&_PyParser_Grammar); _PyExc_Fini(); - } - _PyGC_Fini(tstate); + _PyGC_Fini(); + } }