From d1e4cf57287c0f253dd93687f5cf271883d27c5b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 18:16:02 -0600 Subject: [PATCH 01/13] Make __mro__ a getter. --- Objects/typeobject.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index a9d3a69263fb40..daf31cf644d958 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -809,7 +809,6 @@ static PyMemberDef type_members[] = { {"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY}, {"__dictoffset__", T_PYSSIZET, offsetof(PyTypeObject, tp_dictoffset), READONLY}, - {"__mro__", T_OBJECT, offsetof(PyTypeObject, tp_mro), READONLY}, {0} }; @@ -1026,6 +1025,12 @@ type_get_bases(PyTypeObject *type, void *context) return Py_NewRef(lookup_tp_bases(type)); } +static PyObject * +type_get_mro(PyTypeObject *type, void *context) +{ + return Py_NewRef(lookup_tp_mro(type)); +} + static PyTypeObject *best_base(PyObject *); static int mro_internal(PyTypeObject *, PyObject **); static int type_is_subtype_base_chain(PyTypeObject *, PyTypeObject *); @@ -1402,6 +1407,7 @@ static PyGetSetDef type_getsets[] = { {"__name__", (getter)type_name, (setter)type_set_name, NULL}, {"__qualname__", (getter)type_qualname, (setter)type_set_qualname, NULL}, {"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL}, + {"__mro__", (getter)type_get_mro, NULL, NULL}, {"__module__", (getter)type_module, (setter)type_set_module, NULL}, {"__abstractmethods__", (getter)type_abstractmethods, (setter)type_set_abstractmethods, NULL}, From d3db6bfc6e13edef90bb3d80fbb85beddac8a98b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 18:21:43 -0600 Subject: [PATCH 02/13] Add the fields to static_builtin_state. --- Include/internal/pycore_typeobject.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index f865e51aeba503..5897a948502895 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -44,6 +44,9 @@ struct type_cache { typedef struct { PyTypeObject *type; + PyObject *tp_dict; + PyObject *tp_bases; + PyObject *tp_mro; PyObject *tp_subclasses; /* We never clean up weakrefs for static builtin types since they will effectively never get triggered. However, there From b5799d8f6e42c9dfb710999c85f084eb969b0900 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 18:23:29 -0600 Subject: [PATCH 03/13] Isolate tp_bases. --- Objects/typeobject.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index daf31cf644d958..683adf6914ebf0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -197,6 +197,12 @@ clear_tp_dict(PyTypeObject *self) static inline PyObject * lookup_tp_bases(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + return state->tp_bases; + } return self->tp_bases; } @@ -209,12 +215,26 @@ _PyType_GetBases(PyTypeObject *self) static inline void set_tp_bases(PyTypeObject *self, PyObject *bases) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + state->tp_bases = bases; + return; + } self->tp_bases = bases; } static inline void clear_tp_bases(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + Py_CLEAR(state->tp_bases); + return; + } Py_CLEAR(self->tp_bases); } @@ -4699,10 +4719,12 @@ clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type) { if (_Py_IsMainInterpreter(interp)) { clear_tp_dict(type); - clear_tp_bases(type); clear_tp_mro(type); Py_CLEAR(type->tp_cache); } + else { + clear_tp_bases(type); + } clear_static_tp_subclasses(type); } From 2e28be59e408ec899746c2d70e04dc84deb8b6b4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 18:26:12 -0600 Subject: [PATCH 04/13] Isolate tp_mro. --- Modules/_abc.c | 1 + Objects/typeobject.c | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Modules/_abc.c b/Modules/_abc.c index 997b618d557ab2..9694331339aa78 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -7,6 +7,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyType_GetSubclasses() #include "pycore_runtime.h" // _Py_ID() +#include "pycore_typeobject.h" // _PyType_GetMRO() #include "clinic/_abc.c.h" /*[clinic input] diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 683adf6914ebf0..3c7f816a89c85f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -242,6 +242,12 @@ clear_tp_bases(PyTypeObject *self) static inline PyObject * lookup_tp_mro(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + return state->tp_mro; + } return self->tp_mro; } @@ -254,12 +260,26 @@ _PyType_GetMRO(PyTypeObject *self) static inline void set_tp_mro(PyTypeObject *self, PyObject *mro) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + state->tp_mro = mro; + return; + } self->tp_mro = mro; } static inline void clear_tp_mro(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + Py_CLEAR(state->tp_mro); + return; + } Py_CLEAR(self->tp_mro); } @@ -4719,11 +4739,11 @@ clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type) { if (_Py_IsMainInterpreter(interp)) { clear_tp_dict(type); - clear_tp_mro(type); Py_CLEAR(type->tp_cache); } else { clear_tp_bases(type); + clear_tp_mro(type); } clear_static_tp_subclasses(type); } From fb7a025abc74429039b8bfc7d07e1f2315e7e67f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 18:36:07 -0600 Subject: [PATCH 05/13] Add a note. --- Include/internal/pycore_typeobject.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 5897a948502895..b82327cf9befee 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -44,6 +44,8 @@ struct type_cache { typedef struct { PyTypeObject *type; + // XXX tp_dict, tp_bases, and tp_mro can probably be statically + // allocated, instead of dynamically and stored on the interpreter. PyObject *tp_dict; PyObject *tp_bases; PyObject *tp_mro; From 2822f0d32f95ed9d22259a1fbed2d240daef39f6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 19:03:32 -0600 Subject: [PATCH 06/13] Isolate tp_dict. --- Objects/structseq.c | 9 ++------- Objects/typeobject.c | 28 +++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Objects/structseq.c b/Objects/structseq.c index f63660acb639c3..8b1895957101a4 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -511,7 +511,6 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp, Py_ssize_t n_members = count_members(desc, &n_unnamed_members); PyMemberDef *members = NULL; - int initialized = 1; if ((type->tp_flags & Py_TPFLAGS_READY) == 0) { assert(type->tp_name == NULL); assert(type->tp_members == NULL); @@ -524,7 +523,6 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp, initialize_static_fields(type, desc, members, tp_flags); _Py_SetImmortal(type); - initialized = 0; } #ifndef NDEBUG else { @@ -543,13 +541,10 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp, desc->name); goto error; } - // This should be dropped if tp_dict is made per-interpreter. - if (initialized) { - return 0; - } if (initialize_structseq_dict( - desc, _PyType_GetDict(type), n_members, n_unnamed_members) < 0) { + desc, _PyType_GetDict(type), n_members, n_unnamed_members) < 0) + { goto error; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3c7f816a89c85f..19ff4b1fb49e82 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -172,6 +172,12 @@ static_builtin_state_clear(PyInterpreterState *interp, PyTypeObject *self) static inline PyObject * lookup_tp_dict(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + return state->tp_dict; + } return self->tp_dict; } @@ -184,12 +190,26 @@ _PyType_GetDict(PyTypeObject *self) static inline void set_tp_dict(PyTypeObject *self, PyObject *dict) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + state->tp_dict = dict; + return; + } self->tp_dict = dict; } static inline void clear_tp_dict(PyTypeObject *self) { + if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = _PyStaticType_GetState(interp, self); + assert(state != NULL); + Py_CLEAR(state->tp_dict); + return; + } Py_CLEAR(self->tp_dict); } @@ -4738,13 +4758,11 @@ static void clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type) { if (_Py_IsMainInterpreter(interp)) { - clear_tp_dict(type); Py_CLEAR(type->tp_cache); } - else { - clear_tp_bases(type); - clear_tp_mro(type); - } + clear_tp_dict(type); + clear_tp_bases(type); + clear_tp_mro(type); clear_static_tp_subclasses(type); } From 68f000802ccbe68f32a3b3d9620b74bb909d13ac Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 26 Apr 2023 19:34:51 -0600 Subject: [PATCH 07/13] Fix type_get_bases() and type_get_mro(). --- Objects/typeobject.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 19ff4b1fb49e82..3fcf0a6dfcf3c2 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1082,13 +1082,21 @@ type_set_abstractmethods(PyTypeObject *type, PyObject *value, void *context) static PyObject * type_get_bases(PyTypeObject *type, void *context) { - return Py_NewRef(lookup_tp_bases(type)); + PyObject *bases = lookup_tp_bases(type); + if (bases == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(bases); } static PyObject * type_get_mro(PyTypeObject *type, void *context) { - return Py_NewRef(lookup_tp_mro(type)); + PyObject *mro = lookup_tp_mro(type); + if (mro == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(mro); } static PyTypeObject *best_base(PyObject *); From de2a3c1165d5adc1ed8e38af2c8782ccec1b63ec Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 27 Apr 2023 11:04:29 -0600 Subject: [PATCH 08/13] Fix _PyStaticType_InitBuiltin() for subinterpreters. --- Objects/typeobject.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3fcf0a6dfcf3c2..1ad49b4aec8172 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6758,6 +6758,10 @@ type_ready_pre_checks(PyTypeObject *type) static int type_ready_set_bases(PyTypeObject *type) { + if (lookup_tp_bases(type) != NULL) { + return 0; + } + /* Initialize tp_base (defaults to BaseObject unless that's us) */ PyTypeObject *base = type->tp_base; if (base == NULL && type != &PyBaseObject_Type) { @@ -7274,6 +7278,11 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) Otherwise we would initialize it here. */ assert(_PyType_CheckConsistency(self)); + /* We must explicitly set these for subinterpreters. + tp_subclasses is set lazily. */ + type_ready_set_dict(self); + type_ready_set_bases(self); + type_ready_mro(self); return 0; } From 8bd3b67f9048d50351e596484350e85bf6e7e782 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 27 Apr 2023 16:27:08 -0600 Subject: [PATCH 09/13] Call _PyType_CheckConsistency() after setting the fields. --- Objects/typeobject.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1ad49b4aec8172..6c5eec89570a87 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -7274,15 +7274,12 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) static_builtin_state_init(interp, self); - /* Per-interpreter tp_subclasses is done lazily. - Otherwise we would initialize it here. */ - - assert(_PyType_CheckConsistency(self)); /* We must explicitly set these for subinterpreters. tp_subclasses is set lazily. */ type_ready_set_dict(self); type_ready_set_bases(self); type_ready_mro(self); + assert(_PyType_CheckConsistency(self)); return 0; } From 071ef3f342fbba516b35df1a8f219dac10df2fb8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 28 Apr 2023 16:26:40 -0600 Subject: [PATCH 10/13] Re-run most of type_ready() under each interpreter. --- Objects/typeobject.c | 77 +++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 6c5eec89570a87..61d44c8aeea5dd 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -7075,7 +7075,7 @@ type_ready_add_subclasses(PyTypeObject *type) // Set tp_new and the "__new__" key in the type dictionary. // Use the Py_TPFLAGS_DISALLOW_INSTANTIATION flag. static int -type_ready_set_new(PyTypeObject *type) +type_ready_set_new(PyTypeObject *type, int rerunbuiltin) { PyTypeObject *base = type->tp_base; /* The condition below could use some explanation. @@ -7097,10 +7097,12 @@ type_ready_set_new(PyTypeObject *type) if (!(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)) { if (type->tp_new != NULL) { - // If "__new__" key does not exists in the type dictionary, - // set it to tp_new_wrapper(). - if (add_tp_new_wrapper(type) < 0) { - return -1; + if (!rerunbuiltin || base == NULL || type->tp_new != base->tp_new) { + // If "__new__" key does not exists in the type dictionary, + // set it to tp_new_wrapper(). + if (add_tp_new_wrapper(type) < 0) { + return -1; + } } } else { @@ -7174,7 +7176,7 @@ type_ready_post_checks(PyTypeObject *type) static int -type_ready(PyTypeObject *type) +type_ready(PyTypeObject *type, int rerunbuiltin) { _PyObject_ASSERT((PyObject *)type, (type->tp_flags & Py_TPFLAGS_READYING) == 0); @@ -7203,17 +7205,19 @@ type_ready(PyTypeObject *type) if (type_ready_mro(type) < 0) { goto error; } - if (type_ready_set_new(type) < 0) { + if (type_ready_set_new(type, rerunbuiltin) < 0) { goto error; } if (type_ready_fill_dict(type) < 0) { goto error; } - if (type_ready_inherit(type) < 0) { - goto error; - } - if (type_ready_preheader(type) < 0) { - goto error; + if (!rerunbuiltin) { + if (type_ready_inherit(type) < 0) { + goto error; + } + if (type_ready_preheader(type) < 0) { + goto error; + } } if (type_ready_set_hash(type) < 0) { goto error; @@ -7221,11 +7225,13 @@ type_ready(PyTypeObject *type) if (type_ready_add_subclasses(type) < 0) { goto error; } - if (type_ready_managed_dict(type) < 0) { - goto error; - } - if (type_ready_post_checks(type) < 0) { - goto error; + if (!rerunbuiltin) { + if (type_ready_managed_dict(type) < 0) { + goto error; + } + if (type_ready_post_checks(type) < 0) { + goto error; + } } /* All done -- set the ready flag */ @@ -7253,7 +7259,7 @@ PyType_Ready(PyTypeObject *type) type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; } - return type_ready(type); + return type_ready(type, 0); } int @@ -7264,37 +7270,26 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self) assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_DICT)); assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF)); -#ifndef NDEBUG int ismain = _Py_IsMainInterpreter(interp); -#endif - if (self->tp_flags & Py_TPFLAGS_READY) { + if ((self->tp_flags & Py_TPFLAGS_READY) == 0) { + assert(ismain); + + self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN; + self->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; + + assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG); + self->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++; + self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG; + } + else { assert(!ismain); assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN); assert(self->tp_flags & Py_TPFLAGS_VALID_VERSION_TAG); - - static_builtin_state_init(interp, self); - - /* We must explicitly set these for subinterpreters. - tp_subclasses is set lazily. */ - type_ready_set_dict(self); - type_ready_set_bases(self); - type_ready_mro(self); - assert(_PyType_CheckConsistency(self)); - return 0; } - assert(ismain); - - self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN; - self->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; - - assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG); - self->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++; - self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG; - static_builtin_state_init(interp, self); - int res = type_ready(self); + int res = type_ready(self, !ismain); if (res < 0) { static_builtin_state_clear(interp, self); } From 850059a1197aa81b031e5f829ebdd9ca3a2805c6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 2 May 2023 16:17:37 -0600 Subject: [PATCH 11/13] Add some comments. --- Objects/typeobject.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 61d44c8aeea5dd..6c7c46741bf95b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -184,6 +184,7 @@ lookup_tp_dict(PyTypeObject *self) PyObject * _PyType_GetDict(PyTypeObject *self) { + /* It returns a borrowed reference. */ return lookup_tp_dict(self); } @@ -229,6 +230,7 @@ lookup_tp_bases(PyTypeObject *self) PyObject * _PyType_GetBases(PyTypeObject *self) { + /* It returns a borrowed reference. */ return lookup_tp_bases(self); } @@ -274,6 +276,7 @@ lookup_tp_mro(PyTypeObject *self) PyObject * _PyType_GetMRO(PyTypeObject *self) { + /* It returns a borrowed reference. */ return lookup_tp_mro(self); } From cd1dd10a6978af14c798b13303d5fe4f1cdc7b21 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 2 May 2023 16:18:08 -0600 Subject: [PATCH 12/13] Drop an old workaround for previously shared objects. --- Modules/gcmodule.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 3fd5f4cd70e832..8f701cfe6a2b5f 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2174,23 +2174,6 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp) } -static void -gc_fini_untrack(PyGC_Head *list) -{ - PyGC_Head *gc; - for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(list)) { - PyObject *op = FROM_GC(gc); - _PyObject_GC_UNTRACK(op); - // gh-92036: If a deallocator function expect the object to be tracked - // by the GC (ex: func_dealloc()), it can crash if called on an object - // which is no longer tracked by the GC. Leak one strong reference on - // purpose so the object is never deleted and its deallocator is not - // called. - Py_INCREF(op); - } -} - - void _PyGC_Fini(PyInterpreterState *interp) { @@ -2198,17 +2181,9 @@ _PyGC_Fini(PyInterpreterState *interp) Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); - if (!_Py_IsMainInterpreter(interp)) { - // bpo-46070: Explicitly untrack all objects currently tracked by the - // GC. Otherwise, if an object is used later by another interpreter, - // calling PyObject_GC_UnTrack() on the object crashs if the previous - // or the next object of the PyGC_Head structure became a dangling - // pointer. - for (int i = 0; i < NUM_GENERATIONS; i++) { - PyGC_Head *gen = GEN_HEAD(gcstate, i); - gc_fini_untrack(gen); - } - } + /* We expect that none of this interpreters objects are shared + with other interpreters. + See https://github.com/python/cpython/issues/90228. */ } /* for debugging */ From 2771f4ee643c1fb08a2055ef0c7219913e5584c7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 2 May 2023 16:54:21 -0600 Subject: [PATCH 13/13] Move Py_TPFLAGS_READYING to each interpreter for static builtin types. --- Include/internal/pycore_typeobject.h | 2 + Objects/typeobject.c | 57 ++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index b82327cf9befee..6a5ab7e63f85c3 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -44,6 +44,8 @@ struct type_cache { typedef struct { PyTypeObject *type; + int readying; + int ready; // XXX tp_dict, tp_bases, and tp_mro can probably be statically // allocated, instead of dynamically and stored on the interpreter. PyObject *tp_dict; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 6c7c46741bf95b..cf0efe199b2828 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -167,6 +167,49 @@ static_builtin_state_clear(PyInterpreterState *interp, PyTypeObject *self) /* end static builtin helpers */ +static inline void +start_readying(PyTypeObject *type) +{ + if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = static_builtin_state_get(interp, type); + assert(state != NULL); + assert(!state->readying); + state->readying = 1; + return; + } + assert((type->tp_flags & Py_TPFLAGS_READYING) == 0); + type->tp_flags |= Py_TPFLAGS_READYING; +} + +static inline void +stop_readying(PyTypeObject *type) +{ + if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = static_builtin_state_get(interp, type); + assert(state != NULL); + assert(state->readying); + state->readying = 0; + return; + } + assert(type->tp_flags & Py_TPFLAGS_READYING); + type->tp_flags &= ~Py_TPFLAGS_READYING; +} + +static inline int +is_readying(PyTypeObject *type) +{ + if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + static_builtin_state *state = static_builtin_state_get(interp, type); + assert(state != NULL); + return state->readying; + } + return (type->tp_flags & Py_TPFLAGS_READYING) != 0; +} + + /* accessors for objects stored on PyTypeObject */ static inline PyObject * @@ -471,7 +514,7 @@ _PyType_CheckConsistency(PyTypeObject *type) CHECK(Py_REFCNT(type) >= 1); CHECK(PyType_Check(type)); - CHECK(!(type->tp_flags & Py_TPFLAGS_READYING)); + CHECK(!is_readying(type)); CHECK(lookup_tp_dict(type) != NULL); if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { @@ -4419,7 +4462,7 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) /* Look in tp_dict of types in MRO */ PyObject *mro = lookup_tp_mro(type); if (mro == NULL) { - if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) { + if (!is_readying(type)) { if (PyType_Ready(type) < 0) { *error = -1; return NULL; @@ -7181,9 +7224,8 @@ type_ready_post_checks(PyTypeObject *type) static int type_ready(PyTypeObject *type, int rerunbuiltin) { - _PyObject_ASSERT((PyObject *)type, - (type->tp_flags & Py_TPFLAGS_READYING) == 0); - type->tp_flags |= Py_TPFLAGS_READYING; + _PyObject_ASSERT((PyObject *)type, !is_readying(type)); + start_readying(type); if (type_ready_pre_checks(type) < 0) { goto error; @@ -7238,13 +7280,14 @@ type_ready(PyTypeObject *type, int rerunbuiltin) } /* All done -- set the ready flag */ - type->tp_flags = (type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY; + type->tp_flags = type->tp_flags | Py_TPFLAGS_READY; + stop_readying(type); assert(_PyType_CheckConsistency(type)); return 0; error: - type->tp_flags &= ~Py_TPFLAGS_READYING; + stop_readying(type); return -1; }