Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r95662:fdd107ae6257
Date: 2019-01-16 18:26 +0100
http://bitbucket.org/pypy/pypy/changeset/fdd107ae6257/

Log:    Implemented first version of CPython-style cycle detection Fixed dot
        tests

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
@@ -3031,6 +3031,8 @@
             self.rrc_dealloc_pending = self.AddressStack()
             self.rrc_tp_traverse = tp_traverse
             self.rrc_pyobj_list = self._pygchdr(pyobj_list)
+            self.rrc_pyobj_old_list = lltype.malloc(
+                self.PYOBJ_GC_HDR, flavor='raw', immortal=True)
             self.rrc_gc_as_pyobj = gc_as_pyobj
             self.rrc_pyobj_as_gc = pyobj_as_gc
             self.rrc_enabled = True
@@ -3203,14 +3205,13 @@
     _rrc_free._always_inline_ = True
 
     def rrc_major_collection_trace(self):
+        self._rrc_collect_rawrefcount_roots()
+        self._rrc_mark_rawrefcount()
         self.rrc_p_list_old.foreach(self._rrc_major_trace, None)
 
     def _rrc_major_trace(self, pyobject, ignore):
-        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
-        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
-        #
-        rc = self._pyobj(pyobject).c_ob_refcnt
-        if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+        rc = self.rrc_pyobj_as_gc(self._pyobj(pyobject)).c_gc_refs
+        if rc == 0:
             pass     # the corresponding object may die
         else:
             # force the corresponding object to be alive
@@ -3220,6 +3221,9 @@
             self.visit_all_objects()
 
     def rrc_major_collection_free(self):
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+        #
         ll_assert(self.rrc_p_dict_nurs.length() == 0, "p_dict_nurs not empty 
2")
         length_estimate = self.rrc_p_dict.length()
         self.rrc_p_dict.delete()
@@ -3238,6 +3242,20 @@
                                                             no_o_dict)
         self.rrc_o_list_old.delete()
         self.rrc_o_list_old = new_o_list
+        # free all dead refcounted objects, in unreachable cycles
+        pygchdr = self.rrc_pyobj_old_list.c_gc_next
+        while pygchdr <> self.rrc_pyobj_old_list:
+            assert pygchdr.c_gc_refs == 0
+            pyobj = self.rrc_gc_as_pyobj(pygchdr)
+            if pyobj.c_ob_refcnt >= REFCNT_FROM_PYPY_LIGHT:
+                lltype.free(pyobj, flavor='raw')
+            elif pyobj.c_ob_refcnt >= REFCNT_FROM_PYPY:
+                pyobject = llmemory.cast_ptr_to_adr(pyobj)
+                pyobj.c_ob_refcnt = 1
+                self.rrc_dealloc_pending.append(pyobject)
+            else:
+                lltype.free(pyobj, flavor='raw')
+            pygchdr = pygchdr.c_gc_next
 
     def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
         # The pyobject survives if the corresponding obj survives.
@@ -3251,7 +3269,110 @@
             if surviving_dict:
                 surviving_dict.insertclean(obj, pyobject)
         else:
-            self._rrc_free(pyobject)
+            # The pyobject is freed later, if it is in old list, so
+            # just unlink here.
+            self._pyobj(pyobject).c_ob_pypy_link = 0
+
+    def _rrc_collect_rawrefcount_roots(self):
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+        #
+        # Initialize the cyclic refcount with the real refcount.
+        pygchdr = self.rrc_pyobj_list.c_gc_next
+        while pygchdr <> self.rrc_pyobj_list:
+            pygchdr.c_gc_refs = self.rrc_gc_as_pyobj(pygchdr).c_ob_refcnt
+            if pygchdr.c_gc_refs >= REFCNT_FROM_PYPY_LIGHT:
+                pygchdr.c_gc_refs -= REFCNT_FROM_PYPY_LIGHT
+            elif pygchdr.c_gc_refs >= REFCNT_FROM_PYPY:
+                pygchdr.c_gc_refs -= REFCNT_FROM_PYPY
+            pygchdr = pygchdr.c_gc_next
+
+        # For every object in this set, if it is marked, add 1 as a real
+        # refcount
+        self.rrc_p_list_old.foreach(self._rrc_obj_fix_refcnt, None)
+
+        # Subtract all internal refcounts from the cyclic refcount
+        # of rawrefcounted objects
+        pygchdr = self.rrc_pyobj_list.c_gc_next
+        while pygchdr <> self.rrc_pyobj_list:
+            pyobj = self.rrc_gc_as_pyobj(pygchdr)
+            self._rrc_visit_pyobj = self._rrc_subtract_internal_refcnt
+            self._rrc_traverse(pyobj)
+            pygchdr = pygchdr.c_gc_next
+
+        # now all rawrefcounted roots or live border objects have a
+        # refcount > 0
+
+    def _rrc_subtract_internal_refcnt(self, pyobj):
+        pygchdr = self.rrc_pyobj_as_gc(pyobj)
+        pygchdr.c_gc_refs -= 1
+
+    def _rrc_obj_fix_refcnt(self, pyobject, ignore):
+        intobj = self._pyobj(pyobject).c_ob_pypy_link
+        obj = llmemory.cast_int_to_adr(intobj)
+        gchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
+        if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
+            gchdr.c_gc_refs += 1
+
+    def _rrc_mark_rawrefcount(self):
+        if self.rrc_pyobj_list.c_gc_next == self.rrc_pyobj_list:
+            self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_old_list
+            self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_old_list
+            return
+        # as long as new objects with cyclic a refcount > 0 or alive border
+        # objects are found, increment the refcount of all referenced objects
+        # of those newly found objects
+        self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_list.c_gc_next
+        self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_list.c_gc_prev
+        self.rrc_pyobj_old_list.c_gc_next.c_gc_prev = self.rrc_pyobj_old_list
+        self.rrc_pyobj_old_list.c_gc_prev.c_gc_next = self.rrc_pyobj_old_list
+        self.rrc_pyobj_list.c_gc_next = self.rrc_pyobj_list
+        self.rrc_pyobj_list.c_gc_prev = self.rrc_pyobj_list
+        found_alive = True
+        #
+        while found_alive:
+            found_alive = False
+            gchdr = self.rrc_pyobj_old_list.c_gc_next
+            while gchdr <> self.rrc_pyobj_old_list:
+                next_old = gchdr.c_gc_next
+                alive = gchdr.c_gc_refs > 0
+                pyobj = self.rrc_gc_as_pyobj(gchdr)
+                obj = None
+                if pyobj.c_ob_pypy_link <> 0:
+                    intobj = pyobj.c_ob_pypy_link
+                    obj = llmemory.cast_int_to_adr(intobj)
+                    if not alive and self.header(obj).tid & (
+                            GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
+                        # add fake refcount, to mark it as live
+                        gchdr.c_gc_refs += 1
+                        alive = True
+                if alive:
+                    # remove from old list
+                    next = gchdr.c_gc_next
+                    next.c_gc_prev = gchdr.c_gc_prev
+                    gchdr.c_gc_prev.c_gc_next = next
+                    # add to new list
+                    next = self.rrc_pyobj_list.c_gc_next
+                    self.rrc_pyobj_list.c_gc_next = gchdr
+                    gchdr.c_gc_prev = self.rrc_pyobj_list
+                    gchdr.c_gc_next = next
+                    next.c_gc_prev = gchdr
+                    # increment refcounts
+                    self._rrc_visit_pyobj = self._rrc_increment_refcnt
+                    self._rrc_traverse(pyobj)
+                    # mark recursively, if it is a pypyobj
+                    if not obj is None:
+                        self.objects_to_trace.append(obj)
+                        self.visit_all_objects()
+                    found_alive = True
+                gchdr = next_old
+        #
+        # now all rawrefcounted objects, which are alive, have a cyclic
+        # refcount > 0 or are marked
+
+    def _rrc_increment_refcnt(self, pyobj):
+        pygchdr = self.rrc_pyobj_as_gc(pyobj)
+        pygchdr.c_gc_refs += 1
 
     def _rrc_visit(pyobj, self_ptr):
         from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
@@ -3261,12 +3382,11 @@
         self._rrc_visit_pyobj(pyobj)
         return rffi.cast(rffi.INT_real, 0)
 
-    def _rrc_traverse(self, pyobject):
+    def _rrc_traverse(self, pyobj):
         from rpython.rlib.objectmodel import we_are_translated
         from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
                                                 llhelper)
         #
-        pyobj = self._pyobj(pyobject)
         if we_are_translated():
             callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
                                     IncrementalMiniMarkGC._rrc_visit)
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
@@ -31,7 +31,7 @@
         def rawrefcount_tp_traverse(obj, callback, args):
             refs = self.pyobj_refs[self.pyobjs.index(obj)]
             for ref in refs:
-                callback(self.gc, ref)
+                callback(ref)
 
         def rawrefcount_gc_as_pyobj(gc):
             return self.pyobjs[self.gcobjs.index(gc)]
@@ -439,8 +439,7 @@
             info = NodeInfo(type, alive, ext_refcnt)
             if type == "C":
                 r, raddr, check_alive = self._rawrefcount_pyobj()
-                if ext_refcnt > 0:
-                    r.c_ob_refcnt = ext_refcnt
+                r.c_ob_refcnt += ext_refcnt
                 nodes[name] = CPythonNode(r, raddr, check_alive, info)
             elif type == "P":
                 p, pref, check_alive = \
@@ -452,8 +451,7 @@
                 p, pref, r, raddr, check_alive =\
                     self._rawrefcount_pair(42 + i, rooted=rooted,
                                            create_old=True)
-                if ext_refcnt > 0:
-                    r.c_ob_refcnt = ext_refcnt
+                r.c_ob_refcnt += ext_refcnt
                 nodes[name] = BorderNode(p, pref, r, raddr, check_alive, info)
                 i += 1
 
@@ -483,15 +481,23 @@
                 dests_by_source[source].append(dest.r)
         for source in dests_by_source:
             dests_target = dests_by_source[source]
-            def append(self, pyobj):
+            def append(pyobj):
                 dests_target.remove(pyobj)
             self.gc._rrc_visit_pyobj = append
-            self.gc._rrc_traverse(source.raddr)
+            self.gc._rrc_traverse(source.r)
             assert len(dests_target) == 0
 
         # do collection
         self.gc.collect()
 
+        # simply free all pending deallocations, we don't care about the
+        # side effects
+        next_dead = self.gc.rawrefcount_next_dead()
+        while next_dead <>  llmemory.NULL:
+            pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR)
+            lltype.free(pyobj, flavor='raw')
+            next_dead = self.gc.rawrefcount_next_dead()
+
         # check livelihood of objects, according to graph
         for name in nodes:
             n = nodes[name]
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to