summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>2008-06-16 19:12:42 (GMT)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>2008-06-16 19:12:42 (GMT)
commita8919fe631c50a35d355e7174676283ca57e465e (patch)
tree2275d64a09c5b95c92ff04ca21777a983623823f
parent305480c9dcec736cba91db62062f75b5ceff9b60 (diff)
downloadcpython-a8919fe631c50a35d355e7174676283ca57e465e.zip
cpython-a8919fe631c50a35d355e7174676283ca57e465e.tar.gz
cpython-a8919fe631c50a35d355e7174676283ca57e465e.tar.bz2
Issue 3110: Crash with weakref subclass,
seen after a "import multiprocessing.reduction" An instance of a weakref subclass can have attributes. If such a weakref holds the only strong reference to the object, deleting the weakref will delete the object. In this case, the callback must not be called, because the ref object is being deleted!
-rw-r--r--Lib/test/test_weakref.py41
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/weakrefobject.c16
3 files changed, 55 insertions, 5 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 185105b..c70804e 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -666,7 +666,7 @@ class ReferencesTestCase(TestBase):
w = Target()
-class SubclassableWeakrefTestCase(unittest.TestCase):
+class SubclassableWeakrefTestCase(TestBase):
def test_subclass_refs(self):
class MyRef(weakref.ref):
@@ -730,6 +730,44 @@ class SubclassableWeakrefTestCase(unittest.TestCase):
self.assertEqual(r.meth(), "abcdef")
self.failIf(hasattr(r, "__dict__"))
+ def test_subclass_refs_with_cycle(self):
+ # Bug #3110
+ # An instance of a weakref subclass can have attributes.
+ # If such a weakref holds the only strong reference to the object,
+ # deleting the weakref will delete the object. In this case,
+ # the callback must not be called, because the ref object is
+ # being deleted.
+ class MyRef(weakref.ref):
+ pass
+
+ # Use a local callback, for "regrtest -R::"
+ # to detect refcounting problems
+ def callback(w):
+ self.cbcalled += 1
+
+ o = C()
+ r1 = MyRef(o, callback)
+ r1.o = o
+ del o
+
+ del r1 # Used to crash here
+
+ self.assertEqual(self.cbcalled, 0)
+
+ # Same test, with two weakrefs to the same object
+ # (since code paths are different)
+ o = C()
+ r1 = MyRef(o, callback)
+ r2 = MyRef(o, callback)
+ r1.r = r2
+ r2.o = o
+ del o
+ del r2
+
+ del r1 # Used to crash here
+
+ self.assertEqual(self.cbcalled, 0)
+
class Object:
def __init__(self, arg):
@@ -1171,6 +1209,7 @@ def test_main():
MappingTestCase,
WeakValueDictionaryTestCase,
WeakKeyDictionaryTestCase,
+ SubclassableWeakrefTestCase,
)
test_support.run_doctest(sys.modules[__name__])
diff --git a/Misc/NEWS b/Misc/NEWS
index cd44835..386f3f5 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.6 beta 1?
Core and Builtins
-----------------
+- Issue #3100: Corrected a crash on deallocation of a subclassed weakref which
+ holds the last (strong) reference to its referent.
+
- Add future_builtins.ascii().
- Several set methods now accept multiple arguments: update(), union(),
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 1aee5a5..5cd9173 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -907,7 +907,8 @@ PyObject_ClearWeakRefs(PyObject *object)
current->wr_callback = NULL;
clear_weakref(current);
if (callback != NULL) {
- handle_callback(current, callback);
+ if (current->ob_refcnt > 0)
+ handle_callback(current, callback);
Py_DECREF(callback);
}
}
@@ -925,9 +926,15 @@ PyObject_ClearWeakRefs(PyObject *object)
for (i = 0; i < count; ++i) {
PyWeakReference *next = current->wr_next;
- Py_INCREF(current);
- PyTuple_SET_ITEM(tuple, i * 2, (PyObject *) current);
- PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback);
+ if (current->ob_refcnt > 0)
+ {
+ Py_INCREF(current);
+ PyTuple_SET_ITEM(tuple, i * 2, (PyObject *) current);
+ PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback);
+ }
+ else {
+ Py_DECREF(current->wr_callback);
+ }
current->wr_callback = NULL;
clear_weakref(current);
current = next;
@@ -935,6 +942,7 @@ PyObject_ClearWeakRefs(PyObject *object)
for (i = 0; i < count; ++i) {
PyObject *callback = PyTuple_GET_ITEM(tuple, i * 2 + 1);
+ /* The tuple may have slots left to NULL */
if (callback != NULL) {
PyObject *item = PyTuple_GET_ITEM(tuple, i * 2);
handle_callback((PyWeakReference *)item, callback);