]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Allocating a new weakref object can cause existing weakref objects for
authorFred Drake <fdrake@acm.org>
Wed, 4 Feb 2004 23:13:43 +0000 (23:13 +0000)
committerFred Drake <fdrake@acm.org>
Wed, 4 Feb 2004 23:13:43 +0000 (23:13 +0000)
the same object to be collected by the cyclic GC support if they are
only referenced by a cycle.  If the weakref being collected was one of
the weakrefs without callbacks, some local variables for the
constructor became invalid and have to be re-computed.

The test caused a segfault under a debug build without the fix applied.

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

index a50e05a2d7c00de2f970854ba31f8c0fe6614549..ae8ad2f2ffd8580832664da27bd29bd4a46b9723 100644 (file)
@@ -1,3 +1,4 @@
+import gc
 import sys
 import unittest
 import UserList
@@ -592,6 +593,37 @@ class ReferencesTestCase(TestBase):
         gc.collect()
         self.assertEqual(alist, [])
 
+    def test_gc_during_ref_creation(self):
+        self.check_gc_during_creation(weakref.ref)
+
+    def test_gc_during_proxy_creation(self):
+        self.check_gc_during_creation(weakref.proxy)
+
+    def check_gc_during_creation(self, makeref):
+        thresholds = gc.get_threshold()
+        gc.set_threshold(1, 1, 1)
+        gc.collect()
+        class A:
+            pass
+
+        def callback(*args):
+            pass
+
+        referenced = A()
+
+        a = A()
+        a.a = a
+        a.wr = makeref(referenced)
+
+        try:
+            # now make sure the object and the ref get labeled as
+            # cyclic trash:
+            a = A()
+            a.wrc = weakref.ref(referenced, callback)
+
+        finally:
+            gc.set_threshold(*thresholds)
+
 class Object:
     def __init__(self, arg):
         self.arg = arg
index cf0316a50fd943dec296a7ceee117ebb985e56b6..575a928f7528f2c538e12210b264e3315e2af957 100644 (file)
@@ -630,16 +630,23 @@ PyWeakref_NewRef(PyObject *ob, PyObject *callback)
         /* return existing weak reference if it exists */
         result = ref;
     if (result != NULL)
-        Py_XINCREF(result);
+        Py_INCREF(result);
     else {
+        /* Note: new_weakref() can trigger cyclic GC, so the weakref
+           list on ob can be mutated.  This means that the ref and
+           proxy pointers we got back earlier may have been collected,
+           so we need to compute these values again before we use
+           them. */
         result = new_weakref(ob, callback);
         if (result != NULL) {
             if (callback == NULL) {
                 insert_head(result, list);
             }
             else {
-                PyWeakReference *prev = (proxy == NULL) ? ref : proxy;
+                PyWeakReference *prev;
 
+                get_basic_refs(*list, &ref, &proxy);
+                prev = (proxy == NULL) ? ref : proxy;
                 if (prev == NULL)
                     insert_head(result, list);
                 else
@@ -672,8 +679,13 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
         /* attempt to return an existing weak reference if it exists */
         result = proxy;
     if (result != NULL)
-        Py_XINCREF(result);
+        Py_INCREF(result);
     else {
+        /* Note: new_weakref() can trigger cyclic GC, so the weakref
+           list on ob can be mutated.  This means that the ref and
+           proxy pointers we got back earlier may have been collected,
+           so we need to compute these values again before we use
+           them. */
         result = new_weakref(ob, callback);
         if (result != NULL) {
             PyWeakReference *prev;
@@ -682,6 +694,7 @@ PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
                 result->ob_type = &_PyWeakref_CallableProxyType;
             else
                 result->ob_type = &_PyWeakref_ProxyType;
+            get_basic_refs(*list, &ref, &proxy);
             if (callback == NULL)
                 prev = ref;
             else