diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index a23dca805491d0..cdee52b17ebe2d 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -161,7 +161,7 @@ struct _gc_runtime_state { Py_ssize_t long_lived_pending; }; -extern void _PyGC_InitState(struct _gc_runtime_state *); +extern void _PyGC_InitializeRuntime(struct _gc_runtime_state *); extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index d48ea87fd67fe3..96b73cb34181a7 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,7 +16,6 @@ extern "C" { #include "pycore_floatobject.h" // struct _Py_float_state #include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_gil.h" // struct _gil_runtime_state -#include "pycore_gc.h" // struct _gc_runtime_state #include "pycore_list.h" // struct _Py_list_state #include "pycore_tuple.h" // struct _Py_tuple_state #include "pycore_typeobject.h" // struct type_cache @@ -105,7 +104,6 @@ struct _is { int finalizing; struct _ceval_state ceval; - struct _gc_runtime_state gc; // sys.modules dictionary PyObject *modules; diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 9041a4dc8a3ce5..47e83a96bc3c95 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -85,8 +85,8 @@ static inline void _PyObject_GC_TRACK( "object is in generation which is garbage collected", filename, lineno, __func__); - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyGC_Head *generation0 = interp->gc.generation0; + _PyRuntimeState *runtime = &_PyRuntime; + PyGC_Head *generation0 = runtime->gc.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 766e889f237b96..0df9568a005c4d 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -62,7 +62,7 @@ extern PyStatus _PyBuiltins_AddExceptions(PyObject * bltinmod); extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyImportZip_Init(PyThreadState *tstate); -extern PyStatus _PyGC_Init(PyInterpreterState *interp); +extern PyStatus _PyGC_Init(void); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); @@ -73,7 +73,7 @@ extern void _PySignal_Fini(void); extern void _PyImport_Fini(void); extern void _PyImport_Fini2(void); -extern void _PyGC_Fini(PyInterpreterState *interp); +extern void _PyGC_Fini(void); extern void _Py_HashRandomization_Fini(void); extern void _PyFaulthandler_Fini(void); extern void _PyHash_Fini(void); @@ -86,7 +86,7 @@ extern PyStatus _PyGILState_Init(_PyRuntimeState *runtime); extern PyStatus _PyGILState_SetTstate(PyThreadState *tstate); extern void _PyGILState_Fini(PyInterpreterState *interp); -PyAPI_FUNC(void) _PyGC_DumpShutdownStats(PyInterpreterState *interp); +PyAPI_FUNC(void) _PyGC_DumpShutdownStats(void); PyAPI_FUNC(PyStatus) _Py_PreInitializeFromPyArgv( const PyPreConfig *src_config, diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 725c859ea7853d..2d5030a5f56545 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -9,6 +9,7 @@ extern "C" { #endif #include "pycore_atomic.h" /* _Py_atomic_address */ +#include "pycore_gc.h" // struct _gc_runtime_state #include "pycore_gil.h" // struct _gil_runtime_state #include "pycore_global_objects.h" // struct _Py_global_objects #include "pycore_unicodeobject.h" // struct _Py_unicode_runtime_ids @@ -105,6 +106,7 @@ typedef struct pyruntimestate { void (*exitfuncs[NEXITFUNCS])(void); int nexitfuncs; + struct _gc_runtime_state gc; struct _ceval_runtime_state ceval; struct _gilstate_runtime_state gilstate; 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 1808057a650e98..ebd23c837a5daa 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -131,13 +131,13 @@ gc_decref(PyGC_Head *g) static GCState * get_gc_state(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return &interp->gc; + _PyRuntimeState *runtime = &_PyRuntime; + return &runtime->gc; } void -_PyGC_InitState(GCState *gcstate) +_PyGC_InitializeRuntime(GCState *gcstate) { gcstate->enabled = 1; /* automatic collection enabled? */ @@ -161,9 +161,9 @@ _PyGC_InitState(GCState *gcstate) PyStatus -_PyGC_Init(PyInterpreterState *interp) +_PyGC_Init(void) { - GCState *gcstate = &interp->gc; + GCState *gcstate = get_gc_state(); gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { @@ -1192,7 +1192,7 @@ gc_collect_main(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(); // gc_collect_main() must not be called before _PyGC_Init // or after _PyGC_Fini() @@ -1366,7 +1366,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; } @@ -1418,7 +1418,7 @@ gc_collect_with_callback(PyThreadState *tstate, int generation) static Py_ssize_t gc_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. */ @@ -1539,7 +1539,7 @@ gc_collect_impl(PyObject *module, int 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 */ @@ -1760,10 +1760,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; @@ -1777,16 +1776,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; } @@ -2079,9 +2078,7 @@ PyGC_IsEnabled(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; } @@ -2094,6 +2091,7 @@ PyGC_Collect(void) else { PyObject *exc, *value, *tb; gcstate->collecting = 1; + PyThreadState *tstate = _PyThreadState_GET(); _PyErr_Fetch(tstate, &exc, &value, &tb); n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1); _PyErr_Restore(tstate, exc, value, tb); @@ -2112,7 +2110,7 @@ _PyGC_CollectNoFail(PyThreadState *tstate) during interpreter shutdown (and then never finish it). See http://bugs.python.org/issue8713#msg195178 for an example. */ - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); if (gcstate->collecting) { return 0; } @@ -2125,9 +2123,9 @@ _PyGC_CollectNoFail(PyThreadState *tstate) } void -_PyGC_DumpShutdownStats(PyInterpreterState *interp) +_PyGC_DumpShutdownStats(void) { - GCState *gcstate = &interp->gc; + GCState *gcstate = get_gc_state(); if (!(gcstate->debug & DEBUG_SAVEALL) && gcstate->garbage != NULL && PyList_GET_SIZE(gcstate->garbage) > 0) { const char *message; @@ -2162,9 +2160,9 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp) } void -_PyGC_Fini(PyInterpreterState *interp) +_PyGC_Fini(void) { - GCState *gcstate = &interp->gc; + GCState *gcstate = get_gc_state(); Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); } @@ -2237,8 +2235,7 @@ _PyObject_GC_Link(PyObject *op) PyGC_Head *g = AS_GC(op); assert(((uintptr_t)g & (sizeof(uintptr_t)-1)) == 0); // g must be correctly aligned - PyThreadState *tstate = _PyThreadState_GET(); - GCState *gcstate = &tstate->interp->gc; + GCState *gcstate = get_gc_state(); g->_gc_next = 0; g->_gc_prev = 0; gcstate->generations[0].count++; /* number of allocated GC objects */ @@ -2246,9 +2243,10 @@ _PyObject_GC_Link(PyObject *op) gcstate->enabled && gcstate->generations[0].threshold && !gcstate->collecting && - !_PyErr_Occurred(tstate)) + !PyErr_Occurred()) { gcstate->collecting = 1; + PyThreadState *tstate = _PyThreadState_GET(); gc_collect_generations(tstate); gcstate->collecting = 0; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 284cfac3c40a51..48bcbbacedfc13 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -832,10 +832,12 @@ pycore_interp_init(PyThreadState *tstate) return status; } - // The GC must be initialized before the first GC collection. - status = _PyGC_Init(interp); - if (_PyStatus_EXCEPTION(status)) { - return status; + if (_Py_IsMainInterpreter(interp)) { + // The GC must be initialized before the first GC collection. + status = _PyGC_Init(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } } status = pycore_init_types(interp); @@ -1557,7 +1559,7 @@ finalize_modules(PyThreadState *tstate) // Dump GC stats before it's too late, since it uses the warnings // machinery. - _PyGC_DumpShutdownStats(interp); + _PyGC_DumpShutdownStats(); if (weaklist != NULL) { // Now, if there are any modules left alive, clear their globals to diff --git a/Python/pystate.c b/Python/pystate.c index 68fae8d283091d..7a370a15504ae3 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -100,6 +100,7 @@ init_runtime(_PyRuntimeState *runtime, runtime->audit_hook_head = audit_hook_head; _PyEval_InitRuntimeState(&runtime->ceval); + _PyGC_InitializeRuntime(&runtime->gc); PyPreConfig_InitPythonConfig(&runtime->preconfig); @@ -283,7 +284,6 @@ init_interpreter(PyInterpreterState *interp, interp->next = next; _PyEval_InitState(&interp->ceval, pending_lock); - _PyGC_InitState(&interp->gc); PyConfig_InitPythonConfig(&interp->config); _PyType_InitCache(interp); interp->eval_frame = NULL; @@ -426,7 +426,9 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) /* Last garbage collection on this interpreter */ _PyGC_CollectNoFail(tstate); - _PyGC_Fini(interp); + if (_Py_IsMainInterpreter(interp)) { + _PyGC_Fini(); + } /* We don't clear sysdict and builtins until the end of this function. Because clearing other attributes can execute arbitrary Python code diff --git a/Python/traceback.c b/Python/traceback.c index 4d6cbaae8da6cd..95f468358c33e1 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -1309,6 +1309,7 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, return "unable to get the thread head state"; /* Dump the traceback of each thread */ + _PyRuntimeState *runtime = &_PyRuntime; tstate = PyInterpreterState_ThreadHead(interp); nthreads = 0; _Py_BEGIN_SUPPRESS_IPH @@ -1321,7 +1322,7 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, break; } write_thread_id(fd, tstate, tstate == current_tstate); - if (tstate == current_tstate && tstate->interp->gc.collecting) { + if (tstate == current_tstate && runtime->gc.collecting) { PUTS(fd, " Garbage-collecting\n"); } dump_traceback(fd, tstate, 0);