Skip to content

Commit 47e75a0

Browse files
gh-94673: Add Per-Interpreter Storage for Static Builtin Types (#95255)
This is the last precursor to storing tp_subclasses (and tp_weaklist) on the interpreter state for static builtin types. Here we add per-type storage on PyInterpreterState, but only for the static builtin types. This involves the following: * add PyInterpreterState.types * move PyInterpreterState.type_cache to it * add a "num_builtins_initialized" field * add a "builtins" field (a static array big enough for all the static builtin types) * add _PyStaticType_GetState() to look up a static builtin type's state * (temporarily) add PyTypeObject.tp_static_builtin_index (to hold the type's index into PyInterpreterState.types.builtins) We will be eliminating tp_static_builtin_index in a later change.
1 parent 7ac5bb3 commit 47e75a0

File tree

5 files changed

+125
-7
lines changed

5 files changed

+125
-7
lines changed

Diff for: Include/cpython/object.h

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ struct _typeobject {
227227

228228
destructor tp_finalize;
229229
vectorcallfunc tp_vectorcall;
230+
size_t tp_static_builtin_index; /* 0 means "not initialized" */
230231
};
231232

232233
/* This struct is used by the specializer

Diff for: Include/internal/pycore_interp.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ struct _is {
173173
struct _Py_exc_state exc_state;
174174

175175
struct ast_state ast;
176-
struct type_cache type_cache;
176+
struct types_state types;
177177
struct callable_cache callable_cache;
178178

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

Diff for: Include/internal/pycore_typeobject.h

+16
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,25 @@ struct type_cache {
3939
#endif
4040
};
4141

42+
/* For now we hard-code this to a value for which we are confident
43+
all the static builtin types will fit (for all builds). */
44+
#define _Py_MAX_STATIC_BUILTIN_TYPES 200
45+
46+
typedef struct {
47+
PyTypeObject *type;
48+
} static_builtin_state;
49+
50+
struct types_state {
51+
struct type_cache type_cache;
52+
size_t num_builtins_initialized;
53+
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
54+
};
55+
56+
4257
extern PyStatus _PyTypes_InitSlotDefs(void);
4358

4459
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
60+
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
4561
extern void _PyStaticType_Dealloc(PyTypeObject *type);
4662

4763

Diff for: Lib/test/test_sys.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1507,7 +1507,7 @@ def delx(self): del self.__x
15071507
check((1,2,3), vsize('') + 3*self.P)
15081508
# type
15091509
# static type: PyTypeObject
1510-
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
1510+
fmt = 'P2nPI13Pl4Pn9Pn12PIPI'
15111511
s = vsize('2P' + fmt)
15121512
check(int, s)
15131513
# class

Diff for: Objects/typeobject.c

+106-5
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,88 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
6767

6868
static inline PyTypeObject * subclass_from_ref(PyObject *ref);
6969

