Skip to content

Commit 364f0b0

Browse files
eduardo-elizondoencukou
authored andcommitted
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
1 parent 1fc5bf2 commit 364f0b0

File tree

8 files changed

+89
-10
lines changed

8 files changed

+89
-10
lines changed

Doc/whatsnew/3.8.rst

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ Build and C API Changes
509509
``1`` for objects implementing ``__index__()``.
510510
(Contributed by Serhiy Storchaka in :issue:`36048`.)
511511

512+
* Heap-allocated type objects will now increase their reference count
513+
in :c:func:`PyObject_Init` (and its parallel macro ``PyObject_INIT``)
514+
instead of in :c:func:`PyType_GenericAlloc`. Types that modify instance
515+
allocation or deallocation may need to be adjusted.
516+
(Contributed by Eddie Elizondo in :issue:`35810`.)
517+
512518

513519
Deprecated
514520
==========
@@ -732,6 +738,67 @@ Changes in the C API
732738
(Contributed by Inada Naoki in :issue:`36381`.)
733739

734740

741+
Changes in the C API
742+
--------------------------
743+
744+
* Instances of heap-allocated types (such as those created with
745+
:c:func:`PyType_FromSpec`) hold a reference to their type object.
746+
Increasing the reference count of these type objects has been moved from
747+
:c:func:`PyType_GenericAlloc` to the more low-level functions,
748+
:c:func:`PyObject_Init` and :c:func:`PyObject_INIT`.
749+
This makes types created through :c:func:`PyType_FromSpec` behave like
750+
other classes in managed code.
751+
752+
Statically allocated types are not affected.
753+
754+
For the vast majority of cases, there should be no side effect.
755+
However, types that manually increase the reference count after allocating
756+
an instance (perhaps to work around the bug) may now become immortal.
757+
To avoid this, these classes need to call Py_DECREF on the type object
758+
during instance deallocation.
759+
760+
To correctly port these types into 3.8, please apply the following
761+
changes:
762+
763+
* Remove :c:macro:`Py_INCREF` on the type object after allocating an
764+
instance - if any.
765+
This may happen after calling :c:func:`PyObject_New`,
766+
:c:func:`PyObject_NewVar`, :c:func:`PyObject_GC_New`,
767+
:c:func:`PyObject_GC_NewVar`, or any other custom allocator that uses
768+
:c:func:`PyObject_Init` or :c:func:`PyObject_INIT`.
769+
770+
Example::
771+
772+
static foo_struct *
773+
foo_new(PyObject *type) {
774+
foo_struct *foo = PyObject_GC_New(foo_struct, (PyTypeObject *) type);
775+
if (foo == NULL)
776+
return NULL;
777+
#if PY_VERSION_HEX < 0x03080000
778+
// Workaround for Python issue 35810; no longer necessary in Python 3.8
779+
PY_INCREF(type)
780+
#endif
781+
return foo;
782+
}
783+
784+
* Ensure that all custom ``tp_dealloc`` functions of heap-allocated types
785+
decrease the type's reference count.
786+
787+
Example::
788+
789+
static void
790+
foo_dealloc(foo_struct *instance) {
791+
PyObject *type = Py_TYPE(instance);
792+
PyObject_GC_Del(instance);
793+
#if PY_VERSION_HEX >= 0x03080000
794+
// This was not needed before Python 3.8 (Python issue 35810)
795+
Py_DECREF(type);
796+
#endif
797+
}
798+
799+
(Contributed by Eddie Elizondo in :issue:`35810`.)
800+
801+
735802
CPython bytecode changes
736803
------------------------
737804

Include/objimpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ _PyObject_INIT(PyObject *op, PyTypeObject *typeobj)
138138
{
139139
assert(op != NULL);
140140
Py_TYPE(op) = typeobj;
141+
if (PyType_GetFlags(typeobj) & Py_TPFLAGS_HEAPTYPE) {
142+
Py_INCREF(typeobj);
143+
}
141144
_Py_NewReference(op);
142145
return op;
143146
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Modify ``PyObject_Init`` to correctly increase the refcount of heap-
2+
allocated Type objects. Also fix the refcounts of the heap-allocated types
3+
that were either doing this manually or not decreasing the type's refcount
4+
in tp_dealloc

Modules/_curses_panel.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,10 @@ PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
250250
static void
251251
PyCursesPanel_Dealloc(PyCursesPanelObject *po)
252252
{
253-
PyObject *obj = (PyObject *) panel_userptr(po->pan);
253+
PyObject *tp, *obj;
254+
255+
tp = (PyObject *) Py_TYPE(po);
256+
obj = (PyObject *) panel_userptr(po->pan);
254257
if (obj) {
255258
(void)set_panel_userptr(po->pan, NULL);
256259
Py_DECREF(obj);
@@ -261,6 +264,7 @@ PyCursesPanel_Dealloc(PyCursesPanelObject *po)
261264
remove_lop(po);
262265
}
263266
PyObject_DEL(po);
267+
Py_DECREF(tp);
264268
}
265269

266270
/* panel_above(NULL) returns the bottom panel in the stack. To get

Modules/_tkinter.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,6 @@ Tkapp_New(const char *screenName, const char *className,
617617
v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type);
618618
if (v == NULL)
619619
return NULL;
620-
Py_INCREF(Tkapp_Type);
621620

622621
v->interp = Tcl_CreateInterp();
623622
v->wantobjects = wantobjects;
@@ -802,7 +801,6 @@ newPyTclObject(Tcl_Obj *arg)
802801
self = PyObject_New(PyTclObject, (PyTypeObject *) PyTclObject_Type);
803802
if (self == NULL)
804803
return NULL;
805-
Py_INCREF(PyTclObject_Type);
806804
Tcl_IncrRefCount(arg);
807805
self->value = arg;
808806
self->string = NULL;
@@ -2722,7 +2720,6 @@ Tktt_New(PyObject *func)
27222720
v = PyObject_New(TkttObject, (PyTypeObject *) Tktt_Type);
27232721
if (v == NULL)
27242722
return NULL;
2725-
Py_INCREF(Tktt_Type);
27262723

27272724
Py_INCREF(func);
27282725
v->token = NULL;

Objects/object.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ PyObject_Init(PyObject *op, PyTypeObject *tp)
230230
return PyErr_NoMemory();
231231
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
232232
Py_TYPE(op) = tp;
233+
if (PyType_GetFlags(tp) & Py_TPFLAGS_HEAPTYPE) {
234+
Py_INCREF(tp);
235+
}
233236
_Py_NewReference(op);
234237
return op;
235238
}
@@ -240,9 +243,8 @@ PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, Py_ssize_t size)
240243
if (op == NULL)
241244
return (PyVarObject *) PyErr_NoMemory();
242245
/* Any changes should be reflected in PyObject_INIT_VAR */
243-
op->ob_size = size;
244-
Py_TYPE(op) = tp;
245-
_Py_NewReference((PyObject *)op);
246+
Py_SIZE(op) = size;
247+
PyObject_Init((PyObject *)op, tp);
246248
return op;
247249
}
248250

Objects/structseq.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ static void
6363
structseq_dealloc(PyStructSequence *obj)
6464
{
6565
Py_ssize_t i, size;
66+
PyTypeObject *tp;
6667

68+
tp = (PyTypeObject *) Py_TYPE(obj);
6769
size = REAL_SIZE(obj);
6870
for (i = 0; i < size; ++i) {
6971
Py_XDECREF(obj->ob_item[i]);
7072
}
7173
PyObject_GC_Del(obj);
74+
if (PyType_GetFlags(tp) & Py_TPFLAGS_HEAPTYPE) {
75+
Py_DECREF(tp);
76+
}
7277
}
7378

7479
/*[clinic input]

Objects/typeobject.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -987,9 +987,6 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
987987

988988
memset(obj, '\0', size);
989989

990-
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
991-
Py_INCREF(type);
992-
993990
if (type->tp_itemsize == 0)
994991
(void)PyObject_INIT(obj, type);
995992
else

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy