]> granicus.if.org Git - python/commitdiff
Merged revisions 64309 via svnmerge from
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 16 Jun 2008 19:50:09 +0000 (19:50 +0000)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 16 Jun 2008 19:50:09 +0000 (19:50 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r64309 | amaury.forgeotdarc | 2008-06-16 21:12:42 +0200 (lun., 16 juin 2008) | 8 lines

  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!
........

Lib/test/test_weakref.py
Misc/NEWS
Objects/weakrefobject.c

index a056c23377206eff156fdca37beab302d3f868be..1788ac54d77393cc99553b9a8a5b9c0a8c63fc70 100644 (file)
@@ -661,7 +661,7 @@ class ReferencesTestCase(TestBase):
         w = Target()
 
 
-class SubclassableWeakrefTestCase(unittest.TestCase):
+class SubclassableWeakrefTestCase(TestBase):
 
     def test_subclass_refs(self):
         class MyRef(weakref.ref):
@@ -725,6 +725,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,
         )
     support.run_doctest(sys.modules[__name__])
 
index 4408b79a3636b705febb520dbf6fd6574c577bb8..07c5eddf3e3330d487a705ea8c6d803b974ee62b 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's new in Python 3.0b1?
 Core and Builtins
 -----------------
 
+- Issue #3100: Corrected a crash on deallocation of a subclassed weakref which
+  holds the last (strong) reference to its referent.
+
 - Issue #2630: implement PEP 3138. repr() now returns printable
   Unicode characters unescaped, to get an ASCII-only representation
   of an object use ascii().
index 040e93810ddd2b60d6a4d5d23135feee3ec6bd9e..302cdc9edf96bf45108172b2725bc1cf0a2e4f45 100644 (file)
@@ -884,7 +884,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);
             }
         }
@@ -902,9 +903,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;
@@ -912,6 +919,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);