Skip to content

Commit 01fa907

Browse files
gh-81057: Move contextvars-related Globals to _PyRuntimeState (gh-99400)
This is part of the effort to consolidate global variables, to make them easier to manage (and make it easier to later move some of them to PyInterpreterState). #81057
1 parent 5f55067 commit 01fa907

File tree

9 files changed

+82
-90
lines changed

9 files changed

+82
-90
lines changed

Include/internal/pycore_context.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ void _PyContext_Fini(PyInterpreterState *);
1818

1919
/* other API */
2020

21+
typedef struct {
22+
PyObject_HEAD
23+
} _PyContextTokenMissing;
24+
2125
#ifndef WITH_FREELISTS
2226
// without freelists
2327
# define PyContext_MAXFREELIST 0

Include/internal/pycore_global_objects.h

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

1111
#include "pycore_gc.h" // PyGC_Head
1212
#include "pycore_global_strings.h" // struct _Py_global_strings
13+
#include "pycore_hamt.h" // PyHamtNode_Bitmap
14+
#include "pycore_context.h" // _PyContextTokenMissing
1315
#include "pycore_typeobject.h" // pytype_slotdef
1416

1517

@@ -52,6 +54,10 @@ struct _Py_global_objects {
5254

5355
_PyGC_Head_UNUSED _tuple_empty_gc_not_used;
5456
PyTupleObject tuple_empty;
57+
58+
_PyGC_Head_UNUSED _hamt_bitmap_node_empty_gc_not_used;
59+
PyHamtNode_Bitmap hamt_bitmap_node_empty;
60+
_PyContextTokenMissing context_token_missing;
5561
} singletons;
5662

5763
PyObject *interned;
@@ -76,6 +82,9 @@ struct _Py_interp_cached_objects {
7682
struct _Py_interp_static_objects {
7783
struct {
7884
int _not_used;
85+
// hamt_empty is here instead of global because of its weakreflist.
86+
_PyGC_Head_UNUSED _hamt_empty_gc_not_used;
87+
PyHamtObject hamt_empty;
7988
} singletons;
8089
};
8190

Include/internal/pycore_global_objects_fini_generated.h

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

Include/internal/pycore_hamt.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ extern PyTypeObject _PyHamtKeys_Type;
2828
extern PyTypeObject _PyHamtValues_Type;
2929
extern PyTypeObject _PyHamtItems_Type;
3030

31-
/* runtime lifecycle */
32-
33-
void _PyHamt_Fini(PyInterpreterState *);
34-
3531

3632
/* other API */
3733

@@ -53,6 +49,13 @@ typedef struct {
5349
} PyHamtObject;
5450

5551

52+
typedef struct {
53+
PyObject_VAR_HEAD
54+
uint32_t b_bitmap;
55+
PyObject *b_array[1];
56+
} PyHamtNode_Bitmap;
57+
58+
5659
/* A struct to hold the state of depth-first traverse of the tree.
5760
5861
HAMT is an immutable collection. Iterators will hold a strong reference

Include/internal/pycore_runtime_init.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ extern "C" {
7575
.tuple_empty = { \
7676
.ob_base = _PyVarObject_IMMORTAL_INIT(&PyTuple_Type, 0) \
7777
}, \
78+
.hamt_bitmap_node_empty = { \
79+
.ob_base = _PyVarObject_IMMORTAL_INIT(&_PyHamt_BitmapNode_Type, 0) \
80+
}, \
81+
.context_token_missing = { \
82+
.ob_base = _PyObject_IMMORTAL_INIT(&_PyContextTokenMissing_Type), \
83+
}, \
7884
}, \
7985
}, \
8086
._main_interpreter = _PyInterpreterState_INIT, \
@@ -112,6 +118,10 @@ extern "C" {
112118
.static_objects = { \
113119
.singletons = { \
114120
._not_used = 1, \
121+
.hamt_empty = { \
122+
.ob_base = _PyObject_IMMORTAL_INIT(&_PyHamt_Type), \
123+
.h_root = (PyHamtNode*)&_Py_SINGLETON(hamt_bitmap_node_empty), \
124+
}, \
115125
}, \
116126
}, \
117127
._initial_thread = _PyThreadState_INIT, \

Python/context.c

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,25 +1235,29 @@ token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
12351235
/////////////////////////// Token.MISSING
12361236

12371237

1238-
static PyObject *_token_missing;
1239-
1240-
1241-
typedef struct {
1242-
PyObject_HEAD
1243-
} PyContextTokenMissing;
1244-
1245-
12461238
static PyObject *
12471239
context_token_missing_tp_repr(PyObject *self)
12481240
{
12491241
return PyUnicode_FromString("<Token.MISSING>");
12501242
}
12511243

1244+
static void
1245+
context_token_missing_tp_dealloc(_PyContextTokenMissing *Py_UNUSED(self))
1246+
{
1247+
#ifdef Py_DEBUG
1248+
/* The singleton is statically allocated. */
1249+
_Py_FatalRefcountError("deallocating the token missing singleton");
1250+
#else
1251+
return;
1252+
#endif
1253+
}
1254+
12521255

12531256
PyTypeObject _PyContextTokenMissing_Type = {
12541257
PyVarObject_HEAD_INIT(&PyType_Type, 0)
12551258
"Token.MISSING",
1256-
sizeof(PyContextTokenMissing),
1259+
sizeof(_PyContextTokenMissing),
1260+
.tp_dealloc = (destructor)context_token_missing_tp_dealloc,
12571261
.tp_getattro = PyObject_GenericGetAttr,
12581262
.tp_flags = Py_TPFLAGS_DEFAULT,
12591263
.tp_repr = context_token_missing_tp_repr,
@@ -1263,17 +1267,7 @@ PyTypeObject _PyContextTokenMissing_Type = {
12631267
static PyObject *
12641268
get_token_missing(void)
12651269
{
1266-
if (_token_missing != NULL) {
1267-
return Py_NewRef(_token_missing);
1268-
}
1269-
1270-
_token_missing = (PyObject *)PyObject_New(
1271-
PyContextTokenMissing, &_PyContextTokenMissing_Type);
1272-
if (_token_missing == NULL) {
1273-
return NULL;
1274-
}
1275-
1276-
return Py_NewRef(_token_missing);
1270+
return Py_NewRef(&_Py_SINGLETON(context_token_missing));
12771271
}
12781272

12791273

@@ -1298,15 +1292,11 @@ _PyContext_ClearFreeList(PyInterpreterState *interp)
12981292
void
12991293
_PyContext_Fini(PyInterpreterState *interp)
13001294
{
1301-
if (_Py_IsMainInterpreter(interp)) {
1302-
Py_CLEAR(_token_missing);
1303-
}
13041295
_PyContext_ClearFreeList(interp);
13051296
#if defined(Py_DEBUG) && PyContext_MAXFREELIST > 0
13061297
struct _Py_context_state *state = &interp->context;
13071298
state->numfree = -1;
13081299
#endif
1309-
_PyHamt_Fini(interp);
13101300
}
13111301

13121302

Python/hamt.c

Lines changed: 32 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -319,24 +319,13 @@ typedef struct {
319319
} PyHamtNode_Array;
320320

321321

322-
typedef struct {
323-
PyObject_VAR_HEAD
324-
uint32_t b_bitmap;
325-
PyObject *b_array[1];
326-
} PyHamtNode_Bitmap;
327-
328-
329322
typedef struct {
330323
PyObject_VAR_HEAD
331324
int32_t c_hash;
332325
PyObject *c_array[1];
333326
} PyHamtNode_Collision;
334327

335328

336-
static PyHamtNode_Bitmap *_empty_bitmap_node;
337-
static PyHamtObject *_empty_hamt;
338-
339-
340329
static PyHamtObject *
341330
hamt_alloc(void);
342331

@@ -521,13 +510,16 @@ hamt_node_bitmap_new(Py_ssize_t size)
521510
PyHamtNode_Bitmap *node;
522511
Py_ssize_t i;
523512

513+
if (size == 0) {
514+
/* Since bitmap nodes are immutable, we can cache the instance
515+
for size=0 and reuse it whenever we need an empty bitmap node.
516+
*/
517+
return (PyHamtNode *)Py_NewRef(&_Py_SINGLETON(hamt_bitmap_node_empty));
518+
}
519+
524520
assert(size >= 0);
525521
assert(size % 2 == 0);
526522

527-
if (size == 0 && _empty_bitmap_node != NULL) {
528-
return (PyHamtNode *)Py_NewRef(_empty_bitmap_node);
529-
}
530-
531523
/* No freelist; allocate a new bitmap node */
532524
node = PyObject_GC_NewVar(
533525
PyHamtNode_Bitmap, &_PyHamt_BitmapNode_Type, size);
@@ -545,13 +537,6 @@ hamt_node_bitmap_new(Py_ssize_t size)
545537

546538
_PyObject_GC_TRACK(node);
547539

548-
if (size == 0 && _empty_bitmap_node == NULL) {
549-
/* Since bitmap nodes are immutable, we can cache the instance
550-
for size=0 and reuse it whenever we need an empty bitmap node.
551-
*/
552-
_empty_bitmap_node = (PyHamtNode_Bitmap*)Py_NewRef(node);
553-
}
554-
555540
return (PyHamtNode *)node;
556541
}
557542

@@ -1142,6 +1127,16 @@ hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self)
11421127
Py_ssize_t len = Py_SIZE(self);
11431128
Py_ssize_t i;
11441129

1130+
if (Py_SIZE(self) == 0) {
1131+
/* The empty node is statically allocated. */
1132+
assert(self == &_Py_SINGLETON(hamt_bitmap_node_empty));
1133+
#ifdef Py_DEBUG
1134+
_Py_FatalRefcountError("deallocating the empty hamt node bitmap singleton");
1135+
#else
1136+
return;
1137+
#endif
1138+
}
1139+
11451140
PyObject_GC_UnTrack(self);
11461141
Py_TRASHCAN_BEGIN(self, hamt_node_bitmap_dealloc)
11471142

@@ -2431,33 +2426,15 @@ hamt_alloc(void)
24312426
return o;
24322427
}
24332428

2429+
#define _empty_hamt \
2430+
(&_Py_INTERP_SINGLETON(_PyInterpreterState_Get(), hamt_empty))
2431+
24342432
PyHamtObject *
24352433
_PyHamt_New(void)
24362434
{
2437-
if (_empty_hamt != NULL) {
2438-
/* HAMT is an immutable object so we can easily cache an
2439-
empty instance. */
2440-
return (PyHamtObject*)Py_NewRef(_empty_hamt);
2441-
}
2442-
2443-
PyHamtObject *o = hamt_alloc();
2444-
if (o == NULL) {
2445-
return NULL;
2446-
}
2447-
2448-
o->h_root = hamt_node_bitmap_new(0);
2449-
if (o->h_root == NULL) {
2450-
Py_DECREF(o);
2451-
return NULL;
2452-
}
2453-
2454-
o->h_count = 0;
2455-
2456-
if (_empty_hamt == NULL) {
2457-
_empty_hamt = (PyHamtObject*)Py_NewRef(o);
2458-
}
2459-
2460-
return o;
2435+
/* HAMT is an immutable object so we can easily cache an
2436+
empty instance. */
2437+
return (PyHamtObject*)Py_NewRef(_empty_hamt);
24612438
}
24622439

24632440
#ifdef Py_DEBUG
@@ -2673,6 +2650,15 @@ hamt_tp_traverse(PyHamtObject *self, visitproc visit, void *arg)
26732650
static void
26742651
hamt_tp_dealloc(PyHamtObject *self)
26752652
{
2653+
if (self == _empty_hamt) {
2654+
/* The empty one is statically allocated. */
2655+
#ifdef Py_DEBUG
2656+
_Py_FatalRefcountError("deallocating the empty hamt singleton");
2657+
#else
2658+
return;
2659+
#endif
2660+
}
2661+
26762662
PyObject_GC_UnTrack(self);
26772663
if (self->h_weakreflist != NULL) {
26782664
PyObject_ClearWeakRefs((PyObject*)self);
@@ -2908,11 +2894,3 @@ PyTypeObject _PyHamt_CollisionNode_Type = {
29082894
.tp_free = PyObject_GC_Del,
29092895
.tp_hash = PyObject_HashNotImplemented,
29102896
};
2911-
2912-
2913-
void
2914-
_PyHamt_Fini(PyInterpreterState *interp)
2915-
{
2916-
Py_CLEAR(_empty_hamt);
2917-
Py_CLEAR(_empty_bitmap_node);
2918-
}

Tools/build/generate_global_objects.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@
127127
# The generated ones come from generate_runtime_init().
128128
'(PyObject *)&_Py_SINGLETON(bytes_empty)',
129129
'(PyObject *)&_Py_SINGLETON(tuple_empty)',
130+
'(PyObject *)&_Py_SINGLETON(hamt_bitmap_node_empty)',
131+
'(PyObject *)&_Py_INTERP_SINGLETON(interp, hamt_empty)',
132+
'(PyObject *)&_Py_SINGLETON(context_token_missing)',
130133
]
131134

132135

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

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

301-
#-----------------------
302-
# other
303-
304-
# initialized once
305-
Python/context.c - _token_missing -
306-
Python/hamt.c - _empty_bitmap_node -
307-
Python/hamt.c - _empty_hamt -
308-
309301

310302
##################################
311303
# global non-objects to fix in core code

0 commit comments

Comments
 (0)