Skip to content

Commit a088290

Browse files
gh-81057: Move Global Variables Holding Objects to _PyRuntimeState. (gh-99487)
This moves nearly all remaining object-holding globals in core code (other than static types). #81057
1 parent 619cadc commit a088290

File tree

10 files changed

+66
-55
lines changed

10 files changed

+66
-55
lines changed

Include/internal/pycore_global_objects.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include "pycore_gc.h" // PyGC_Head
1212
#include "pycore_global_strings.h" // struct _Py_global_strings
13+
#include "pycore_typeobject.h" // pytype_slotdef
1314

1415

1516
// These would be in pycore_long.h if it weren't for an include cycle.
@@ -20,6 +21,13 @@ extern "C" {
2021
// Only immutable objects should be considered runtime-global.
2122
// All others must be per-interpreter.
2223

24+
#define _Py_CACHED_OBJECT(NAME) \
25+
_PyRuntime.cached_objects.NAME
26+
27+
struct _Py_cached_objects {
28+
PyObject *str_replace_inf;
29+
};
30+
2331
#define _Py_GLOBAL_OBJECT(NAME) \
2432
_PyRuntime.global_objects.NAME
2533
#define _Py_SINGLETON(NAME) \
@@ -54,6 +62,10 @@ struct _Py_global_objects {
5462

5563
struct _Py_interp_cached_objects {
5664
int _not_set;
65+
/* object.__reduce__ */
66+
PyObject *objreduce;
67+
PyObject *type_slots_pname;
68+
pytype_slotdef *type_slots_ptrs[MAX_EQUIV];
5769
};
5870

5971
#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \

Include/internal/pycore_runtime.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,15 @@ typedef struct pyruntimestate {
136136

137137
struct _Py_unicode_runtime_ids unicode_ids;
138138

139+
struct {
140+
/* Used to set PyTypeObject.tp_version_tag */
141+
// bpo-42745: next_version_tag remains shared by all interpreters
142+
// because of static types.
143+
unsigned int next_version_tag;
144+
} types;
145+
139146
/* All the objects that are shared by the runtime's interpreters. */
147+
struct _Py_cached_objects cached_objects;
140148
struct _Py_global_objects global_objects;
141149

142150
/* The following fields are here to avoid allocation during init.

Include/internal/pycore_runtime_init.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ extern "C" {
3636
until _PyInterpreterState_Enable() is called. */ \
3737
.next_id = -1, \
3838
}, \
39+
.types = { \
40+
.next_version_tag = 1, \
41+
}, \
3942
.global_objects = { \
4043
.singletons = { \
4144
.small_ints = _Py_small_ints_INIT, \

Include/internal/pycore_typeobject.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ extern void _PyTypes_Fini(PyInterpreterState *);
1818

1919
/* other API */
2020

21+
/* Length of array of slotdef pointers used to store slots with the
22+
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
23+
the same __name__, for any __name__. Since that's a static property, it is
24+
appropriate to declare fixed-size arrays for this. */
25+
#define MAX_EQUIV 10
26+
27+
typedef struct wrapperbase pytype_slotdef;
28+
29+
2130
// Type attribute lookup cache: speed up attribute and method lookups,
2231
// see _PyType_Lookup().
2332
struct type_cache_entry {

Objects/typeobject.c

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ class object "PyObject *" "&PyBaseObject_Type"
4343
PyUnicode_IS_READY(name) && \
4444
(PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)
4545

46-
// bpo-42745: next_version_tag remains shared by all interpreters because of static types
47-
// Used to set PyTypeObject.tp_version_tag
48-
static unsigned int next_version_tag = 1;
46+
#define next_version_tag (_PyRuntime.types.next_version_tag)
4947

5048
typedef struct PySlot_Offset {
5149
short subslot_offset;
@@ -5828,7 +5826,8 @@ static PyObject *
58285826
object___reduce_ex___impl(PyObject *self, int protocol)
58295827
/*[clinic end generated code: output=2e157766f6b50094 input=f326b43fb8a4c5ff]*/
58305828
{
5831-
static PyObject *objreduce;
5829+
#define objreduce \
5830+
(_Py_INTERP_CACHED_OBJECT(_PyInterpreterState_Get(), objreduce))
58325831
PyObject *reduce, *res;
58335832

58345833
if (objreduce == NULL) {
@@ -5864,6 +5863,7 @@ object___reduce_ex___impl(PyObject *self, int protocol)
58645863
}
58655864

58665865
return _common_reduce(self, protocol);
5866+
#undef objreduce
58675867
}
58685868

58695869
static PyObject *
@@ -8524,8 +8524,6 @@ __ne__ etc. all map to tp_richcompare) and one name may map to multiple slots
85248524
an all-zero entry.
85258525
*/
85268526

8527-
typedef struct wrapperbase slotdef;
8528-
85298527
#undef TPSLOT
85308528
#undef FLSLOT
85318529
#undef AMSLOT
@@ -8574,7 +8572,7 @@ typedef struct wrapperbase slotdef;
85748572
ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, \
85758573
#NAME "($self, value, /)\n--\n\n" DOC)
85768574

8577-
static slotdef slotdefs[] = {
8575+
static pytype_slotdef slotdefs[] = {
85788576
TPSLOT(__getattribute__, tp_getattr, NULL, NULL, ""),
85798577
TPSLOT(__getattr__, tp_getattr, NULL, NULL, ""),
85808578
TPSLOT(__setattr__, tp_setattr, NULL, NULL, ""),
@@ -8799,12 +8797,6 @@ slotptr(PyTypeObject *type, int ioffset)
87998797
return (void **)ptr;
88008798
}
88018799

8802-
/* Length of array of slotdef pointers used to store slots with the
8803-
same __name__. There should be at most MAX_EQUIV-1 slotdef entries with
8804-
the same __name__, for any __name__. Since that's a static property, it is
8805-
appropriate to declare fixed-size arrays for this. */
8806-
#define MAX_EQUIV 10
8807-
88088800
/* Return a slot pointer for a given name, but ONLY if the attribute has
88098801
exactly one slot function. The name must be an interned string. */
88108802
static void **
@@ -8813,9 +8805,10 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
88138805
/* XXX Maybe this could be optimized more -- but is it worth it? */
88148806

88158807
/* pname and ptrs act as a little cache */
8816-
static PyObject *pname;
8817-
static slotdef *ptrs[MAX_EQUIV];
8818-
slotdef *p, **pp;
8808+
PyInterpreterState *interp = _PyInterpreterState_Get();
8809+
#define pname _Py_INTERP_CACHED_OBJECT(interp, type_slots_pname)
8810+
#define ptrs _Py_INTERP_CACHED_OBJECT(interp, type_slots_ptrs)
8811+
pytype_slotdef *p, **pp;
88198812
void **res, **ptr;
88208813

88218814
if (pname != name) {
@@ -8842,6 +8835,8 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
88428835
res = ptr;
88438836
}
88448837
return res;
8838+
#undef pname
8839+
#undef ptrs
88458840
}
88468841

88478842

@@ -8899,8 +8894,8 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
88998894
* When done, return a pointer to the next slotdef with a different offset,
89008895
* because that's convenient for fixup_slot_dispatchers(). This function never
89018896
* sets an exception: if an internal error happens (unlikely), it's ignored. */
8902-
static slotdef *
8903-
update_one_slot(PyTypeObject *type, slotdef *p)
8897+
static pytype_slotdef *
8898+
update_one_slot(PyTypeObject *type, pytype_slotdef *p)
89048899
{
89058900
PyObject *descr;
89068901
PyWrapperDescrObject *d;
@@ -9015,7 +9010,7 @@ update_one_slot(PyTypeObject *type, slotdef *p)
90159010
static int
90169011
update_slots_callback(PyTypeObject *type, void *data)
90179012
{
9018-
slotdef **pp = (slotdef **)data;
9013+
pytype_slotdef **pp = (pytype_slotdef **)data;
90199014
for (; *pp; pp++) {
90209015
update_one_slot(type, *pp);
90219016
}
@@ -9026,9 +9021,9 @@ update_slots_callback(PyTypeObject *type, void *data)
90269021
static int
90279022
update_slot(PyTypeObject *type, PyObject *name)
90289023
{
9029-
slotdef *ptrs[MAX_EQUIV];
9030-
slotdef *p;
9031-
slotdef **pp;
9024+
pytype_slotdef *ptrs[MAX_EQUIV];
9025+
pytype_slotdef *p;
9026+
pytype_slotdef **pp;
90329027
int offset;
90339028

90349029
assert(PyUnicode_CheckExact(name));
@@ -9065,15 +9060,15 @@ static void
90659060
fixup_slot_dispatchers(PyTypeObject *type)
90669061
{
90679062
assert(!PyErr_Occurred());
9068-
for (slotdef *p = slotdefs; p->name; ) {
9063+
for (pytype_slotdef *p = slotdefs; p->name; ) {
90699064
p = update_one_slot(type, p);
90709065
}
90719066
}
90729067

90739068
static void
90749069
update_all_slots(PyTypeObject* type)
90759070
{
9076-
slotdef *p;
9071+
pytype_slotdef *p;
90779072

90789073
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
90799074
PyType_Modified(type);
@@ -9244,7 +9239,7 @@ static int
92449239
add_operators(PyTypeObject *type)
92459240
{
92469241
PyObject *dict = type->tp_dict;
9247-
slotdef *p;
9242+
pytype_slotdef *p;
92489243
PyObject *descr;
92499244
void **ptr;
92509245

Parser/asdl_c.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,10 @@ def generate_ast_fini(module_state, f):
14831483
for s in module_state:
14841484
f.write(" Py_CLEAR(state->" + s + ');\n')
14851485
f.write(textwrap.dedent("""
1486+
if (_PyInterpreterState_Get() == _PyInterpreterState_Main()) {
1487+
Py_CLEAR(_Py_CACHED_OBJECT(str_replace_inf));
1488+
}
1489+
14861490
#if !defined(NDEBUG)
14871491
state->initialized = -1;
14881492
#else

Python/Python-ast.c

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/ast_unparse.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ _Py_DECLARE_STR(open_br, "{");
1313
_Py_DECLARE_STR(dbl_open_br, "{{");
1414
_Py_DECLARE_STR(close_br, "}");
1515
_Py_DECLARE_STR(dbl_close_br, "}}");
16-
static PyObject *_str_replace_inf;
16+
#define _str_replace_inf _Py_CACHED_OBJECT(str_replace_inf)
1717

1818
/* Forward declarations for recursion via helper functions. */
1919
static PyObject *

Tools/c-analyzer/cpython/globals-to-fix.tsv

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,6 @@ Objects/setobject.c - _dummy_struct -
298298
Objects/setobject.c - _PySet_Dummy -
299299
Objects/sliceobject.c - _Py_EllipsisObject -
300300

301-
#-----------------------
302-
# cached - initialized once
303-
304-
# manually cached PyUnicodeObject
305-
Python/ast_unparse.c - _str_replace_inf -
306-
307-
# other
308-
Objects/typeobject.c object___reduce_ex___impl objreduce -
309-
310301
#-----------------------
311302
# other
312303

@@ -315,9 +306,6 @@ Python/context.c - _token_missing -
315306
Python/hamt.c - _empty_bitmap_node -
316307
Python/hamt.c - _empty_hamt -
317308

318-
# state
319-
Objects/typeobject.c resolve_slotdups pname -
320-
321309

322310
##################################
323311
# global non-objects to fix in core code
@@ -438,8 +426,6 @@ Python/perf_trampoline.c - perf_status -
438426
Python/perf_trampoline.c - extra_code_index -
439427
Python/perf_trampoline.c - code_arena -
440428
Python/perf_trampoline.c - trampoline_api -
441-
Objects/typeobject.c - next_version_tag -
442-
Objects/typeobject.c resolve_slotdups ptrs -
443429
Parser/pegen.c - memo_statistics -
444430
Python/bootstrap_hash.c - urandom_cache -
445431
Python/ceval_gil.c make_pending_calls busy -
@@ -513,27 +499,12 @@ Modules/itertoolsmodule.c - ziplongest_type -
513499
#-----------------------
514500
# other
515501

516-
# statically initializd pointer to static type
517-
# XXX should be const?
518-
Modules/_io/winconsoleio.c - _PyWindowsConsoleIO_Type -
519-
520-
# initialized once
521-
Modules/_functoolsmodule.c - kwd_mark -
522-
Modules/_io/_iomodule.c - _PyIO_empty_bytes -
523-
Modules/_testcapi/heaptype.c - _testcapimodule -
524-
Modules/_testcapi/unicode.c - _testcapimodule -
525-
Modules/_tracemalloc.c - tracemalloc_empty_traceback -
526-
Modules/signalmodule.c - DefaultHandler -
527-
Modules/signalmodule.c - IgnoreHandler -
528-
Modules/signalmodule.c - IntHandler -
529-
530502
# state
531503
Modules/faulthandler.c - fatal_error -
532504
Modules/faulthandler.c - thread -
533505
Modules/faulthandler.c - user_signals -
534506
Modules/faulthandler.c - stack -
535507
Modules/faulthandler.c - old_stack -
536-
Modules/signalmodule.c - Handlers -
537508

538509

539510
##################################
@@ -554,6 +525,7 @@ Modules/timemodule.c _PyTime_GetProcessTimeWithInfo ticks_per_second -
554525

555526
Modules/_tracemalloc.c - allocators -
556527
Modules/_tracemalloc.c - tables_lock -
528+
Modules/_tracemalloc.c - tracemalloc_empty_traceback -
557529
Modules/_tracemalloc.c - tracemalloc_traced_memory -
558530
Modules/_tracemalloc.c - tracemalloc_peak_traced_memory -
559531
Modules/_tracemalloc.c - tracemalloc_filenames -
@@ -567,6 +539,7 @@ Modules/posixmodule.c - environ -
567539
Modules/signalmodule.c - is_tripped -
568540
Modules/signalmodule.c - signal_global_state -
569541
Modules/signalmodule.c - wakeup -
542+
Modules/signalmodule.c - Handlers -
570543

571544

572545
##################################

Tools/c-analyzer/cpython/ignored.tsv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ Modules/_testbuffer.c ndarray_memoryview_from_buffer strides -
181181
Modules/_testbuffer.c ndarray_memoryview_from_buffer suboffsets -
182182
Modules/_testbuffer.c ndarray_push kwlist -
183183
Modules/_testbuffer.c staticarray_init kwlist -
184+
Modules/_testcapi/heaptype.c - _testcapimodule -
185+
Modules/_testcapi/unicode.c - _testcapimodule -
184186
Modules/_testcapimodule.c - ContainerNoGC_members -
185187
Modules/_testcapimodule.c - ContainerNoGC_type -
186188
Modules/_testcapimodule.c - FmData -
@@ -379,6 +381,7 @@ Modules/_decimal/_decimal.c - ssize_constants -
379381
Modules/_elementtree.c - ExpatMemoryHandler -
380382
Modules/_io/_iomodule.c - static_types -
381383
Modules/_io/textio.c - encodefuncs -
384+
Modules/_io/winconsoleio.c - _PyWindowsConsoleIO_Type -
382385
Modules/_localemodule.c - langinfo_constants -
383386
Modules/_pickle.c - READ_WHOLE_LINE -
384387
Modules/_sqlite/module.c - error_codes -

0 commit comments

Comments
 (0)