70+
71+
/* helpers for for static builtin types */
72+
73+
static inline int
74+
static_builtin_index_is_set(PyTypeObject *self)
75+
{
76+
return self->tp_static_builtin_index > 0;
77+
}
78+
79+
static inline size_t
80+
static_builtin_index_get(PyTypeObject *self)
81+
{
82+
assert(static_builtin_index_is_set(self));
83+
/* We store a 1-based index so 0 can mean "not initialized". */
84+
return self->tp_static_builtin_index - 1;
85+
}
86+
87+
static inline void
88+
static_builtin_index_set(PyTypeObject *self, size_t index)
89+
{
90+
assert(index < _Py_MAX_STATIC_BUILTIN_TYPES);
91+
/* We store a 1-based index so 0 can mean "not initialized". */
92+
self->tp_static_builtin_index = index + 1;
93+
}
94+
95+
static inline void
96+
static_builtin_index_clear(PyTypeObject *self)
97+
{
98+
self->tp_static_builtin_index = 0;
99+
}
100+
101+
static inline static_builtin_state *
102+
static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
103+
{
104+
return &(interp->types.builtins[static_builtin_index_get(self)]);
105+
}
106+
107+
/* For static types we store some state in an array on each interpreter. */
108+
static_builtin_state *
109+
_PyStaticType_GetState(PyTypeObject *self)
110+
{
111+
assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
112+
PyInterpreterState *interp = _PyInterpreterState_GET();
113+
return static_builtin_state_get(interp, self);
114+
}
115+
116+
static void
117+
static_builtin_state_init(PyTypeObject *self)
118+
{
119+
/* Set the type's per-interpreter state. */
120+
PyInterpreterState *interp = _PyInterpreterState_GET();
121+
122+
/* It should only be called once for each builtin type. */
123+
assert(!static_builtin_index_is_set(self));
124+
125+
static_builtin_index_set(self, interp->types.num_builtins_initialized);
126+
interp->types.num_builtins_initialized++;
127+
128+
static_builtin_state *state = static_builtin_state_get(interp, self);
129+
state->type = self;
130+
}
131+
132+
static void
133+
static_builtin_state_clear(PyTypeObject *self)
134+
{
135+
/* Reset the type's per-interpreter state.
136+
This basically undoes what static_builtin_state_init() did. */
137+
PyInterpreterState *interp = _PyInterpreterState_GET();
138+
139+
static_builtin_state *state = static_builtin_state_get(interp, self);
140+
state->type = NULL;
141+
static_builtin_index_clear(self);
142+
143+
assert(interp->types.num_builtins_initialized > 0);
144+
interp->types.num_builtins_initialized--;
145+
}
146+
147+
// Also see _PyStaticType_InitBuiltin() and _PyStaticType_Dealloc().
148+
149+
/* end static builtin helpers */
150+
151+
70152
/*
71153
* finds the beginning of the docstring's introspection signature.
72154
* if present, returns a pointer pointing to the first '('.
@@ -206,7 +288,7 @@ static struct type_cache*
206288
get_type_cache(void)
207289
{
208290
PyInterpreterState *interp = _PyInterpreterState_GET();
209-
return &interp->type_cache;
291+
return &interp->types.type_cache;
210292
}
211293

212294

@@ -225,7 +307,7 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
225307
void
226308
_PyType_InitCache(PyInterpreterState *interp)
227309
{
228-
struct type_cache *cache = &interp->type_cache;
310+
struct type_cache *cache = &interp->types.type_cache;
229311
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
230312
struct type_cache_entry *entry = &cache->hashtable[i];
231313
assert(entry->name == NULL);
@@ -242,7 +324,7 @@ _PyType_InitCache(PyInterpreterState *interp)
242324
static unsigned int
243325
_PyType_ClearCache(PyInterpreterState *interp)
244326
{
245-
struct type_cache *cache = &interp->type_cache;
327+
struct type_cache *cache = &interp->types.type_cache;
246328
#if MCACHE_STATS
247329
size_t total = cache->hits + cache->collisions + cache->misses;
248330
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
@@ -274,11 +356,17 @@ PyType_ClearCache(void)
274356
void
275357
_PyTypes_Fini(PyInterpreterState *interp)
276358
{
277-
struct type_cache *cache = &interp->type_cache;
359+
struct type_cache *cache = &interp->types.type_cache;
278360
type_cache_clear(cache, NULL);
279361
if (_Py_IsMainInterpreter(interp)) {
280362
clear_slotdefs();
281363
}
364+
365+
assert(interp->types.num_builtins_initialized == 0);
366+
// All the static builtin types should have been finalized already.
367+
for (size_t i = 0; i < _Py_MAX_STATIC_BUILTIN_TYPES; i++) {
368+
assert(interp->types.builtins[i].type == NULL);
369+
}
282370
}
283371

284372

@@ -4247,6 +4335,8 @@ clear_static_tp_subclasses(PyTypeObject *type)
42474335
void
42484336
_PyStaticType_Dealloc(PyTypeObject *type)
42494337
{
4338+
assert(!(type->tp_flags & Py_TPFLAGS_HEAPTYPE));
4339+
42504340
type_dealloc_common(type);
42514341

42524342
Py_CLEAR(type->tp_dict);
@@ -4261,6 +4351,11 @@ _PyStaticType_Dealloc(PyTypeObject *type)
42614351
}
42624352

42634353
type->tp_flags &= ~Py_TPFLAGS_READY;
4354+
4355+
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
4356+
static_builtin_state_clear(type);
4357+
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
4358+
}
42644359
}
42654360

42664361

@@ -6679,7 +6774,13 @@ _PyStaticType_InitBuiltin(PyTypeObject *self)
66796774
{
66806775
self->tp_flags = self->tp_flags | _Py_TPFLAGS_STATIC_BUILTIN;
66816776

6682-
return PyType_Ready(self);
6777+
static_builtin_state_init(self);
6778+
6779+
int res = PyType_Ready(self);
6780+
if (res < 0) {
6781+
static_builtin_state_clear(self);
6782+
}
6783+
return res;
66836784
}
66846785

66856786

0 commit comments

Comments
 (0)