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
This commit is contained in:
Eric Snow 2024-04-12 16:39:27 -06:00 committed by GitHub
parent 30f0643e36
commit eca53620e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 1 deletions

View File

@ -68,6 +68,43 @@ struct types_state {
unsigned int next_version_tag; unsigned int next_version_tag;
struct type_cache type_cache; struct type_cache type_cache;
/* Every static builtin type is initialized for each interpreter
during its own initialization, including for the main interpreter
during global runtime initialization. This is done by calling
_PyStaticType_InitBuiltin().
The first time a static builtin type is initialized, all the
normal PyType_Ready() stuff happens. The only difference from
normal is that there are three PyTypeObject fields holding
objects which are stored here (on PyInterpreterState) rather
than in the corresponding PyTypeObject fields. Those are:
tp_dict (cls.__dict__), tp_subclasses (cls.__subclasses__),
and tp_weaklist.
When a subinterpreter is initialized, each static builtin type
is still initialized, but only the interpreter-specific portion,
namely those three objects.
Those objects are stored in the PyInterpreterState.types.builtins
array, at the index corresponding to each specific static builtin
type. That index (a size_t value) is stored in the tp_subclasses
field. For static builtin types, we re-purposed the now-unused
tp_subclasses to avoid adding another field to PyTypeObject.
In all other cases tp_subclasses holds a dict like before.
(The field was previously defined as PyObject*, but is now void*
to reflect its dual use.)
The index for each static builtin type isn't statically assigned.
Instead it is calculated the first time a type is initialized
(by the main interpreter). The index matches the order in which
the type was initialized relative to the others. The actual
value comes from the current value of num_builtins_initialized,
as each type is initialized for the main interpreter.
num_builtins_initialized is incremented once for each static
builtin type. Once initialization is over for a subinterpreter,
the value will be the same as for all other interpreters. */
size_t num_builtins_initialized; size_t num_builtins_initialized;
static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES]; static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
PyMutex mutex; PyMutex mutex;

View File

@ -162,9 +162,14 @@ _PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self)
static void static void
static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self)
{ {
if (!static_builtin_index_is_set(self)) { if (_Py_IsMainInterpreter(interp)) {
assert(!static_builtin_index_is_set(self));
static_builtin_index_set(self, interp->types.num_builtins_initialized); static_builtin_index_set(self, interp->types.num_builtins_initialized);
} }
else {
assert(static_builtin_index_get(self) ==
interp->types.num_builtins_initialized);
}
static_builtin_state *state = static_builtin_state_get(interp, self); static_builtin_state *state = static_builtin_state_get(interp, self);
/* It should only be called once for each builtin type. */ /* It should only be called once for each builtin type. */