]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Fixed the gc-vs-__del__ bugs for new-style classes. That's it for this one.
authorTim Peters <tim.peters@gmail.com>
Tue, 8 Apr 2003 20:33:05 +0000 (20:33 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 8 Apr 2003 20:33:05 +0000 (20:33 +0000)
Lib/test/test_gc.py
Misc/NEWS
Modules/gcmodule.c

index 3824d7ca6db8650856c028a44505a303af40cd1c..3b8f415e50830fb67e0fac21f6fad91013cd6a15 100644 (file)
@@ -257,6 +257,48 @@ def test_boom2():
     expect(gc.collect(), 4, "boom2")
     expect(len(gc.garbage), garbagelen, "boom2")
 
+# boom__new and boom2_new are exactly like boom and boom2, except use
+# new-style classes.
+
+class Boom_New(object):
+    def __getattr__(self, someattribute):
+        del self.attr
+        raise AttributeError
+
+def test_boom_new():
+    a = Boom_New()
+    b = Boom_New()
+    a.attr = b
+    b.attr = a
+
+    gc.collect()
+    garbagelen = len(gc.garbage)
+    del a, b
+    expect(gc.collect(), 4, "boom_new")
+    expect(len(gc.garbage), garbagelen, "boom_new")
+
+class Boom2_New(object):
+    def __init__(self):
+        self.x = 0
+
+    def __getattr__(self, someattribute):
+        self.x += 1
+        if self.x > 1:
+            del self.attr
+        raise AttributeError
+
+def test_boom2_new():
+    a = Boom2_New()
+    b = Boom2_New()
+    a.attr = b
+    b.attr = a
+
+    gc.collect()
+    garbagelen = len(gc.garbage)
+    del a, b
+    expect(gc.collect(), 4, "boom2_new")
+    expect(len(gc.garbage), garbagelen, "boom2_new")
+
 def test_all():
     gc.collect() # Delete 2nd generation garbage
     run_test("lists", test_list)
@@ -275,6 +317,8 @@ def test_all():
     run_test("trashcan", test_trashcan)
     run_test("boom", test_boom)
     run_test("boom2", test_boom2)
+    run_test("boom_new", test_boom_new)
+    run_test("boom2_new", test_boom2_new)
 
 def test():
     if verbose:
index 77bb761784ac5bb113bb59c864217935e310734f..485b17da96bfcd7dedbfd82c00da7e97734573ac 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,14 @@ What's New in Python 2.2.3 ?
 Release date: XX-XXX-2003
 ============================
 
+- Some horridly obscure problems were fixed involving interaction
+  between garbage collection and classes with "ambitious" getattr hooks.
+  If a class instance didn't have a __del__ method, but did have a
+  __getattr__ hook, and the instance became reachable only from an
+  unreachable cycle, and the hook resurrected or deleted unreachable
+  objects when asked to resolve "__del__", anything up to a segfault
+  could happen.  That's been repaired.
+
 - Skip locale test on Mac OS X.
 
 - Backported the "largefile" requirement for test_largefile on Mac OS X.
index 81b6040e1708f4434f9bf96a26795e764824256d..f1edb72e1fae1c24ab2ad9108094a504a050a88c 100644 (file)
@@ -259,7 +259,6 @@ move_root_reachable(PyGC_Head *reachable)
  * could call the class's __getattr__ hook (if any).  That could invoke
  * arbitrary Python code, mutating the object graph in arbitrary ways, and
  * that was the source of some excruciatingly subtle bugs.
- * XXX This is still broken for new-style classes.
  */
 static int
 has_finalizer(PyObject *op)
@@ -274,8 +273,7 @@ has_finalizer(PyObject *op)
        if (PyInstance_Check(op))
                return _PyInstance_Lookup(op, delstr) != NULL;
        else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE))
-               /* XXX This path is still Evil. */
-               return PyObject_HasAttr(op, delstr);
+               return _PyType_Lookup(op->ob_type, delstr) != NULL;
        else
                return 0;
 }
@@ -284,20 +282,18 @@ has_finalizer(PyObject *op)
 static void
 move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
 {
-       PyGC_Head *next;
        PyGC_Head *gc = unreachable->gc.gc_next;
-       for (; gc != unreachable; gc=next) {
+
+       while (gc != unreachable) {
+               PyGC_Head *next = gc->gc.gc_next;
                PyObject *op = FROM_GC(gc);
-               /* XXX has_finalizer() may result in arbitrary Python
-                  code being run. */
+
                if (has_finalizer(op)) {
-                       next = gc->gc.gc_next;
                        gc_list_remove(gc);
                        gc_list_append(gc, finalizers);
                        gc->gc.gc_refs = GC_MOVED;
                }
-               else
-                       next = gc->gc.gc_next;
+               gc = next;
        }
 }