bpo-35810: Incref heap-allocated types in PyObject_Init (GH-11661)
* Incref heap-allocated types in PyObject_Init * Add documentation and porting notes to What's New
This commit is contained in:
parent
1fc5bf2ff2
commit
364f0b0f19
|
@ -509,6 +509,12 @@ Build and C API Changes
|
|||
``1`` for objects implementing ``__index__()``.
|
||||
(Contributed by Serhiy Storchaka in :issue:`36048`.)
|
||||
|
||||
* Heap-allocated type objects will now increase their reference count
|
||||
in :c:func:`PyObject_Init` (and its parallel macro ``PyObject_INIT``)
|
||||
instead of in :c:func:`PyType_GenericAlloc`. Types that modify instance
|
||||
allocation or deallocation may need to be adjusted.
|
||||
(Contributed by Eddie Elizondo in :issue:`35810`.)
|
||||
|
||||
|
||||
Deprecated
|
||||
==========
|
||||
|
@ -732,6 +738,67 @@ Changes in the C API
|
|||
(Contributed by Inada Naoki in :issue:`36381`.)
|
||||
|
||||
|
||||
Changes in the C API
|
||||
--------------------------
|
||||
|
||||
* Instances of heap-allocated types (such as those created with
|
||||
:c:func:`PyType_FromSpec`) hold a reference to their type object.
|
||||
Increasing the reference count of these type objects has been moved from
|
||||
:c:func:`PyType_GenericAlloc` to the more low-level functions,
|
||||
:c:func:`PyObject_Init` and :c:func:`PyObject_INIT`.
|
||||
This makes types created through :c:func:`PyType_FromSpec` behave like
|
||||
other classes in managed code.
|
||||
|
||||
Statically allocated types are not affected.
|
||||
|
||||
For the vast majority of cases, there should be no side effect.
|
||||
However, types that manually increase the reference count after allocating
|
||||
an instance (perhaps to work around the bug) may now become immortal.
|
||||
To avoid this, these classes need to call Py_DECREF on the type object
|
||||
during instance deallocation.
|
||||
|
||||
To correctly port these types into 3.8, please apply the following
|
||||
changes:
|
||||
|
||||
* Remove :c:macro:`Py_INCREF` on the type object after allocating an
|
||||
instance - if any.
|
||||
This may happen after calling :c:func:`PyObject_New`,
|
||||
:c:func:`PyObject_NewVar`, :c:func:`PyObject_GC_New`,
|
||||
:c:func:`PyObject_GC_NewVar`, or any other custom allocator that uses
|
||||
:c:func:`PyObject_Init` or :c:func:`PyObject_INIT`.
|
||||
|
||||
Example::
|
||||
|
||||
static foo_struct *
|
||||
foo_new(PyObject *type) {
|
||||
foo_struct *foo = PyObject_GC_New(foo_struct, (PyTypeObject *) type);
|
||||
if (foo == NULL)
|
||||
return NULL;
|
||||
#if PY_VERSION_HEX < 0x03080000
|
||||
// Workaround for Python issue 35810; no longer necessary in Python 3.8
|
||||
PY_INCREF(type)
|
||||
#endif
|
||||
return foo;
|
||||
}
|
||||
|
||||
* Ensure that all custom ``tp_dealloc`` functions of heap-allocated types
|
||||
decrease the type's reference count.
|
||||
|
||||
Example::
|
||||
|
||||
static void
|
||||
foo_dealloc(foo_struct *instance) {
|
||||
PyObject *type = Py_TYPE(instance);
|
||||
PyObject_GC_Del(instance);
|
||||
#if PY_VERSION_HEX >= 0x03080000
|
||||
// This was not needed before Python 3.8 (Python issue 35810)
|
||||
Py_DECREF(type);
|
||||
#endif
|
||||
}
|
||||
|
||||
(Contributed by Eddie Elizondo in :issue:`35810`.)
|
||||
|
||||
|
||||
CPython bytecode changes
|
||||
------------------------
|
||||
|
||||
|
|
|
@ -138,6 +138,9 @@ _PyObject_INIT(PyObject *op, PyTypeObject *typeobj)
|
|||
{
|
||||
assert(op != NULL);
|
||||
Py_TYPE(op) = typeobj;
|
||||
if (PyType_GetFlags(typeobj) & Py_TPFLAGS_HEAPTYPE) {
|
||||
Py_INCREF(typeobj);
|
||||
}
|
||||
_Py_NewReference(op);
|
||||
return op;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Modify ``PyObject_Init`` to correctly increase the refcount of heap-
|
||||
allocated Type objects. Also fix the refcounts of the heap-allocated types
|
||||
that were either doing this manually or not decreasing the type's refcount
|
||||
in tp_dealloc
|
|
@ -250,7 +250,10 @@ PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
|
|||
static void
|
||||
PyCursesPanel_Dealloc(PyCursesPanelObject *po)
|
||||
{
|
||||
PyObject *obj = (PyObject *) panel_userptr(po->pan);
|
||||
PyObject *tp, *obj;
|
||||
|
||||
tp = (PyObject *) Py_TYPE(po);
|
||||
obj = (PyObject *) panel_userptr(po->pan);
|
||||
if (obj) {
|
||||
(void)set_panel_userptr(po->pan, NULL);
|
||||
Py_DECREF(obj);
|
||||
|
@ -261,6 +264,7 @@ PyCursesPanel_Dealloc(PyCursesPanelObject *po)
|
|||
remove_lop(po);
|
||||
}
|
||||
PyObject_DEL(po);
|
||||
Py_DECREF(tp);
|
||||
}
|
||||
|
||||
/* panel_above(NULL) returns the bottom panel in the stack. To get
|
||||
|
|
|
@ -617,7 +617,6 @@ Tkapp_New(const char *screenName, const char *className,
|
|||
v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type);
|
||||
if (v == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(Tkapp_Type);
|
||||
|
||||
v->interp = Tcl_CreateInterp();
|
||||
v->wantobjects = wantobjects;
|
||||
|
@ -802,7 +801,6 @@ newPyTclObject(Tcl_Obj *arg)
|
|||
self = PyObject_New(PyTclObject, (PyTypeObject *) PyTclObject_Type);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(PyTclObject_Type);
|
||||
Tcl_IncrRefCount(arg);
|
||||
self->value = arg;
|
||||
self->string = NULL;
|
||||
|
@ -2722,7 +2720,6 @@ Tktt_New(PyObject *func)
|
|||
v = PyObject_New(TkttObject, (PyTypeObject *) Tktt_Type);
|
||||
if (v == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(Tktt_Type);
|
||||
|
||||
Py_INCREF(func);
|
||||
v->token = NULL;
|
||||
|
|
|
@ -230,6 +230,9 @@ PyObject_Init(PyObject *op, PyTypeObject *tp)
|
|||
return PyErr_NoMemory();
|
||||
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
|
||||
Py_TYPE(op) = tp;
|
||||
if (PyType_GetFlags(tp) & Py_TPFLAGS_HEAPTYPE) {
|
||||
Py_INCREF(tp);
|
||||
}
|
||||
_Py_NewReference(op);
|
||||
return op;
|
||||
}
|
||||
|
@ -240,9 +243,8 @@ PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, Py_ssize_t size)
|
|||
if (op == NULL)
|
||||
return (PyVarObject *) PyErr_NoMemory();
|
||||
/* Any changes should be reflected in PyObject_INIT_VAR */
|
||||
op->ob_size = size;
|
||||
Py_TYPE(op) = tp;
|
||||
_Py_NewReference((PyObject *)op);
|
||||
Py_SIZE(op) = size;
|
||||
PyObject_Init((PyObject *)op, tp);
|
||||
return op;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,12 +63,17 @@ static void
|
|||
structseq_dealloc(PyStructSequence *obj)
|
||||
{
|
||||
Py_ssize_t i, size;
|
||||
PyTypeObject *tp;
|
||||
|
||||
tp = (PyTypeObject *) Py_TYPE(obj);
|
||||
size = REAL_SIZE(obj);
|
||||
for (i = 0; i < size; ++i) {
|
||||
Py_XDECREF(obj->ob_item[i]);
|
||||
}
|
||||
PyObject_GC_Del(obj);
|
||||
if (PyType_GetFlags(tp) & Py_TPFLAGS_HEAPTYPE) {
|
||||
Py_DECREF(tp);
|
||||
}
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
|
@ -987,9 +987,6 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
|
|||
|
||||
memset(obj, '\0', size);
|
||||
|
||||
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
|
||||
Py_INCREF(type);
|
||||
|
||||
if (type->tp_itemsize == 0)
|
||||
(void)PyObject_INIT(obj, type);
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue