From c0b035e86ac4d9ac23c4fc4b23d450b5ee28e8df Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Apr 2024 09:51:13 -0600 Subject: [PATCH 1/8] Add a note about the static builtin types to struct types_state. --- Include/internal/pycore_typeobject.h | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 8a25935f308178..734604e75015b3 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -68,6 +68,40 @@ 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 (defined as PyObject*). We re-purposed the now-unused + tp_subclasses to avoid adding another field to PyTypeObject. + + 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; From a73483b01e3b2671f0b2c8d7466bd09a444786a1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Apr 2024 09:56:30 -0600 Subject: [PATCH 2/8] Add asserts for static builtin type initialization constraints. --- Objects/typeobject.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e9f2d2577e9fab..dc17f3cc43cb3d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -163,8 +163,14 @@ static void static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) { if (!static_builtin_index_is_set(self)) { + assert(_Py_IsMainInterpreter(interp)); static_builtin_index_set(self, interp->types.num_builtins_initialized); } + else { + assert(!_Py_IsMainInterpreter(interp)); + 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. */ From b8d622e047553e8b36d09448e61741d341d47816 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 11 Apr 2024 10:09:30 -0600 Subject: [PATCH 3/8] Add _PyRuntimeState.types.next_builtin_index. --- Include/internal/pycore_typeobject.h | 8 ++++++-- Objects/typeobject.c | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 734604e75015b3..318c0575d47a8d 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -22,6 +22,9 @@ struct _types_runtime_state { // bpo-42745: next_version_tag remains shared by all interpreters // because of static types. unsigned int next_version_tag; + + /* See the note in struct types_state below. */ + size_t next_builtin_index; }; @@ -96,8 +99,9 @@ struct types_state { 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. + value comes from the current value of + _PyRuntimeState.types.next_builtin_index 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, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index dc17f3cc43cb3d..bb5fff1e3d82a8 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -164,7 +164,11 @@ static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) { if (!static_builtin_index_is_set(self)) { assert(_Py_IsMainInterpreter(interp)); - static_builtin_index_set(self, interp->types.num_builtins_initialized); + assert(interp->types.num_builtins_initialized == \ + interp->runtime->types.next_builtin_index); + static_builtin_index_set(self, + interp->runtime->types.next_builtin_index); + interp->runtime->types.next_builtin_index++; } else { assert(!_Py_IsMainInterpreter(interp)); From 00feed6383c49c07d746e19cc942f15dee5d847b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Apr 2024 10:15:13 -0600 Subject: [PATCH 4/8] Revert "Add _PyRuntimeState.types.next_builtin_index." This reverts commit b8d622e047553e8b36d09448e61741d341d47816. --- Include/internal/pycore_typeobject.h | 8 ++------ Objects/typeobject.c | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 318c0575d47a8d..734604e75015b3 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -22,9 +22,6 @@ struct _types_runtime_state { // bpo-42745: next_version_tag remains shared by all interpreters // because of static types. unsigned int next_version_tag; - - /* See the note in struct types_state below. */ - size_t next_builtin_index; }; @@ -99,9 +96,8 @@ struct types_state { 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 - _PyRuntimeState.types.next_builtin_index as each type - is initialized for the main interpreter. + 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, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bb5fff1e3d82a8..dc17f3cc43cb3d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -164,11 +164,7 @@ static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) { if (!static_builtin_index_is_set(self)) { assert(_Py_IsMainInterpreter(interp)); - assert(interp->types.num_builtins_initialized == \ - interp->runtime->types.next_builtin_index); - static_builtin_index_set(self, - interp->runtime->types.next_builtin_index); - interp->runtime->types.next_builtin_index++; + static_builtin_index_set(self, interp->types.num_builtins_initialized); } else { assert(!_Py_IsMainInterpreter(interp)); From 6e1853be4c9f5521e43af3e01827a6fe27122ed8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Apr 2024 10:27:29 -0600 Subject: [PATCH 5/8] Describe the correct type for tp_subclasses. --- Include/internal/pycore_typeobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 734604e75015b3..94b590edd3e91b 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -89,7 +89,7 @@ struct types_state { 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 (defined as PyObject*). We re-purposed the now-unused + field (defined as void*). We re-purposed the now-unused tp_subclasses to avoid adding another field to PyTypeObject. The index for each static builtin type isn't statically assigned. From 7b029d91261b493ae8e8b288a8e737606a420a79 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Apr 2024 10:28:29 -0600 Subject: [PATCH 6/8] Clarify that tp_subclasses is PyObject* for other type objects. --- Include/internal/pycore_typeobject.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 94b590edd3e91b..864983d0ccea93 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -89,8 +89,11 @@ struct types_state { 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 (defined as void*). We re-purposed the now-unused + 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 From 7cf7c04e08eae0744f6d45e876f92df7c2265693 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Apr 2024 10:36:02 -0600 Subject: [PATCH 7/8] Flip around the condition in static_builtin_state_init(). --- Objects/typeobject.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index dc17f3cc43cb3d..e15abae38a189b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -162,12 +162,11 @@ _PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self) static void static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) { - if (!static_builtin_index_is_set(self)) { - assert(_Py_IsMainInterpreter(interp)); + if (_Py_IsMainInterpreter(interp)) { + assert(!static_builtin_index_is_set(self)); static_builtin_index_set(self, interp->types.num_builtins_initialized); } else { - assert(!_Py_IsMainInterpreter(interp)); assert(static_builtin_index_get(self) == \ interp->types.num_builtins_initialized); } From f668cf1ccb4526c941cf83f1e9ee44958d936bed Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 12 Apr 2024 10:43:18 -0600 Subject: [PATCH 8/8] Drop an unnecessary line escape. --- Objects/typeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e15abae38a189b..11de23df5efca3 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -167,7 +167,7 @@ static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self) static_builtin_index_set(self, interp->types.num_builtins_initialized); } else { - assert(static_builtin_index_get(self) == \ + assert(static_builtin_index_get(self) == interp->types.num_builtins_initialized); } static_builtin_state *state = static_builtin_state_get(interp, self);