From f531b6879b530515b009ac79767702829848cf07 Mon Sep 17 00:00:00 2001
From: Eric Snow <ericsnowcurrently@gmail.com>
Date: Fri, 11 Nov 2022 14:24:18 -0700
Subject: [PATCH] gh-81057: Add PyInterpreterState.static_objects (gh-99397)

As we consolidate global variables, we find some objects that are almost suitable to add to _PyRuntimeState.global_objects, but have some small/sneaky bit of per-interpreter state (e.g. a weakref list). We're adding PyInterpreterState.static_objects so we can move such objects there. (We'll removed the _not_used field once we've added others.)

https://github.com/python/cpython/issues/81057
---
 Include/internal/pycore_global_objects.h       | 18 ++++++++++++++++++
 .../pycore_global_objects_fini_generated.h     |  5 +++--
 Include/internal/pycore_interp.h               |  4 ++++
 Include/internal/pycore_runtime_init.h         |  5 +++++
 Python/pylifecycle.c                           |  2 +-
 Tools/build/generate_global_objects.py         |  6 ++++--
 6 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/Include/internal/pycore_global_objects.h b/Include/internal/pycore_global_objects.h
index 82e89db7b1b..3561f686a0d 100644
--- a/Include/internal/pycore_global_objects.h
+++ b/Include/internal/pycore_global_objects.h
@@ -49,6 +49,24 @@ struct _Py_global_objects {
     PyObject *interned;
 };
 
+#define _Py_INTERP_CACHED_OBJECT(interp, NAME) \
+    (interp)->cached_objects.NAME
+
+struct _Py_interp_cached_objects {
+    int _not_set;
+};
+
+#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \
+    (interp)->static_objects.NAME
+#define _Py_INTERP_SINGLETON(interp, NAME) \
+    _Py_INTERP_STATIC_OBJECT(interp, singletons.NAME)
+
+struct _Py_interp_static_objects {
+    struct {
+        int _not_used;
+    } singletons;
+};
+
 
 #ifdef __cplusplus
 }
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index 7bf31841ff5..59001dc9509 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -24,8 +24,9 @@ _PyStaticObject_CheckRefcnt(PyObject *obj) {
 /* The following is auto-generated by Tools/build/generate_global_objects.py. */
 #ifdef Py_DEBUG
 static inline void
-_PyStaticObjects_CheckRefcnt(void) {
-    /* generated (see pycore_runtime_init_generated.h) */
+_PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
+    /* generated runtime-global */
+    // (see pycore_runtime_init_generated.h)
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_SINGLETON(small_ints)[_PY_NSMALLNEGINTS + -5]);
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_SINGLETON(small_ints)[_PY_NSMALLNEGINTS + -4]);
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_SINGLETON(small_ints)[_PY_NSMALLNEGINTS + -3]);
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 068b0a700af..976e16a3742 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -20,6 +20,7 @@ extern "C" {
 #include "pycore_genobject.h"     // struct _Py_async_gen_state
 #include "pycore_gc.h"            // struct _gc_runtime_state
 #include "pycore_list.h"          // struct _Py_list_state
+#include "pycore_global_objects.h"  // struct _Py_interp_static_objects
 #include "pycore_tuple.h"         // struct _Py_tuple_state
 #include "pycore_typeobject.h"    // struct type_cache
 #include "pycore_unicodeobject.h" // struct _Py_unicode_state
@@ -207,6 +208,9 @@ struct _is {
     struct callable_cache callable_cache;
     PyCodeObject *interpreter_trampoline;
 
+    struct _Py_interp_cached_objects cached_objects;
+    struct _Py_interp_static_objects static_objects;
+
     /* The following fields are here to avoid allocation during init.
        The data is exposed through PyInterpreterState pointer fields.
        These fields should not be accessed directly outside of init.
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index 4e8dd7b8a0f..41a7730490a 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -77,6 +77,11 @@ extern "C" {
                 { .threshold = 10, }, \
             }, \
         }, \
+        .static_objects = { \
+            .singletons = { \
+                ._not_used = 1, \
+            }, \
+        }, \
         ._initial_thread = _PyThreadState_INIT, \
     }
 
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 13519762fa8..3991089b38c 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1744,7 +1744,7 @@ finalize_interp_types(PyInterpreterState *interp)
     _PyUnicode_Fini(interp);
     _PyFloat_Fini(interp);
 #ifdef Py_DEBUG
-    _PyStaticObjects_CheckRefcnt();
+    _PyStaticObjects_CheckRefcnt(interp);
 #endif
 }
 
diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py
index 815045c27c1..b4243273ff4 100644
--- a/Tools/build/generate_global_objects.py
+++ b/Tools/build/generate_global_objects.py
@@ -383,8 +383,10 @@ def generate_global_object_finalizers(generated_immortal_objects):
         printer.write(START)
         printer.write('#ifdef Py_DEBUG')
         printer.write("static inline void")
-        with printer.block("_PyStaticObjects_CheckRefcnt(void)"):
-            printer.write('/* generated (see pycore_runtime_init_generated.h) */')
+        with printer.block(
+                "_PyStaticObjects_CheckRefcnt(PyInterpreterState *interp)"):
+            printer.write('/* generated runtime-global */')
+            printer.write('// (see pycore_runtime_init_generated.h)')
             for ref in generated_immortal_objects:
                 printer.write(f'_PyStaticObject_CheckRefcnt({ref});')
             printer.write('/* non-generated */')
-- 
GitLab