[pypy-commit] pypy cpyext-gc-cycle: Implemented tests for gc.garbage list and adapted interface

stevie_92 pypy.commits at gmail.com
Fri Mar 1 08:14:13 EST 2019


Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96197:15bed0a0192d
Date: 2019-03-01 14:13 +0100
http://bitbucket.org/pypy/pypy/changeset/15bed0a0192d/

Log:	Implemented tests for gc.garbage list and adapted interface

diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -77,6 +77,7 @@
         space = self.space
         if not self.space.config.translating:
             def dealloc_trigger():
+                from pypy.interpreter.baseobjspace import W_Root
                 from pypy.module.cpyext.pyobject import PyObject, decref, \
                     incref, cts, finalize, from_ref
                 w_list = space.getattr(space.builtin_modules['gc'],
@@ -116,10 +117,15 @@
                             llmemory.cast_ptr_to_adr(head)):
                         rawrefcount.cyclic_garbage_remove()
                 while True:
-                    py_obj = rawrefcount.next_garbage(PyObject)
+                    w_obj = rawrefcount.next_garbage_pypy(W_Root)
                     if not py_obj:
                         break
-                    w_obj = from_ref(space, py_obj)
+                    w_list.append(w_obj)
+                while True:
+                    w_pyobj = rawrefcount.next_garbage_pyobj(PyObject)
+                    if not py_obj:
+                        break
+                    w_obj = from_ref(space, w_pyobj)
                     w_list.append(w_obj)
                 print 'dealloc_trigger DONE'
                 return "RETRY"
@@ -282,6 +288,7 @@
 
 
 def _rawrefcount_perform(space):
+    from pypy.interpreter.baseobjspace import W_Root
     from pypy.module.cpyext.pyobject import (PyObject, incref, decref,
                                              finalize, from_ref)
 
@@ -315,10 +322,15 @@
             rawrefcount.cyclic_garbage_remove()
 
     while True:
-        py_obj = rawrefcount.next_garbage(PyObject)
+        w_obj = rawrefcount.next_garbage_pypy(W_Root)
         if not py_obj:
             break
-        w_obj = from_ref(space, py_obj)
+        w_list.append(w_obj)
+    while True:
+        w_pyobj = rawrefcount.next_garbage_pyobj(PyObject)
+        if not py_obj:
+            break
+        w_obj = from_ref(space, w_pyobj)
         w_list.append(w_obj)
 
 class PyObjDeallocAction(executioncontext.AsyncAction):
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -3217,7 +3217,22 @@
         gchdr.c_gc_next = next
         next.c_gc_prev = gchdr
 
-    def rawrefcount_next_garbage(self):
+    def rawrefcount_next_garbage_pypy(self):
+        # return the next pypy object which is only reachable from garbage
+        # pyobjects, probably need two more colors for this. one for marking
+        # so that they stay alive during sweep, one for marking, so they do not
+        # get returned here again
+        return lltype.nullptr(llmemory.GCREF.TO)
+
+    def rawrefcount_next_garbage_pyobj(self):
+        # implement st objects in this list still remain in the set of
+        # all pyobjs, because references could still change and cause them
+        # to live again. also keep in mind, that state will create references
+        # to pyobjs in this list and might increment the refcount.
+
+        # use create_link_pyobj on the result to create gc objects for pyobjects
+        #p = W_Root(42)
+        #p.pyobj = ob
         return llmemory.NULL
 
 
diff --git a/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot b/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/test/dot/garbage_cpython_simple_1.dot
@@ -0,0 +1,6 @@
+digraph G {
+    "a" [type=C, alive=y, garbage=y, finalizer=legacy];
+    "b" [type=C, alive=y, garbage=y];
+    "a" -> "b";
+    "b" -> "a";
+}
\ No newline at end of file
diff --git a/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot b/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/test/dot/garbage_cross_simple_1.dot
@@ -0,0 +1,10 @@
+digraph G {
+    "a" [type=C, alive=y, garbage=y, finalizer=legacy];
+    "b" [type=B, alive=y, garbage=y];
+    "c" [type=P, alive=y, garbage=y];
+    "d" [type=P, alive=y, rooted=y];
+    "a" -> "b";
+    "b" -> "a";
+    "b" -> "c";
+    "c" -> "d";
+}
\ No newline at end of file
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
@@ -1,17 +1,17 @@
 import os, py
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
-from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
+from rpython.memory.gc.incminimark import IncrementalMiniMarkGC as IncMiniMark
 from rpython.memory.gc.test.test_direct import BaseDirectGCTest
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
-from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
-PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
-PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
-RAWREFCOUNT_VISIT = IncrementalMiniMarkGC.RAWREFCOUNT_VISIT
-PYOBJ_GC_HDR = IncrementalMiniMarkGC.PYOBJ_GC_HDR
-PYOBJ_GC_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_GC_HDR_PTR
-RAWREFCOUNT_FINALIZER_MODERN = \
-    IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_MODERN
-RAWREFCOUNT_FINALIZER_NONE = IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_NONE
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT
+
+PYOBJ_HDR = IncMiniMark.PYOBJ_HDR
+PYOBJ_HDR_PTR = IncMiniMark.PYOBJ_HDR_PTR
+RAWREFCOUNT_VISIT = IncMiniMark.RAWREFCOUNT_VISIT
+PYOBJ_GC_HDR = IncMiniMark.PYOBJ_GC_HDR
+PYOBJ_GC_HDR_PTR = IncMiniMark.PYOBJ_GC_HDR_PTR
+RAWREFCOUNT_FINALIZER_MODERN = IncMiniMark.RAWREFCOUNT_FINALIZER_MODERN
+RAWREFCOUNT_FINALIZER_LEGACY = IncMiniMark.RAWREFCOUNT_FINALIZER_LEGACY
+RAWREFCOUNT_FINALIZER_NONE = IncMiniMark.RAWREFCOUNT_FINALIZER_NONE
 
 S = lltype.GcForwardReference()
 S.become(lltype.GcStruct('S',
@@ -21,7 +21,7 @@
 
 
 class TestRawRefCount(BaseDirectGCTest):
-    GCClass = IncrementalMiniMarkGC
+    GCClass = IncMiniMark
 
     def setup_method(self, method):
         BaseDirectGCTest.setup_method(self, method)
@@ -448,13 +448,14 @@
 
         class NodeInfo:
             def __init__(self, type, alive, ext_refcnt, finalizer, resurrect,
-                         delete):
+                         delete, garbage):
                 self.type = type
                 self.alive = alive
                 self.ext_refcnt = ext_refcnt
                 self.finalizer = finalizer
                 self.resurrect = resurrect
                 self.delete = delete
+                self.garbage = garbage
 
         path = os.path.join(self.dot_dir, file)
         g = pydot.graph_from_dot_file(path)[0]
@@ -475,8 +476,9 @@
                 finalizers = True
             resurrect = attr['resurrect'] if 'resurrect' in attr else None
             delete = attr['delete'] if 'delete' in attr else None
+            garbage = True if 'garbage' in attr else False
             info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect,
-                            delete)
+                            delete, garbage)
             if type == "C":
                 r, raddr, check_alive = self._rawrefcount_pyobj()
                 r.c_ob_refcnt += ext_refcnt
@@ -526,6 +528,8 @@
                         nodes[resurrect].info.ext_refcnt += 1
                     if delete is not None:
                         self._rawrefcount_add_delete(n.r, nodes[delete].r)
+                elif n.info.finalizer == "legacy":
+                    self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_LEGACY
                 else:
                     self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_NONE
 
@@ -545,6 +549,8 @@
             self.gc.rrc_tp_traverse(source.r, append, None)
             assert len(dests_target) == 0
 
+        garbage_pypy = []
+        garbage_pyobj = []
         def cleanup():
             # do cleanup after collection (clear all dead pyobjects)
             def finalize_modern(pyobj):
@@ -621,6 +627,13 @@
                     self.gc.rawrefcount_cyclic_garbage_remove()
                     next_dead = self.gc.rawrefcount_cyclic_garbage_head()
 
+            next = self.gc.rawrefcount_next_garbage_pypy()
+            while next <> lltype.nullptr(llmemory.GCREF.TO):
+                garbage_pypy.append(next)
+            next = self.gc.rawrefcount_next_garbage_pyobj()
+            while next <> llmemory.NULL:
+                garbage_pyobj.append(next)
+
         # do a collection to find cyclic isolates and clean them, if there are
         # no finalizers
         self.gc.collect()
@@ -649,3 +662,16 @@
                     py.test.raises(RuntimeError, "n.p.x")  # dead
                 else:
                     py.test.raises(RuntimeError, "n.r.c_ob_refcnt")  # dead
+
+        # 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
+        for name in nodes:
+            n = nodes[name]
+            if n.info.alive:
+                if n.info.type == "C":
+                    assert n.info.garbage != (n.raddr not in garbage_pyobj)
+                else:
+                    assert n.info.garbage != (n.pref not in garbage_pypy)
+            else:
+                assert not n.info.garbage
\ No newline at end of file
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -518,8 +518,11 @@
             self.rawrefcount_cyclic_garbage_remove_ptr = getfn(
                 GCClass.rawrefcount_cyclic_garbage_remove, [s_gc],
                 annmodel.s_None, inline = True)
