Skip to content

Commit eca5362

Browse files
gh-94673: Clarify About Runtime State Related to Static Builtin Types (gh-117761)
Guido pointed out to me that some details about the per-interpreter state for the builtin types aren't especially clear. I'm addressing that by: * adding a comment explaining that state * adding some asserts to point out the relationship between each index and the interp/global runtime state
1 parent 30f0643 commit eca5362

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

Include/internal/pycore_typeobject.h

+37
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,43 @@ struct types_state {
6868
unsigned int next_version_tag;
6969

7070
struct type_cache type_cache;
71+
72+
/* Every static builtin type is initialized for each interpreter
73+
during its own initialization, including for the main interpreter
74+
during global runtime initialization. This is done by calling
75+
_PyStaticType_InitBuiltin().
76+
77+
The first time a static builtin type is initialized, all the
78+
normal PyType_Ready() stuff happens. The only difference from
79+
normal is that there are three PyTypeObject fields holding
80+
objects which are stored here (on PyInterpreterState) rather
81+
than in the corresponding PyTypeObject fields. Those are:
82+
tp_dict (cls.__dict__), tp_subclasses (cls.__subclasses__),
83+
and tp_weaklist.
84+
85+
When a subinterpreter is initialized, each static builtin type
86+
is still initialized, but only the interpreter-specific portion,
87+
namely those three objects.
88+
89+
Those objects are stored in the PyInterpreterState.types.builtins
90+
array, at the index corresponding to each specific static builtin
91+
type. That index (a size_t value) is stored in the tp_subclasses
92+
field. For static builtin types, we re-purposed the now-unused
93+
tp_subclasses to avoid adding another field to PyTypeObject.
94+
In all other cases tp_subclasses holds a dict like before.
95+
(The field was previously defined as PyObject*, but is now void*
96+
to reflect its dual use.)
97+
98+
The index for each static builtin type isn't statically assigned.
99+
Instead it is calculated the first time a type is initialized
100+
(by the main interpreter). The index matches the order in which
101+
the type was initialized relative to the others. The actual
102+
value comes from the current value of num_builtins_initialized,
103+
as each type is initialized for the main interpreter.
104+
105+
num_builtins_initialized is incremented once for each static
106+
builtin type. Once initialization is over for a subinterpreter,
107+
the value will be the same as for all other interpreters. */
71108
size_t num_builtins_initialized;
72109
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
73110
PyMutex mutex;

Objects/typeobject.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,14 @@ _PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self)
162162
static void
163163
static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self)
164164
{
165-
if (!static_builtin_index_is_set(self)) {
165+
if (_Py_IsMainInterpreter(interp)) {
166+
assert(!static_builtin_index_is_set(self));
166167
static_builtin_index_set(self, interp->types.num_builtins_initialized);
167168
}
169+
else {
170+
assert(static_builtin_index_get(self) ==
171+
interp->types.num_builtins_initialized);
172+
}
168173
static_builtin_state *state = static_builtin_state_get(interp, self);
169174

170175
/* It should only be called once for each builtin type. */

0 commit comments

Comments
 (0)