]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-94673: Clarify About Runtime State Related to Static Builtin Types (gh-117761)
authorEric Snow <ericsnowcurrently@gmail.com>
Fri, 12 Apr 2024 22:39:27 +0000 (16:39 -0600)
committerGitHub <noreply@github.com>
Fri, 12 Apr 2024 22:39:27 +0000 (16:39 -0600)
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

Include/internal/pycore_typeobject.h
Objects/typeobject.c

index 1693119ffece03f451fa276aa36745b707d479e5..09c4501c38c935cb00ccc6062588f443b115bb8a 100644 (file)
@@ -68,6 +68,43 @@ struct types_state {
     unsigned int next_version_tag;
 
     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;
     static_builtin_state builtins[_Py_MAX_STATIC_BUILTIN_TYPES];
     PyMutex mutex;
index a3c137536a4d870beca6decd73d2ea7ccebb5cd2..1cb53516a9ae76dc5048afd22a6cbffffc86a350 100644 (file)
@@ -162,9 +162,14 @@ _PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self)
 static void
 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);
     }
+    else {
+        assert(static_builtin_index_get(self) ==
+                interp->types.num_builtins_initialized);
+    }
     static_builtin_state *state = static_builtin_state_get(interp, self);
 
     /* It should only be called once for each builtin type. */