Commit 3ce41c99 authored by Tim Peters's avatar Tim Peters

subtype_dealloc(): A more complete fix for critical bug 840829 +

expanded the test case with a piece that needs the more-complete fix.

I'll backport this to 2.3 maint.
parent f751fd81
...@@ -318,6 +318,25 @@ class ReferencesTestCase(TestBase): ...@@ -318,6 +318,25 @@ class ReferencesTestCase(TestBase):
wr = weakref.ref(c, lambda ignore: gc.collect()) wr = weakref.ref(c, lambda ignore: gc.collect())
del c del c
# There endeth the first part. It gets worse.
del wr
c1 = C()
c1.i = C()
wr = weakref.ref(c1.i, lambda ignore: gc.collect())
c2 = C()
c2.c1 = c1
del c1 # still alive because c2 points to it
# Now when subtype_dealloc gets called on c2, it's not enough just
# that c2 is immune from gc while the weakref callbacks associated
# with c2 execute (there are none in this 2nd half of the test, btw).
# subtype_dealloc goes on to call the base classes' deallocs too,
# so any gc triggered by weakref callbacks associated with anything
# torn down by a base class dealloc can also trigger double
# deallocation of c2.
del c2
class Object: class Object:
def __init__(self, arg): def __init__(self, arg):
......
...@@ -639,10 +639,10 @@ subtype_dealloc(PyObject *self) ...@@ -639,10 +639,10 @@ subtype_dealloc(PyObject *self)
++_PyTrash_delete_nesting; ++_PyTrash_delete_nesting;
Py_TRASHCAN_SAFE_BEGIN(self); Py_TRASHCAN_SAFE_BEGIN(self);
--_PyTrash_delete_nesting; --_PyTrash_delete_nesting;
/* DO NOT restore GC tracking at this point. The weakref callback /* DO NOT restore GC tracking at this point. weakref callbacks
* (if any) may trigger GC, and if self is tracked at that point, * (if any, and whether directly here or indirectly in something we
* it will look like trash to GC and GC will try to delete it * call) may trigger GC, and if self is tracked at that point, it
* again. Double-deallocation is a subtle disaster. * will look like trash to GC and GC will try to delete self again.
*/ */
/* Find the nearest base with a different tp_dealloc */ /* Find the nearest base with a different tp_dealloc */
...@@ -658,13 +658,15 @@ subtype_dealloc(PyObject *self) ...@@ -658,13 +658,15 @@ subtype_dealloc(PyObject *self)
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
_PyObject_GC_TRACK(self); /* We'll untrack for real later */
/* Maybe call finalizer; exit early if resurrected */ /* Maybe call finalizer; exit early if resurrected */
if (type->tp_del) { if (type->tp_del) {
_PyObject_GC_TRACK(self);
type->tp_del(self); type->tp_del(self);
if (self->ob_refcnt > 0) if (self->ob_refcnt > 0)
goto endlabel; goto endlabel; /* resurrected */
else
_PyObject_GC_UNTRACK(self);
} }
/* Clear slots up to the nearest base with a different tp_dealloc */ /* Clear slots up to the nearest base with a different tp_dealloc */
...@@ -689,6 +691,7 @@ subtype_dealloc(PyObject *self) ...@@ -689,6 +691,7 @@ subtype_dealloc(PyObject *self)
} }
/* Finalize GC if the base doesn't do GC and we do */ /* Finalize GC if the base doesn't do GC and we do */
_PyObject_GC_TRACK(self);
if (!PyType_IS_GC(base)) if (!PyType_IS_GC(base))
_PyObject_GC_UNTRACK(self); _PyObject_GC_UNTRACK(self);
...@@ -730,6 +733,16 @@ subtype_dealloc(PyObject *self) ...@@ -730,6 +733,16 @@ subtype_dealloc(PyObject *self)
trashcan begin trashcan begin
GC track GC track
Q. Why did the last question say "immediately GC-track again"?
It's nowhere near immediately.
A. Because the code *used* to re-track immediately. Bad Idea.
self has a refcount of 0, and if gc ever gets its hands on it
(which can happen if any weakref callback gets invoked), it
looks like trash to gc too, and gc also tries to delete self
then. But we're already deleting self. Double dealloction is
a subtle disaster.
Q. Why the bizarre (net-zero) manipulation of Q. Why the bizarre (net-zero) manipulation of
_PyTrash_delete_nesting around the trashcan macros? _PyTrash_delete_nesting around the trashcan macros?
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment