[pypy-commit] pypy cpyext-gc-cycle: Implemented support for weakrefs in rawrefcount dot tests

stevie_92 pypy.commits at gmail.com
Sat Apr 20 05:39:30 EDT 2019


Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96523:b14c3b4ba179
Date: 2019-04-20 11:38 +0200
http://bitbucket.org/pypy/pypy/changeset/b14c3b4ba179/

Log:	Implemented support for weakrefs in rawrefcount dot tests Added
	first dot test with weakrefs Added simple cpython-only-cycle dot
	test

diff --git a/rpython/memory/gc/test/dot/free_cpython_self.dot b/rpython/memory/gc/test/dot/free_cpython_simple.dot
copy from rpython/memory/gc/test/dot/free_cpython_self.dot
copy to rpython/memory/gc/test/dot/free_cpython_simple.dot
--- a/rpython/memory/gc/test/dot/free_cpython_self.dot
+++ b/rpython/memory/gc/test/dot/free_cpython_simple.dot
@@ -1,4 +1,6 @@
 digraph G {
     "a" [type=C, alive=n];
-    "a" -> "a";
+    "b" [type=C, alive=n];
+    "a" -> "b";
+    "b" -> "a";
 }
diff --git a/rpython/memory/gc/test/dot/free_cpython_self.dot b/rpython/memory/gc/test/dot/free_cpython_weakref_simple.dot
copy from rpython/memory/gc/test/dot/free_cpython_self.dot
copy to rpython/memory/gc/test/dot/free_cpython_weakref_simple.dot
--- a/rpython/memory/gc/test/dot/free_cpython_self.dot
+++ b/rpython/memory/gc/test/dot/free_cpython_weakref_simple.dot
@@ -1,4 +1,7 @@
 digraph G {
     "a" [type=C, alive=n];
-    "a" -> "a";
+    "b" [type=C, alive=n];
+    "a" -> "b";
+    "b" -> "a";
+    "a" -> "b" [weakref=y, callback=y, clear_callback=y];
 }
diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py
--- a/rpython/memory/gc/test/test_rawrefcount.py
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -30,21 +30,34 @@
         self.gcobjs = []
         self.pyobjs = []
         self.pyobj_refs = []
+        self.pyobj_weakrefs = []
         self.pyobj_finalizer = {}
         self.pyobj_finalized = {}
         self.pyobj_resurrect = {}
         self.pyobj_delete = {}
+        self.is_pygc = []
 
         def rawrefcount_tp_traverse(obj, callback, args):
             refs = self.pyobj_refs[self.pyobjs.index(obj)]
+            weakrefs = self.pyobj_weakrefs[self.pyobjs.index(obj)]
             for ref in refs:
                 callback(ref, args)
+            for weakref in weakrefs:
+                callback(weakref.r, args)
 
         def rawrefcount_gc_as_pyobj(gc):
-            return self.pyobjs[self.gcobjs.index(gc)]
+            index = self.gcobjs.index(gc)
+            if self.is_pygc[index]:
+                return self.pyobjs[index]
+            else:
+                assert False
 
         def rawrefcount_pyobj_as_gc(pyobj):
-            return self.gcobjs[self.pyobjs.index(pyobj)]
+            index = self.pyobjs.index(pyobj)
+            if self.is_pygc[index]:
+                return self.gcobjs[index]
+            else:
+                return lltype.nullptr(PYOBJ_GC_HDR)
 
         def rawrefcount_finalizer_type(gc):
             pyobj = self.pyobjs[self.gcobjs.index(gc)]
@@ -56,6 +69,15 @@
             else:
                 return RAWREFCOUNT_FINALIZER_NONE
 
+        def rawrefcount_clear_wr(gc):
+            cleared = False
+            for weakrefs in self.pyobj_weakrefs:
+                for weakref in weakrefs:
+                    if gc._obj.container == weakref.p._obj:
+                        weakref.callback_cleared = True
+                        cleared = True
+            assert cleared
+
         self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw',
                                         immortal=True)
         self.pyobj_list.c_gc_next = self.pyobj_list
