Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r96199:f0fce8ff6d08
Date: 2019-03-02 11:15 +0100
http://bitbucket.org/pypy/pypy/changeset/f0fce8ff6d08/

Log:    Implemented additional rawrefcount states and added a gc-header flag
        Extended 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
@@ -113,6 +113,7 @@
                     if adr_int == llmemory.cast_adr_to_int(
                             llmemory.cast_ptr_to_adr(head)):
                         rawrefcount.cyclic_garbage_remove()
+                rawrefcount.begin_garbage()
                 w_list = space.newlist([])
                 while True:
                     w_obj = rawrefcount.next_garbage_pypy(W_Root)
@@ -130,6 +131,7 @@
                     last_py_obj = w_pyobj
                 space.setattr(space.builtin_modules['gc'],
                               space.newtext('garbage'), w_list)
+                rawrefcount.end_garbage()
                 print 'dealloc_trigger DONE'
                 return "RETRY"
             def tp_traverse(pyobj_ptr, callback, args):
@@ -321,6 +323,7 @@
         if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)):
             rawrefcount.cyclic_garbage_remove()
 
+    rawrefcount.begin_garbage()
     w_list = space.newlist([])
     while True:
         w_obj = rawrefcount.next_garbage_pypy(W_Root)
@@ -337,6 +340,7 @@
         last_py_obj = w_pyobj
     space.setattr(space.builtin_modules['gc'], space.newtext('garbage'),
                   w_list)
