[Python-checkins] bpo-36854: Move _PyRuntimeState.gc to PyInterpreterState (GH-17287)

Victor Stinner webhook-mailer at python.org
Wed Nov 20 06:25:56 EST 2019


https://github.com/python/cpython/commit/7247407c35330f3f6292f1d40606b7ba6afd5700
commit: 7247407c35330f3f6292f1d40606b7ba6afd5700
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2019-11-20T12:25:50+01:00
summary:

bpo-36854: Move _PyRuntimeState.gc to PyInterpreterState (GH-17287)

* Rename _PyGC_InitializeRuntime() to _PyGC_InitState()
* finalize_interp_clear() now also calls _PyGC_Fini() in
  subinterpreters (clear the GC state).

files:
A Misc/NEWS.d/next/Core and Builtins/2019-11-20-12-01-37.bpo-36854.Zga_md.rst
M Include/internal/pycore_object.h
M Include/internal/pycore_pymem.h
M Include/internal/pycore_pystate.h
M Modules/gcmodule.c
M Objects/object.c
M Python/pylifecycle.c
M Python/pystate.c

diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 46555218bc5ea..ba6636d7f8cfc 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -38,7 +38,7 @@ static inline void _PyObject_GC_TRACK_impl(const char *filename, int lineno,
                           filename, lineno, "_PyObject_GC_TRACK");
 
     PyThreadState *tstate = _PyThreadState_GET();
-    PyGC_Head *generation0 = tstate->interp->runtime->gc.generation0;
+    PyGC_Head *generation0 = tstate->interp->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_pymem.h b/Include/internal/pycore_pymem.h
index 97d8fd99904a7..a4e972068348c 100644
--- a/Include/internal/pycore_pymem.h
+++ b/Include/internal/pycore_pymem.h
@@ -144,7 +144,7 @@ struct _gc_runtime_state {
     Py_ssize_t long_lived_pending;
 };
 
-PyAPI_FUNC(void) _PyGC_InitializeRuntime(struct _gc_runtime_state *);
+PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
 
 
 /* Set the memory allocator of the specified domain to the default.
diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h
index fec64a7badba1..0c3c1e3df34de 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -74,6 +74,8 @@ struct _is {
 
     int finalizing;
 
+    struct _gc_runtime_state gc;
+
     PyObject *modules;
     PyObject *modules_by_index;
     PyObject *sysdict;
@@ -130,9 +132,7 @@ struct _is {
     struct _warnings_runtime_state warnings;
 
     PyObject *audit_hooks;
-/*
- * See bpo-36876: miscellaneous ad hoc statics have been moved here.
- */
+
     struct {
         struct {
             int level;
@@ -239,7 +239,6 @@ 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/2019-11-20-12-01-37.bpo-36854.Zga_md.rst b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-12-01-37.bpo-36854.Zga_md.rst
new file mode 100644
index 0000000000000..2b4d5b3bc8590
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-11-20-12-01-37.bpo-36854.Zga_md.rst	
@@ -0,0 +1,3 @@
+The garbage collector state becomes per interpreter
+(``PyInterpreterState.gc``), rather than being global
+(``_PyRuntimeState.gc``).
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 6fce9a8a0b6d7..967bbe9c0d45d 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -133,7 +133,7 @@ static PyObject *gc_str = NULL;
 #define GEN_HEAD(gcstate, n) (&(gcstate)->generations[n].head)
 
 void
-_PyGC_InitializeRuntime(GCState *gcstate)
+_PyGC_InitState(GCState *gcstate)
 {
     gcstate->enabled = 1; /* automatic collection enabled? */
 
@@ -159,7 +159,7 @@ _PyGC_InitializeRuntime(GCState *gcstate)
 PyStatus
 _PyGC_Init(PyThreadState *tstate)
 {
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (gcstate->garbage == NULL) {
         gcstate->garbage = PyList_New(0);
         if (gcstate->garbage == NULL) {
@@ -1159,7 +1159,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->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
 
     if (gcstate->debug & DEBUG_STATS) {
         PySys_WriteStderr("gc: collecting generation %d...\n", generation);
@@ -1324,7 +1324,7 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
     assert(!_PyErr_Occurred(tstate));
 
     /* we may get called very early */
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (gcstate->callbacks == NULL) {
         return;
     }
@@ -1376,7 +1376,7 @@ collect_with_callback(PyThreadState *tstate, int generation)
 static Py_ssize_t
 collect_generations(PyThreadState *tstate)
 {
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     /* 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. */
@@ -1410,7 +1410,7 @@ gc_enable_impl(PyObject *module)
 /*[clinic end generated code: output=45a427e9dce9155c input=81ac4940ca579707]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     gcstate->enabled = 1;
     Py_RETURN_NONE;
 }
@@ -1426,7 +1426,7 @@ gc_disable_impl(PyObject *module)
 /*[clinic end generated code: output=97d1030f7aa9d279 input=8c2e5a14e800d83b]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     gcstate->enabled = 0;
     Py_RETURN_NONE;
 }
@@ -1442,7 +1442,7 @@ gc_isenabled_impl(PyObject *module)
 /*[clinic end generated code: output=1874298331c49130 input=30005e0422373b31]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     return gcstate->enabled;
 }
 
@@ -1471,7 +1471,7 @@ gc_collect_impl(PyObject *module, int generation)
         return -1;
     }
 
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     Py_ssize_t n;
     if (gcstate->collecting) {
         /* already collecting, don't do anything */
@@ -1508,7 +1508,7 @@ gc_set_debug_impl(PyObject *module, int flags)
 /*[clinic end generated code: output=7c8366575486b228 input=5e5ce15e84fbed15]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     gcstate->debug = flags;
     Py_RETURN_NONE;
 }
@@ -1524,7 +1524,7 @@ gc_get_debug_impl(PyObject *module)
 /*[clinic end generated code: output=91242f3506cd1e50 input=91a101e1c3b98366]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     return gcstate->debug;
 }
 
@@ -1538,7 +1538,7 @@ static PyObject *
 gc_set_threshold(PyObject *self, PyObject *args)
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (!PyArg_ParseTuple(args, "i|ii:set_threshold",
                           &gcstate->generations[0].threshold,
                           &gcstate->generations[1].threshold,
@@ -1562,7 +1562,7 @@ gc_get_threshold_impl(PyObject *module)
 /*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     return Py_BuildValue("(iii)",
                          gcstate->generations[0].threshold,
                          gcstate->generations[1].threshold,
@@ -1580,7 +1580,7 @@ gc_get_count_impl(PyObject *module)
 /*[clinic end generated code: output=354012e67b16398f input=a392794a08251751]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     return Py_BuildValue("(iii)",
                          gcstate->generations[0].count,
                          gcstate->generations[1].count,
@@ -1630,7 +1630,7 @@ gc_get_referrers(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     for (i = 0; i < NUM_GENERATIONS; i++) {
         if (!(gc_referrers_for(args, GEN_HEAD(gcstate, i), result))) {
             Py_DECREF(result);
@@ -1695,7 +1695,7 @@ gc_get_objects_impl(PyObject *module, Py_ssize_t generation)
     PyThreadState *tstate = _PyThreadState_GET();
     int i;
     PyObject* result;
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
 
     result = PyList_New(0);
     if (result == NULL) {
@@ -1754,7 +1754,7 @@ gc_get_stats_impl(PyObject *module)
 
     /* To get consistent values despite allocations while constructing
        the result list, we use a snapshot of the running stats. */
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     for (i = 0; i < NUM_GENERATIONS; i++) {
         stats[i] = gcstate->generation_stats[i];
     }
@@ -1827,7 +1827,7 @@ gc_freeze_impl(PyObject *module)
 /*[clinic end generated code: output=502159d9cdc4c139 input=b602b16ac5febbe5]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     for (int i = 0; i < NUM_GENERATIONS; ++i) {
         gc_list_merge(GEN_HEAD(gcstate, i), &gcstate->permanent_generation.head);
         gcstate->generations[i].count = 0;
@@ -1848,7 +1848,7 @@ gc_unfreeze_impl(PyObject *module)
 /*[clinic end generated code: output=1c15f2043b25e169 input=2dd52b170f4cef6c]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     gc_list_merge(&gcstate->permanent_generation.head,
                   GEN_HEAD(gcstate, NUM_GENERATIONS-1));
     Py_RETURN_NONE;
@@ -1865,7 +1865,7 @@ gc_get_freeze_count_impl(PyObject *module)
 /*[clinic end generated code: output=61cbd9f43aa032e1 input=45ffbc65cfe2a6ed]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     return gc_list_size(&gcstate->permanent_generation.head);
 }
 
@@ -1929,34 +1929,38 @@ static struct PyModuleDef gcmodule = {
 PyMODINIT_FUNC
 PyInit_gc(void)
 {
-    PyObject *m;
+    PyThreadState *tstate = _PyThreadState_GET();
+    GCState *gcstate = &tstate->interp->gc;
 
-    m = PyModule_Create(&gcmodule);
+    PyObject *m = PyModule_Create(&gcmodule);
 
     if (m == NULL) {
         return NULL;
     }
 
-    GCState *gcstate = &_PyRuntime.gc;
     if (gcstate->garbage == NULL) {
         gcstate->garbage = PyList_New(0);
-        if (gcstate->garbage == NULL)
+        if (gcstate->garbage == NULL) {
             return NULL;
+        }
     }
     Py_INCREF(gcstate->garbage);
-    if (PyModule_AddObject(m, "garbage", gcstate->garbage) < 0)
+    if (PyModule_AddObject(m, "garbage", gcstate->garbage) < 0) {
         return NULL;
+    }
 
     if (gcstate->callbacks == NULL) {
         gcstate->callbacks = PyList_New(0);
-        if (gcstate->callbacks == NULL)
+        if (gcstate->callbacks == NULL) {
             return NULL;
+        }
     }
     Py_INCREF(gcstate->callbacks);
-    if (PyModule_AddObject(m, "callbacks", gcstate->callbacks) < 0)
+    if (PyModule_AddObject(m, "callbacks", gcstate->callbacks) < 0) {
         return NULL;
+    }
 
-#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return NULL
+#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) { return NULL; }
     ADD_INT(DEBUG_STATS);
     ADD_INT(DEBUG_COLLECTABLE);
     ADD_INT(DEBUG_UNCOLLECTABLE);
@@ -1971,7 +1975,7 @@ Py_ssize_t
 PyGC_Collect(void)
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
 
     if (!gcstate->enabled) {
         return 0;
@@ -2006,7 +2010,7 @@ _PyGC_CollectNoFail(void)
     PyThreadState *tstate = _PyThreadState_GET();
     assert(!_PyErr_Occurred(tstate));
 
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     Py_ssize_t n;
 
     /* Ideally, this function is only called on interpreter shutdown,
@@ -2029,7 +2033,7 @@ _PyGC_CollectNoFail(void)
 void
 _PyGC_DumpShutdownStats(PyThreadState *tstate)
 {
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (!(gcstate->debug & DEBUG_SAVEALL)
         && gcstate->garbage != NULL && PyList_GET_SIZE(gcstate->garbage) > 0) {
         const char *message;
@@ -2066,7 +2070,7 @@ _PyGC_DumpShutdownStats(PyThreadState *tstate)
 void
 _PyGC_Fini(PyThreadState *tstate)
 {
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     Py_CLEAR(gcstate->garbage);
     Py_CLEAR(gcstate->callbacks);
 }
@@ -2131,7 +2135,7 @@ static PyObject *
 _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) {
         return _PyErr_NoMemory(tstate);
     }
@@ -2230,7 +2234,7 @@ PyObject_GC_Del(void *op)
         gc_list_remove(g);
     }
     PyThreadState *tstate = _PyThreadState_GET();
-    GCState *gcstate = &tstate->interp->runtime->gc;
+    GCState *gcstate = &tstate->interp->gc;
     if (gcstate->generations[0].count > 0) {
         gcstate->generations[0].count--;
     }
diff --git a/Objects/object.c b/Objects/object.c
index 3e612825c2777..6fc114621c1d1 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -2131,11 +2131,14 @@ Py_ReprLeave(PyObject *obj)
 void
 _PyTrash_deposit_object(PyObject *op)
 {
+    PyThreadState *tstate = _PyThreadState_GET();
+    struct _gc_runtime_state *gcstate = &tstate->interp->gc;
+
     _PyObject_ASSERT(op, PyObject_IS_GC(op));
     _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
     _PyObject_ASSERT(op, op->ob_refcnt == 0);
-    _PyGCHead_SET_PREV(_Py_AS_GC(op), _PyRuntime.gc.trash_delete_later);
-    _PyRuntime.gc.trash_delete_later = op;
+    _PyGCHead_SET_PREV(_Py_AS_GC(op), gcstate->trash_delete_later);
+    gcstate->trash_delete_later = op;
 }
 
 /* The equivalent API, using per-thread state recursion info */
@@ -2156,11 +2159,14 @@ _PyTrash_thread_deposit_object(PyObject *op)
 void
 _PyTrash_destroy_chain(void)
 {
-    while (_PyRuntime.gc.trash_delete_later) {
-        PyObject *op = _PyRuntime.gc.trash_delete_later;
+    PyThreadState *tstate = _PyThreadState_GET();
+    struct _gc_runtime_state *gcstate = &tstate->interp->gc;
+
+    while (gcstate->trash_delete_later) {
+        PyObject *op = gcstate->trash_delete_later;
         destructor dealloc = Py_TYPE(op)->tp_dealloc;
 
-        _PyRuntime.gc.trash_delete_later =
+        gcstate->trash_delete_later =
             (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op));
 
         /* Call the deallocator directly.  This used to try to
@@ -2170,9 +2176,9 @@ _PyTrash_destroy_chain(void)
          * up distorting allocation statistics.
          */
         _PyObject_ASSERT(op, op->ob_refcnt == 0);
-        ++_PyRuntime.gc.trash_delete_nesting;
+        ++gcstate->trash_delete_nesting;
         (*dealloc)(op);
-        --_PyRuntime.gc.trash_delete_nesting;
+        --gcstate->trash_delete_nesting;
     }
 }
 
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 41b9596b60d01..2149dbf569d6b 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1219,8 +1219,9 @@ finalize_interp_clear(PyThreadState *tstate, int is_main_interp)
         PyGrammar_RemoveAccelerators(&_PyParser_Grammar);
 
         _PyExc_Fini();
-        _PyGC_Fini(tstate);
     }
+
+    _PyGC_Fini(tstate);
 }
 
 
diff --git a/Python/pystate.c b/Python/pystate.c
index 93f0ce798888d..2fc563bf5836d 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -58,7 +58,6 @@ _PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
     runtime->open_code_userdata = open_code_userdata;
     runtime->audit_hook_head = audit_hook_head;
 
-    _PyGC_InitializeRuntime(&runtime->gc);
     _PyEval_Initialize(&runtime->ceval);
 
     PyPreConfig_InitPythonConfig(&runtime->preconfig);
@@ -208,6 +207,7 @@ PyInterpreterState_New(void)
     _PyRuntimeState *runtime = &_PyRuntime;
     interp->runtime = runtime;
 
+    _PyGC_InitState(&interp->gc);
     PyConfig_InitPythonConfig(&interp->config);
 
     interp->eval_frame = _PyEval_EvalFrameDefault;



More information about the Python-checkins mailing list