summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2002-08-06 21:41:44 (GMT)
committerGuido van Rossum <guido@python.org>2002-08-06 21:41:44 (GMT)
commit22b1387c51a979a6921c18c6e3f7126d24b85e7c (patch)
treedc05245c255947cd5384036a79a9898b1ec9426d
parent31f3db39f395eef33cb23f2862291c62aaed623f (diff)
downloadcpython-22b1387c51a979a6921c18c6e3f7126d24b85e7c.zip
cpython-22b1387c51a979a6921c18c6e3f7126d24b85e7c.tar.gz
cpython-22b1387c51a979a6921c18c6e3f7126d24b85e7c.tar.bz2
Fix SF bug 574207 (chained __slots__ dealloc segfault).
This is inspired by SF patch 581742 (by Jonathan Hogg, who also submitted the bug report, and two other suggested patches), but separates the non-GC case from the GC case to avoid testing for GC several times. Had to fix an assert() from call_finalizer() that asserted that the object wasn't untracked, because it's possible that the object isn't GC'ed!
-rw-r--r--Objects/typeobject.c62
1 files changed, 52 insertions, 10 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index f7069a0..b7edb14 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -401,7 +401,8 @@ call_finalizer(PyObject *self)
_Py_NewReference(self);
self->ob_refcnt = refcnt;
}
- assert(_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
+ assert(!PyType_IS_GC(self->ob_type) ||
+ _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
/* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but
* _Py_NewReference bumped it again, so that's a wash.
* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
@@ -423,14 +424,55 @@ subtype_dealloc(PyObject *self)
PyTypeObject *type, *base;
destructor basedealloc;
- /* This exists so we can DECREF self->ob_type */
+ /* Extract the type; we expect it to be a heap type */
+ type = self->ob_type;
+ assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
+
+ /* Test whether the type has GC exactly once */
+
+ if (!PyType_IS_GC(type)) {
+ /* It's really rare to find a dynamic type that doesn't have
+ GC; it can only happen when deriving from 'object' and not
+ adding any slots or instance variables. This allows
+ certain simplifications: there's no need to call
+ clear_slots(), or DECREF the dict, or clear weakrefs. */
+
+ /* Maybe call finalizer; exit early if resurrected */
+ if (call_finalizer(self) < 0)
+ return;
+
+ /* Find the nearest base with a different tp_dealloc */
+ base = type;
+ while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
+ assert(base->ob_size == 0);
+ base = base->tp_base;
+ assert(base);
+ }
+
+ /* Call the base tp_dealloc() */
+ assert(basedealloc);
+ basedealloc(self);
+
+ /* Can't reference self beyond this point */
+ Py_DECREF(type);
+
+ /* Done */
+ return;
+ }
+ /* We get here only if the type has GC */
+
+ /* UnTrack and re-Track around the trashcan macro, alas */
+ _PyObject_GC_UNTRACK(self);
+ Py_TRASHCAN_SAFE_BEGIN(self);
+ _PyObject_GC_TRACK(self); /* We'll untrack for real later */
+
+ /* Maybe call finalizer; exit early if resurrected */
if (call_finalizer(self) < 0)
return;
/* Find the nearest base with a different tp_dealloc
and clear slots while we're at it */
- type = self->ob_type;
base = type;
while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
if (base->ob_size)
@@ -456,7 +498,7 @@ subtype_dealloc(PyObject *self)
PyObject_ClearWeakRefs(self);
/* Finalize GC if the base doesn't do GC and we do */
- if (PyType_IS_GC(type) && !PyType_IS_GC(base))
+ if (!PyType_IS_GC(base))
_PyObject_GC_UNTRACK(self);
/* Call the base tp_dealloc() */
@@ -464,9 +506,9 @@ subtype_dealloc(PyObject *self)
basedealloc(self);
/* Can't reference self beyond this point */
- if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- Py_DECREF(type);
- }
+ Py_DECREF(type);
+
+ Py_TRASHCAN_SAFE_END(self);
}
static PyTypeObject *solid_base(PyTypeObject *type);
@@ -2807,7 +2849,7 @@ wrap_descr_set(PyObject *self, PyObject *args, void *wrapped)
Py_INCREF(Py_None);
return Py_None;
}
-
+
static PyObject *
wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
{
@@ -2992,7 +3034,7 @@ slot_sq_length(PyObject *self)
if (len == -1 && PyErr_Occurred())
return -1;
if (len < 0) {
- PyErr_SetString(PyExc_ValueError,
+ PyErr_SetString(PyExc_ValueError,
"__len__() should return >= 0");
return -1;
}
@@ -4039,7 +4081,7 @@ update_one_slot(PyTypeObject *type, slotdef *p)
return p;
}
-static int recurse_down_subclasses(PyTypeObject *type, slotdef **pp,
+static int recurse_down_subclasses(PyTypeObject *type, slotdef **pp,
PyObject *name);
/* In the type, update the slots whose slotdefs are gathered in the pp0 array,