mirror of https://github.com/python/cpython
gh-94673: Add Per-Interpreter tp_weaklist for Static Builtin Types (#95302)
* Store tp_weaklist on the interpreter state for static builtin types. * Factor out _PyStaticType_GET_WEAKREFS_LISTPTR(). * Add _PyStaticType_ClearWeakRefs(). * Add a comment about how _PyStaticType_ClearWeakRefs() loops. * Document the change. * Update Doc/whatsnew/3.12.rst * Fix a typo.
This commit is contained in:
parent
6e44bf9558
commit
3e7cad3bca
|
@ -1942,6 +1942,13 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
Weak reference list head, for weak references to this type object. Not
|
Weak reference list head, for weak references to this type object. Not
|
||||||
inherited. Internal use only.
|
inherited. Internal use only.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12
|
||||||
|
|
||||||
|
Internals detail: For the static builtin types this is always ``NULL``,
|
||||||
|
even if weakrefs are added. Instead, the weakrefs for each are stored
|
||||||
|
on ``PyInterpreterState``. Use the public C-API or the internal
|
||||||
|
``_PyObject_GET_WEAKREFS_LISTPTR()`` macro to avoid the distinction.
|
||||||
|
|
||||||
**Inheritance:**
|
**Inheritance:**
|
||||||
|
|
||||||
This field is not inherited.
|
This field is not inherited.
|
||||||
|
|
|
@ -413,6 +413,13 @@ Porting to Python 3.12
|
||||||
``Py_UNICODE*`` based format (e.g. ``u``, ``Z``) anymore. Please migrate
|
``Py_UNICODE*`` based format (e.g. ``u``, ``Z``) anymore. Please migrate
|
||||||
to other formats for Unicode like ``s``, ``z``, ``es``, and ``U``.
|
to other formats for Unicode like ``s``, ``z``, ``es``, and ``U``.
|
||||||
|
|
||||||
|
* ``tp_weaklist`` for all static builtin types is always ``NULL``.
|
||||||
|
This is an internal-only field on ``PyTypeObject``
|
||||||
|
but we're pointing out the change in case someone happens to be
|
||||||
|
accessing the field directly anyway. To avoid breakage, consider
|
||||||
|
using the existing public C-API instead, or, if necessary, the
|
||||||
|
(internal-only) ``_PyObject_GET_WEAKREFS_LISTPTR()`` macro.
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ struct _typeobject {
|
||||||
PyObject *tp_mro; /* method resolution order */
|
PyObject *tp_mro; /* method resolution order */
|
||||||
PyObject *tp_cache;
|
PyObject *tp_cache;
|
||||||
PyObject *tp_subclasses;
|
PyObject *tp_subclasses;
|
||||||
PyObject *tp_weaklist;
|
PyObject *tp_weaklist; /* not used for static builtin types */
|
||||||
destructor tp_del;
|
destructor tp_del;
|
||||||
|
|
||||||
/* Type attribute cache version tag. Added in version 2.6 */
|
/* Type attribute cache version tag. Added in version 2.6 */
|
||||||
|
|
|
@ -220,6 +220,12 @@ extern void _Py_PrintReferenceAddresses(FILE *);
|
||||||
static inline PyObject **
|
static inline PyObject **
|
||||||
_PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
|
_PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
|
||||||
{
|
{
|
||||||
|
if (PyType_Check(op) &&
|
||||||
|
((PyTypeObject *)op)->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
|
||||||
|
static_builtin_state *state = _PyStaticType_GetState(
|
||||||
|
(PyTypeObject *)op);
|
||||||
|
return _PyStaticType_GET_WEAKREFS_LISTPTR(state);
|
||||||
|
}
|
||||||
Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset;
|
Py_ssize_t offset = Py_TYPE(op)->tp_weaklistoffset;
|
||||||
return (PyObject **)((char *)op + offset);
|
return (PyObject **)((char *)op + offset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,20 @@ struct type_cache {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyTypeObject *type;
|
PyTypeObject *type;
|
||||||
|
/* We never clean up weakrefs for static builtin types since
|
||||||
|
they will effectively never get triggered. However, there
|
||||||
|
are also some diagnostic uses for the list of weakrefs,
|
||||||
|
so we still keep it. */
|
||||||
|
PyObject *tp_weaklist;
|
||||||
} static_builtin_state;
|
} static_builtin_state;
|
||||||
|
|
||||||
|
static inline PyObject **
|
||||||
|
_PyStaticType_GET_WEAKREFS_LISTPTR(static_builtin_state *state)
|
||||||
|
{
|
||||||
|
assert(state != NULL);
|
||||||
|
return &state->tp_weaklist;
|
||||||
|
}
|
||||||
|
|
||||||
struct types_state {
|
struct types_state {
|
||||||
struct type_cache type_cache;
|
struct type_cache type_cache;
|
||||||
size_t num_builtins_initialized;
|
size_t num_builtins_initialized;
|
||||||
|
@ -58,6 +70,7 @@ extern PyStatus _PyTypes_InitSlotDefs(void);
|
||||||
|
|
||||||
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
|
extern int _PyStaticType_InitBuiltin(PyTypeObject *type);
|
||||||
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
|
extern static_builtin_state * _PyStaticType_GetState(PyTypeObject *);
|
||||||
|
extern void _PyStaticType_ClearWeakRefs(PyTypeObject *type);
|
||||||
extern void _PyStaticType_Dealloc(PyTypeObject *type);
|
extern void _PyStaticType_Dealloc(PyTypeObject *type);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,8 @@ static_builtin_state_init(PyTypeObject *self)
|
||||||
|
|
||||||
static_builtin_state *state = static_builtin_state_get(interp, self);
|
static_builtin_state *state = static_builtin_state_get(interp, self);
|
||||||
state->type = self;
|
state->type = self;
|
||||||
|
/* state->tp_weaklist is left NULL until insert_head() or insert_after()
|
||||||
|
(in weakrefobject.c) sets it. */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -138,6 +140,7 @@ static_builtin_state_clear(PyTypeObject *self)
|
||||||
|
|
||||||
static_builtin_state *state = static_builtin_state_get(interp, self);
|
static_builtin_state *state = static_builtin_state_get(interp, self);
|
||||||
state->type = NULL;
|
state->type = NULL;
|
||||||
|
assert(state->tp_weaklist == NULL); // It was already cleared out.
|
||||||
static_builtin_index_clear(self);
|
static_builtin_index_clear(self);
|
||||||
|
|
||||||
assert(interp->types.num_builtins_initialized > 0);
|
assert(interp->types.num_builtins_initialized > 0);
|
||||||
|
@ -502,6 +505,8 @@ static PyMemberDef type_members[] = {
|
||||||
{"__basicsize__", T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),READONLY},
|
{"__basicsize__", T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),READONLY},
|
||||||
{"__itemsize__", T_PYSSIZET, offsetof(PyTypeObject, tp_itemsize), READONLY},
|
{"__itemsize__", T_PYSSIZET, offsetof(PyTypeObject, tp_itemsize), READONLY},
|
||||||
{"__flags__", T_ULONG, offsetof(PyTypeObject, tp_flags), READONLY},
|
{"__flags__", T_ULONG, offsetof(PyTypeObject, tp_flags), READONLY},
|
||||||
|
/* Note that this value is misleading for static builtin types,
|
||||||
|
since the memory at this offset will always be NULL. */
|
||||||
{"__weakrefoffset__", T_PYSSIZET,
|
{"__weakrefoffset__", T_PYSSIZET,
|
||||||
offsetof(PyTypeObject, tp_weaklistoffset), READONLY},
|
offsetof(PyTypeObject, tp_weaklistoffset), READONLY},
|
||||||
{"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY},
|
{"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY},
|
||||||
|
@ -4353,6 +4358,7 @@ _PyStaticType_Dealloc(PyTypeObject *type)
|
||||||
type->tp_flags &= ~Py_TPFLAGS_READY;
|
type->tp_flags &= ~Py_TPFLAGS_READY;
|
||||||
|
|
||||||
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
|
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
|
||||||
|
_PyStaticType_ClearWeakRefs(type);
|
||||||
static_builtin_state_clear(type);
|
static_builtin_state_clear(type);
|
||||||
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
|
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
|
||||||
}
|
}
|
||||||
|
|
|
@ -1019,3 +1019,22 @@ PyObject_ClearWeakRefs(PyObject *object)
|
||||||
PyErr_Restore(err_type, err_value, err_tb);
|
PyErr_Restore(err_type, err_value, err_tb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called by _PyStaticType_Dealloc() to clear weak references.
|
||||||
|
*
|
||||||
|
* This is called at the end of runtime finalization, so we can just
|
||||||
|
* wipe out the type's weaklist. We don't bother with callbacks
|
||||||
|
* or anything else.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PyStaticType_ClearWeakRefs(PyTypeObject *type)
|
||||||
|
{
|
||||||
|
static_builtin_state *state = _PyStaticType_GetState(type);
|
||||||
|
PyObject **list = _PyStaticType_GET_WEAKREFS_LISTPTR(state);
|
||||||
|
while (*list != NULL) {
|
||||||
|
/* Note that clear_weakref() pops the first ref off the type's
|
||||||
|
weaklist before clearing its wr_object and wr_callback.
|
||||||
|
That is how we're able to loop over the list. */
|
||||||
|
clear_weakref((PyWeakReference *)*list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue