diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -286,13 +286,9 @@ However, if the derived class declares a __slots__, those slots are already gone. */ - if (_PyIOBase_finalize((PyObject *) self) < 0) { - /* When called from a heap type's dealloc, the type will be - decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ - if (PyType_HasFeature(Py_TYPE(self), Py_TPFLAGS_HEAPTYPE)) - Py_INCREF(Py_TYPE(self)); + if (_PyIOBase_finalize((PyObject *) self) < 0) return; - } + _PyObject_GC_UNTRACK(self); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -881,6 +881,40 @@ return 0; } +/* Code to monitor if the object gets deleted during the call to an objects + * destructor method. If not, it has been resurrected and we must not + * free its type. This code uses static globals and thus relies on the GIL + * not being released for the duration, but that shouldn't happen, really. + * If that becomes a problem then we can fix it with TLS. + * The fundamental problem is that tp_dealloc does not return information + * about whether it deleted the object or not. + */ +static freefunc base_free; +static PyObject *base_target; +static int base_hit; +static void checking_free(void *ptr) +{ + if ((PyObject*)ptr == base_target) + base_hit = 1; + base_free(ptr); +} + +static int +call_basedealloc(destructor basedealloc, PyObject *self) +{ + PyTypeObject *type = Py_TYPE(self); + base_free = type->tp_free; + base_target = self; + base_hit = 0; + + type->tp_free = checking_free; + basedealloc(self); + type->tp_free = base_free; + return base_hit == 1; +} + + + static void subtype_dealloc(PyObject *self) { @@ -920,10 +954,12 @@ /* Call the base tp_dealloc() */ assert(basedealloc); - basedealloc(self); - - /* Can't reference self beyond this point */ - Py_DECREF(type); + if (call_basedealloc(basedealloc, self)) { + /* Object was indeed destroyed. + * Can't reference self beyond this point + */ + Py_DECREF(type); + } /* Done */ return; @@ -1008,10 +1044,12 @@ if (PyType_IS_GC(base)) _PyObject_GC_TRACK(self); assert(basedealloc); - basedealloc(self); - - /* Can't reference self beyond this point */ - Py_DECREF(type); + if (call_basedealloc(basedealloc, self)) { + /* Object was indeed destroyed. + * Can't reference self beyond this point + */ + Py_DECREF(type); + } endlabel: ++_PyTrash_delete_nesting;