-            self.rawrefcount_next_garbage_ptr = getfn(
-                GCClass.rawrefcount_next_garbage, [s_gc],
+            self.rawrefcount_next_garbage_pypy_ptr = getfn(
+                GCClass.rawrefcount_next_garbage_pypy, [s_gc],
+                s_gcref, inline = True)
+            self.rawrefcount_next_garbage_pyobj_ptr = getfn(
+                GCClass.rawrefcount_next_garbage_pyobj, [s_gc],
                 SomeAddress(), inline = True)
 
         if GCClass.can_usually_pin_objects:
@@ -1429,10 +1432,16 @@
                   [self.rawrefcount_cyclic_garbage_remove_ptr,
                    self.c_const_gc])
 
-    def gct_gc_rawrefcount_next_garbage(self, hop):
+    def gct_gc_rawrefcount_next_garbage_pypy(self, hop):
+        assert hop.spaceop.result.concretetype == llmemory.GCREF
+        hop.genop("direct_call",
+                  [self.rawrefcount_next_garbage_pypy_ptr, self.c_const_gc],
+                  resultvar=hop.spaceop.result)
+
+    def gct_gc_rawrefcount_next_garbage_pyobj(self, hop):
         assert hop.spaceop.result.concretetype == llmemory.Address
         hop.genop("direct_call",
-                  [self.rawrefcount_next_garbage_ptr, self.c_const_gc],
+                  [self.rawrefcount_next_garbage_pyobj_ptr, self.c_const_gc],
                   resultvar=hop.spaceop.result)
 
     def _set_into_gc_array_part(self, op):
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -146,7 +146,11 @@
     return lltype.nullptr(OB_PTR_TYPE.TO)
 
 @not_rpython
-def next_garbage(OB_PTR_TYPE):
+def next_garbage_pypy(Class):
+    return lltype.nullptr(llmemory.GCREF.TO)
+
+ at not_rpython
+def next_garbage_pyobj(OB_PTR_TYPE):
     return lltype.nullptr(OB_PTR_TYPE.TO)
 
 @not_rpython
@@ -372,7 +376,7 @@
 
 class Entry(ExtRegistryEntry):
     _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate,
-               next_garbage)
+               next_garbage_pyobj)
 
     def compute_result_annotation(self, s_OB_PTR_TYPE):
         from rpython.rtyper.llannotation import lltype_to_annotation
@@ -386,13 +390,28 @@
             name = 'gc_rawrefcount_cyclic_garbage_head'
         elif self.instance is next_cyclic_isolate:
             name = 'gc_rawrefcount_next_cyclic_isolate'
-        elif self.instance is next_garbage:
-            name = 'gc_rawrefcount_next_garbage'
+        elif self.instance is next_garbage_pyobj:
+            name = 'gc_rawrefcount_next_garbage_pyobj'
         hop.exception_cannot_occur()
         v_ob = hop.genop(name, [], resulttype = llmemory.Address)
         return _spec_ob(hop, v_ob)
 
 class Entry(ExtRegistryEntry):
+    _about_ = next_garbage_pypy
+
+    def compute_result_annotation(self, s_Class):
+        from rpython.annotator import model as annmodel
+        assert s_Class.is_constant()
+        classdef = self.bookkeeper.getuniqueclassdef(s_Class.const)
+        return annmodel.SomeInstance(classdef, can_be_None=True)
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        v_p = hop.genop('gc_rawrefcount_next_garbage_pypy', [],
+                        resulttype = llmemory.GCREF)
+        return _spec_p(hop, v_p)
+
+class Entry(ExtRegistryEntry):
     _about_ = cyclic_garbage_remove
 
     def compute_result_annotation(self):
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -995,8 +995,10 @@
     def op_gc_rawrefcount_cyclic_garbage_remove(self, *args):
         raise NotImplementedError("gc_rawrefcount_cyclic_garbage_remove")
 
-    def op_gc_rawrefcount_next_garbage(self, *args):
-        raise NotImplementedError("gc_rawrefcount_next_garbage")
+    def op_gc_rawrefcount_next_garbage_pypy(self, *args):
+        raise NotImplementedError("gc_rawrefcount_next_garbage_pypy")
+    def op_gc_rawrefcount_next_garbage_pyobj(self, *args):
+        raise NotImplementedError("gc_rawrefcount_next_garbage_pyobj")
 
     def op_do_malloc_fixedsize(self):
         raise NotImplementedError("do_malloc_fixedsize")
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -532,7 +532,8 @@
     'gc_rawrefcount_next_cyclic_isolate':   LLOp(),
     'gc_rawrefcount_cyclic_garbage_head':   LLOp(sideeffects=False),
     'gc_rawrefcount_cyclic_garbage_remove': LLOp(),
-    'gc_rawrefcount_next_garbage':          LLOp(),
+    'gc_rawrefcount_next_garbage_pypy':     LLOp(),
+    'gc_rawrefcount_next_garbage_pyobj':    LLOp(),
 
     'gc_move_out_of_nursery':           LLOp(),
 


More information about the pypy-commit mailing list