@@ -65,7 +87,8 @@
                                  llmemory.cast_ptr_to_adr(self.pyobj_list),
                                  rawrefcount_gc_as_pyobj,
                                  rawrefcount_pyobj_as_gc,
-                                 rawrefcount_finalizer_type)
+                                 rawrefcount_finalizer_type,
+                                 rawrefcount_clear_wr)
 
     def _collect(self, major, expected_trigger=0):
         if major:
@@ -83,6 +106,11 @@
         refs.append(pyobj_to)
         pyobj_to.c_ob_refcnt += 1
 
+    def _rawrefcount_addweakref(self, pyobj_from, weakref):
+        refs = self.pyobj_weakrefs[self.pyobjs.index(pyobj_from)]
+        refs.append(weakref)
+        weakref.r.c_ob_refcnt += 1
+
     def _rawrefcount_add_resurrect(self, pyobj_source, pyobj_target):
         refs = self.pyobj_resurrect[self.pyobjs.index(pyobj_source)] = []
         refs.append(pyobj_target)
@@ -109,23 +137,26 @@
 
         return p1, p1ref, check_alive
 
-    def _rawrefcount_pyobj(self, create_immortal=False):
+    def _rawrefcount_pyobj(self, create_immortal=False, is_gc=True):
         r1 = lltype.malloc(PYOBJ_HDR, flavor='raw',
                            immortal=create_immortal)
         r1.c_ob_refcnt = 0
         r1.c_ob_pypy_link = 0
         r1addr = llmemory.cast_ptr_to_adr(r1)
 
-        r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
-                             immortal=True)
-        r1gc.c_gc_next = self.pyobj_list
-        r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
-        r1gc.c_gc_prev.c_gc_next = r1gc
-        self.pyobj_list.c_gc_prev = r1gc
+        if is_gc:
+            r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
+                                 immortal=True)
+            r1gc.c_gc_next = self.pyobj_list
+            r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
+            r1gc.c_gc_prev.c_gc_next = r1gc
+            self.pyobj_list.c_gc_prev = r1gc
+            self.gcobjs.append(r1gc)
 
-        self.gcobjs.append(r1gc)
         self.pyobjs.append(r1)
+        self.is_pygc.append(is_gc)
         self.pyobj_refs.append([])
+        self.pyobj_weakrefs.append([])
 
         def check_alive(extra_refcount):
             assert r1.c_ob_refcnt == extra_refcount
@@ -134,7 +165,7 @@
 
     def _rawrefcount_pair(self, intval, is_light=False, is_pyobj=False,
                           create_old=False, create_immortal=False,
-                          rooted=False, force_external=False):
+                          rooted=False, force_external=False, is_gc=True):
         if is_light:
             rc = REFCNT_FROM_PYPY_LIGHT
         else:
@@ -166,16 +197,19 @@
         r1.c_ob_pypy_link = 0
         r1addr = llmemory.cast_ptr_to_adr(r1)
 
-        r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
-                             immortal=True)
-        r1gc.c_gc_next = self.pyobj_list
-        r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
-        r1gc.c_gc_prev.c_gc_next = r1gc
-        self.pyobj_list.c_gc_prev = r1gc
+        if is_gc:
+            r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
+                                 immortal=True)
+            r1gc.c_gc_next = self.pyobj_list
+            r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
+            r1gc.c_gc_prev.c_gc_next = r1gc
+            self.pyobj_list.c_gc_prev = r1gc
+            self.gcobjs.append(r1gc)
 
-        self.gcobjs.append(r1gc)
         self.pyobjs.append(r1)
+        self.is_pygc.append(is_gc)
         self.pyobj_refs.append([])
+        self.pyobj_weakrefs.append([])
 
         if is_pyobj:
             assert not is_light
@@ -457,6 +491,20 @@
                 self.delete = delete
                 self.garbage = garbage
 
+        class WeakrefNode(BorderNode):
+            def __init__(self, p, pref, r, raddr, check_alive, info, r_dest,
+                         callback, clear_callback):
+                self.p = p
+                self.pref = pref
+                self.r = r
+                self.raddr = raddr
+                self.check_alive = check_alive
+                self.info = info
+                self.r_dest = r_dest
+                self.callback = callback
+                self.clear_callback = clear_callback
+                self.callback_cleared = False
+
         path = os.path.join(self.dot_dir, file)
         g = pydot.graph_from_dot_file(path)[0]
         nodes = {}
@@ -501,17 +549,33 @@
         for e in g.get_edges():
             source = nodes[e.get_source()]
             dest = nodes[e.get_destination()]
+            attr = e.obj_dict['attributes']
+            weakref = attr['weakref'] == "y" if 'weakref' in attr else False
+            callback = attr['callback'] == "y" if 'callback' in attr else False
+            clear_callback = attr['clear_callback'] == "y" \
+                if 'clear_callback' in attr else False
             if source.info.type == "C" or dest.info.type == "C":
-                self._rawrefcount_addref(source.r, dest.r)
-                if source.info.alive:
-                    dest.info.ext_refcnt += 1
+                if weakref:
+                    # only weakrefs from C objects supported in tests
+                    assert source.info.type == "C"
+                    p, pref, r, raddr, check_alive = \
+                        self._rawrefcount_pair(42 + i, rooted=False,
+                                               create_old=True, is_gc=False)
+                    weakref = WeakrefNode(p, pref, r, raddr, check_alive, info,
+                                          dest.r, callback, clear_callback)
+                    self._rawrefcount_addweakref(source.r, weakref)
+                    i += 1
+                else:
+                    self._rawrefcount_addref(source.r, dest.r)
+                    if source.info.alive:
+                        dest.info.ext_refcnt += 1
             elif source.info.type == "P" or dest.info.type == "P":
                 if llmemory.cast_ptr_to_adr(source.p.next) == llmemory.NULL:
                     source.p.next = dest.p
                 elif llmemory.cast_ptr_to_adr(source.p.prev) == llmemory.NULL:
                     source.p.prev = dest.p
                 else:
-                    assert False  # only 2 refs supported from pypy obj
+                    assert False # only 2 refs supported from pypy obj in tests
 
         # add finalizers
         for name in nodes:
@@ -538,10 +602,17 @@
         for e in g.get_edges():
             source = nodes[e.get_source()]
             dest = nodes[e.get_destination()]
+            attr = e.obj_dict['attributes']
+            weakref = attr['weakref'] == "y" if 'weakref' in attr else False
             if source.info.type == "C" or dest.info.type == "C":
                 if not dests_by_source.has_key(source):
                     dests_by_source[source] = []
-                dests_by_source[source].append(dest.r)
+                if weakref:
+                    wrs = self.pyobj_weakrefs[self.pyobjs.index(source.r)]
+                    # currently only one weakref supported
+                    dests_by_source[source].append(wrs[0].r)
+                else:
+                    dests_by_source[source].append(dest.r)
         for source in dests_by_source:
             dests_target = dests_by_source[source]
             def append(pyobj, ignore):
@@ -613,8 +684,12 @@
 
                 def clear(pyobj_to, pyobj_from):
                     refs = self.pyobj_refs[self.pyobjs.index(pyobj_from)]
-                    refs.remove(pyobj_to)
-                    decref(pyobj_to, None)
+                    weakrefs = self.pyobj_weakrefs[self.pyobjs.index(pyobj_from)]
+                    if pyobj_to in refs:
+                        refs.remove(pyobj_to)
+                        decref(pyobj_to, None)
+                    else:
+                        pass # weakref
 
                 self.gc.rrc_tp_traverse(pyobj, clear, pyobj)
 
@@ -667,6 +742,14 @@
                 else:
                     py.test.raises(RuntimeError, "n.r.c_ob_refcnt")  # dead
 
+        # check if all callbacks from weakrefs from cyclic garbage structures,
+        # which should not be called because they could ressurrect dead
+        # objects, have been cleared and no other callbacks were cleared; see:
+        # https://github.com/python/cpython/blob/master/Modules/gc_weakref.txt
+        for weakrefs in self.pyobj_weakrefs:
+            for weakref in weakrefs:
+                assert weakref.callback_cleared == weakref.clear_callback
+
         # check if unreachable objects in cyclic structures with legacy
         # finalizers and all otherwise unreachable objects reachable from them
         # have been added to the garbage list


More information about the pypy-commit mailing list