+    rawrefcount.end_garbage()
 
 class PyObjDeallocAction(executioncontext.AsyncAction):
     """An action that invokes _Py_Dealloc() on the dying PyObjects.
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
@@ -163,7 +163,15 @@
 # It does not need an additional copy in trace out
 GCFLAG_SHADOW_INITIALIZED   = first_gcflag << 11
 
-_GCFLAG_FIRST_UNUSED = first_gcflag << 12    # the first unused bit
+# Objects referenced only from legacy rawrefcount finalizers which have
+# not been added to gc.garbage have this flag set. It is set during the
+# rawrefcount marking phase and removed once the object is returned to
+# the caller, who is adding it to the gc.garbage list. During the next
+# collection cycle this process is repeated, as the set of objects might
+# have changed.
+GCFLAG_GARBAGE = first_gcflag << 12
+
+_GCFLAG_FIRST_UNUSED = first_gcflag << 13    # the first unused bit
 
 
 # States for the incremental GC
@@ -2711,6 +2719,9 @@
         # to also set TRACK_YOUNG_PTRS here, for the write barrier.
         hdr.tid |= GCFLAG_VISITED | GCFLAG_TRACK_YOUNG_PTRS
 
+        if self.rrc_state == self.RAWREFCOUNT_STATE_MARKING:
+            hdr.tid |= GCFLAG_GARBAGE
+
         if self.has_gcptr(llop.extract_ushort(llgroup.HALFWORD, hdr.tid)):
             #
             # Trace the content of the object and put all objects it references
@@ -3063,15 +3074,18 @@
 
     rrc_enabled = False
 
-    # The default state. Here cyclic garbage with legacy finalizers is marked.
+    # Default state, no rawrefcount specific code is executed during normal 
marking.
     RAWREFCOUNT_STATE_DEFAULT = 0
 
+    # Here cyclic garbage only reachable from legacy finalizers is marked.
+    RAWREFCOUNT_STATE_MARKING = 1
+
     # The state in which cyclic garbage with legacy finalizers is traced.
     # Do not mark objects during this state, because we remove the flag
     # during tracing and we do not want to trace those objects again. Also
     # during this phase no new objects can be marked, as we are only building
     # the list of cyclic garbage.
-    RAWREFCOUNT_STATE_GARBAGE = 1
+    RAWREFCOUNT_STATE_GARBAGE = 2
 
     _ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
     PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
@@ -3228,19 +3242,22 @@
         gchdr.c_gc_next = next
         next.c_gc_prev = gchdr
 
+    def rawrefcount_begin_garbage(self):
+        # after this, no new objects should be marked with the GCFLAG_GARBAGE
+        # flag
+        self.rrc_state = self.RAWREFCOUNT_STATE_GARBAGE
+
+    def rawrefcount_end_garbage(self):
+        # now there should not be any object left with a GCFLAG_GARBAGE flag
+        # set
+        self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
+
     def rawrefcount_next_garbage_pypy(self):
-        if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
-            self.rrc_state = self.RAWREFCOUNT_STATE_GARBAGE
-
         # 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
-        result = lltype.nullptr(llmemory.GCREF.TO)
-
-        if result == lltype.nullptr(llmemory.GCREF.TO):
-            self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
-        return result
+        return lltype.nullptr(llmemory.GCREF.TO)
 
     def rawrefcount_next_garbage_pyobj(self, curr_pyobj):
         # implement st objects in this list still remain in the set of
@@ -3249,7 +3266,6 @@
         # to pyobjs in this list and might increment the refcount.
         return llmemory.NULL
 
-
     def rrc_invoke_callback(self):
         if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or
                                  self.rrc_pyobj_isolate_list.c_gc_next <>
@@ -3365,61 +3381,63 @@
     _rrc_free._always_inline_ = True
 
     def rrc_major_collection_trace(self):
-        if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
-            # Check, if the cyclic isolate from the last collection cycle
-            # is reachable from outside, after the finalizers have been
-            # executed.
-            self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list)
-            found_alive = False
+        if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
+            if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+                # Check, if the cyclic isolate from the last collection cycle
+                # is reachable from outside, after the finalizers have been
+                # executed.
+                self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list)
+                found_alive = False
+                gchdr = self.rrc_pyobj_old_list.c_gc_next
+                while gchdr <> self.rrc_pyobj_old_list:
+                    if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
+                        found_alive = True
+                        break
+                    gchdr = gchdr.c_gc_next
+                if found_alive:
+                    self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+                                            self.rrc_pyobj_list)
+                else:
+                    self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+                                            self.rrc_pyobj_garbage_list)
+
+            self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list)
+            self._rrc_mark_rawrefcount()
+
+            # handle legacy finalizers
+            self.rrc_state = self.RAWREFCOUNT_STATE_MARKING
+            # TODO: move all pyobjs to sublist of pyobj_list and reset cyclic 
refcount
+            # TODO: mark all pypy-objects with special flag and mark them 
visited
+            self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
+
+            # now handle modern finalizers
+            found_finalizer = False
             gchdr = self.rrc_pyobj_old_list.c_gc_next
             while gchdr <> self.rrc_pyobj_old_list:
-                if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
-                    found_alive = True
-                    break
+                if self.rrc_finalizer_type(gchdr) == \
+                        self.RAWREFCOUNT_FINALIZER_MODERN:
+                    found_finalizer = True
                 gchdr = gchdr.c_gc_next
-            if found_alive:
-                self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
-                                        self.rrc_pyobj_list)
-            else:
-                self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
-                                        self.rrc_pyobj_garbage_list)
-
-        self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list)
-        self._rrc_mark_rawrefcount()
-
-        found_finalizer = False
-        gchdr = self.rrc_pyobj_old_list.c_gc_next
-        while gchdr <> self.rrc_pyobj_old_list:
-            if self.rrc_finalizer_type(gchdr) == \
-                    self.RAWREFCOUNT_FINALIZER_MODERN:
-                found_finalizer = True
-            gchdr = gchdr.c_gc_next
-        if found_finalizer:
-            self._rrc_gc_list_move(self.rrc_pyobj_old_list,
-                                   self.rrc_pyobj_isolate_list)
-
-        self.rrc_p_list_old.foreach(self._rrc_major_trace, found_finalizer)
-        self.rrc_o_list_old.foreach(self._rrc_major_trace, found_finalizer)
-
-        # TODO: for all unreachable objects, which are marked potentially
-        # TODO: uncollectable, move them to the set of uncollectable objs
-
-        # TODO: for all unreachable objects with tp_del (legacy finalizer),
-        # TODO: except for border objects with a refcount of
-        # TODO: REFCNT_FROM_PYPY (equals zero at this point):
-        # TODO:  * mark reachable pypy objects
-        # TODO:  * move reachable cpython objects back to pyobj_list
-        # TODO:  * mark all reachable objects as potentially uncollectable
+            if found_finalizer:
+                self._rrc_gc_list_move(self.rrc_pyobj_old_list,
+                                       self.rrc_pyobj_isolate_list)
+
+            use_cylicrefcnt = not found_finalizer
+        else:
+            use_cylicrefcnt = False
+
+        self.rrc_p_list_old.foreach(self._rrc_major_trace, use_cylicrefcnt)
+        self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrefcnt)
 
         # TODO: handle weakrefs for unreachable objects and create
         # TODO: a list of callbacks, which has to be called after the
         # TODO: the GC runs
 
-    def _rrc_major_trace(self, pyobject, found_finalizer):
+    def _rrc_major_trace(self, pyobject, use_cylicrefcnt):
         from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
         from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
         #
-        if not found_finalizer:
+        if use_cylicrefcnt:
             pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
             if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
                 rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
@@ -3457,9 +3475,10 @@
         self.rrc_o_list_old.delete()
         self.rrc_o_list_old = new_o_list
 
-        if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
-            self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
-                                    self.rrc_pyobj_garbage_list)
+        if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
+            if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+                self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
+                                        self.rrc_pyobj_garbage_list)
 
     def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
         # The pyobject survives if the corresponding obj survives.
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
@@ -627,6 +627,7 @@
                     self.gc.rawrefcount_cyclic_garbage_remove()
                     next_dead = self.gc.rawrefcount_cyclic_garbage_head()
 
+            self.gc.rawrefcount_begin_garbage()
             next_garbage = self.gc.rawrefcount_next_garbage_pypy()
             while next_garbage <> lltype.nullptr(llmemory.GCREF.TO):
                 garbage_pypy.append(next_garbage)
@@ -637,6 +638,7 @@
                 garbage_pyobj.append(next)
                 next = self.gc.rawrefcount_next_garbage_pyobj(last_pyobj)
                 last_pyobj = next
+            self.gc.rawrefcount_end_garbage()
 
         # do a collection to find cyclic isolates and clean them, if there are
         # no finalizers
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,6 +518,12 @@
             self.rawrefcount_cyclic_garbage_remove_ptr = getfn(
                 GCClass.rawrefcount_cyclic_garbage_remove, [s_gc],
                 annmodel.s_None, inline = True)
+            self.rawrefcount_begin_garbage_ptr = getfn(
+                GCClass.rawrefcount_begin_garbage, [s_gc], annmodel.s_None,
+                inline = True)
+            self.rawrefcount_end_garbage_ptr = getfn(
+                GCClass.rawrefcount_end_garbage, [s_gc], annmodel.s_None,
+                inline = True)
             self.rawrefcount_next_garbage_pypy_ptr = getfn(
                 GCClass.rawrefcount_next_garbage_pypy, [s_gc],
                 s_gcref, inline = True)
@@ -1432,6 +1438,14 @@
                   [self.rawrefcount_cyclic_garbage_remove_ptr,
                    self.c_const_gc])
 
+    def gct_gc_rawrefcount_begin_garbage(self, hop):
+        hop.genop("direct_call",
+                  [self.rawrefcount_begin_garbage_ptr, self.c_const_gc])
+
+    def gct_gc_rawrefcount_end_garbage(self, hop):
+        hop.genop("direct_call",
+                  [self.rawrefcount_end_garbage_ptr, self.c_const_gc])
+
     def gct_gc_rawrefcount_next_garbage_pypy(self, hop):
         assert hop.spaceop.result.concretetype == llmemory.GCREF
         hop.genop("direct_call",
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -146,6 +146,14 @@
     return lltype.nullptr(OB_PTR_TYPE.TO)
 
 @not_rpython
+def begin_garbage():
+    pass
+
+@not_rpython
+def end_garbage():
+    pass
+
+@not_rpython
 def next_garbage_pypy(Class):
     return None
 
@@ -426,14 +434,20 @@
         return _spec_p(hop, v_p)
 
 class Entry(ExtRegistryEntry):
-    _about_ = cyclic_garbage_remove
+    _about_ = (cyclic_garbage_remove, begin_garbage, end_garbage)
 
     def compute_result_annotation(self):
         pass
 
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
-        hop.genop('gc_rawrefcount_cyclic_garbage_remove', [])
+        if self.instance is cyclic_garbage_remove:
+            name = 'gc_rawrefcount_cyclic_garbage_remove'
+        elif self.instance is begin_garbage:
+            name = 'gc_rawrefcount_begin_garbage'
+        elif self.instance is end_garbage:
+            name = 'gc_rawrefcount_end_garbage'
+        hop.genop(name, [])
 
 src_dir = py.path.local(__file__).dirpath() / 'src'
 boehm_eci = ExternalCompilationInfo(
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -995,6 +995,10 @@
     def op_gc_rawrefcount_cyclic_garbage_remove(self, *args):
         raise NotImplementedError("gc_rawrefcount_cyclic_garbage_remove")
 
+    def op_gc_rawrefcount_begin_garbage(self, *args):
+        raise NotImplementedError("gc_rawrefcount_begin_garbage")
+    def op_gc_rawrefcount_end_garbage(self, *args):
+        raise NotImplementedError("gc_rawrefcount_end_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):
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,6 +532,8 @@
     'gc_rawrefcount_next_cyclic_isolate':   LLOp(),
     'gc_rawrefcount_cyclic_garbage_head':   LLOp(sideeffects=False),
     'gc_rawrefcount_cyclic_garbage_remove': LLOp(),
+    'gc_rawrefcount_begin_garbage':         LLOp(),
+    'gc_rawrefcount_end_garbage':           LLOp(),
     'gc_rawrefcount_next_garbage_pypy':     LLOp(),
     'gc_rawrefcount_next_garbage_pyobj':    LLOp(),
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to