[pypy-commit] pypy cpyext-gc-cycle: Refactored call to tp_traverse from incminimark so there are no dependencies to pypy

stevie_92 pypy.commits at gmail.com
Fri Jan 11 05:38:59 EST 2019


Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r95600:94b062729ca4
Date: 2018-03-23 11:46 +0100
http://bitbucket.org/pypy/pypy/changeset/94b062729ca4/

Log:	Refactored call to tp_traverse from incminimark so there are no
	dependencies to pypy

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1719,11 +1719,6 @@
     return make_generic_cpy_call(FT, False)(space, func, *args)
 
 @specialize.ll()
-def generic_cpy_call_gc(func, *args):
-    FT = lltype.typeOf(func).TO
-    return make_generic_cpy_call_gc(FT, False)(func, *args)
-
- at specialize.ll()
 def generic_cpy_call_expect_null(space, func, *args):
     FT = lltype.typeOf(func).TO
     return make_generic_cpy_call(FT, True)(space, func, *args)
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -20,8 +20,7 @@
 from rpython.rlib.debug import ll_assert, fatalerror, debug_print
 from rpython.rlib.rawrefcount import (
     REFCNT_MASK, REFCNT_FROM_PYPY, REFCNT_OVERFLOW, REFCNT_CYCLE_BUFFERED,
-    REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE,
-    W_MARKER_DEALLOCATING)
+    REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE)
 from pypy.module.cpyext.api import slot_function
 from pypy.module.cpyext.typeobjectdefs import visitproc
 
@@ -254,6 +253,8 @@
     w_obj._cpyext_attach_pyobj(space, py_obj)
 
 
+w_marker_deallocating = W_Root()
+
 @jit.dont_look_inside
 def from_ref(space, ref):
     """
@@ -265,7 +266,7 @@
         return None
     w_obj = rawrefcount.to_obj(W_Root, ref)
     if w_obj is not None:
-        if w_obj is not W_MARKER_DEALLOCATING:
+        if w_obj is not w_marker_deallocating:
             return w_obj
         fatalerror(
             "*** Invalid usage of a dying CPython object ***\n"
@@ -318,7 +319,7 @@
 
 def pyobj_has_w_obj(pyobj):
     w_obj = rawrefcount.to_obj(W_Root, pyobj)
-    return w_obj is not None and w_obj is not W_MARKER_DEALLOCATING
+    return w_obj is not None and w_obj is not w_marker_deallocating
 
 def w_obj_has_pyobj(w_obj):
     return bool(rawrefcount.from_obj(PyObject, w_obj))
@@ -454,7 +455,7 @@
 @init_function
 def write_w_marker_deallocating(space):
     if we_are_translated():
-        llptr = cast_instance_to_base_ptr(W_MARKER_DEALLOCATING)
+        llptr = cast_instance_to_base_ptr(w_marker_deallocating)
         state = space.fromcache(State)
         state.C.set_marker(rffi.cast(Py_ssize_t, llptr))
 
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
@@ -1,8 +1,8 @@
 from rpython.rlib.objectmodel import we_are_translated, specialize
-from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rtyper.lltypesystem import rffi, lltype, llmemory
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter import executioncontext
-from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.annlowlevel import llhelper, llhelper_args
 from rpython.rlib.rdynload import DLLHANDLE
 from rpython.rlib import rawrefcount
 import sys
@@ -70,7 +70,10 @@
                     decref(space, ob)
                 print 'dealloc_trigger DONE'
                 return "RETRY"
-            rawrefcount.init(dealloc_trigger)
+            def tp_traverse(obj_addr, callback, args):
+                # TODO: implement
+                pass
+            rawrefcount.init(dealloc_trigger, tp_traverse)
         else:
             if space.config.translation.gc == "boehm":
                 action = BoehmPyObjDeallocAction(space)
@@ -80,6 +83,25 @@
                 pyobj_dealloc_action = PyObjDeallocAction(space)
                 self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
 
+                def _rawrefcount_tp_traverse(space, pyobj_ptr, callback, args):
+                    from pypy.module.cpyext.api import (generic_cpy_call,
+                                                        PyObject)
+                    from pypy.module.cpyext.typeobjectdefs import visitproc
+                    # convert to pointers with correct types (PyObject)
+                    callback_addr = llmemory.cast_ptr_to_adr(callback)
+                    callback_ptr = llmemory.cast_adr_to_ptr(callback_addr,
+                                                            visitproc)
+                    pyobj_addr = llmemory.cast_ptr_to_adr(pyobj_ptr)
+                    pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject)
+                    # now call tp_traverse (if possible)
+                    if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse:
+                        generic_cpy_call(space, pyobj.c_ob_type.c_tp_traverse,
+                                         pyobj,
+                                         callback_ptr, args)
+                self.tp_traverse = (lambda o, v, a:
+                                    _rawrefcount_tp_traverse(self.space,
+                                                             o, v, a))
+
     def build_api(self):
         """NOT_RPYTHON
         This function is called when at object space creation,
@@ -111,7 +133,9 @@
                 # does something different. Sigh.
                 rawrefcount.init(
                     llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
-                    self.dealloc_trigger))
+                    self.dealloc_trigger),
+                    llhelper(rawrefcount.RAWREFCOUNT_TRAVERSE,
+                    self.tp_traverse))
             self.builder.attach_all(space)
 
         setup_new_method_def(space)
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
@@ -74,7 +74,6 @@
 from rpython.rlib.objectmodel import specialize
 from rpython.rlib import rgc
 from rpython.memory.gc.minimarkpage import out_of_memory
-from pypy.module.cpyext.api import slot_function, PyObject
 from rpython.rtyper.lltypesystem import rffi
 
 #
@@ -190,30 +189,6 @@
                               ('forw', llmemory.Address))
 FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB)
 NURSARRAY = lltype.Array(llmemory.Address)
-VISIT_FUNCTYPE = rffi.CCallback([PyObject, rffi.VOIDP],
-                                rffi.INT_real)
-
-
-def visit_trace_non_rc_roots(pyobj, self_ptr):
-    from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK,
-                                          REFCNT_CLR_MASK)
-    from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
-
-    self_adr = rffi.cast(llmemory.Address, self_ptr)
-    self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr)
-
-    # if the pyobj is not marked, remember it and if there is a linked pypy
-    # object also remember it
-    if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_BLACK:
-        pyobject = llmemory.cast_ptr_to_adr(pyobj)
-        self.rrc_more_pyobjects_to_scan.append(pyobject)
-        intobj = pyobj.c_ob_pypy_link
-        if intobj != 0:
-            obj = llmemory.cast_int_to_adr(intobj)
-            hdr = self.header(obj)
-            if not (hdr.tid & GCFLAG_VISITED):
-                self.objects_to_trace.append(obj)
-    return rffi.cast(rffi.INT_real, 0)
 
 # ____________________________________________________________
 
@@ -3020,11 +2995,17 @@
                               ('c_ob_pypy_link', lltype.Signed))
     PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
     RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+    VISIT_FUNCTYPE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
+                                                rffi.INT_real))
+    RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR,
+                                                       VISIT_FUNCTYPE,
+                                                       rffi.VOIDP],
+                                                      lltype.Void))
 
     def _pyobj(self, pyobjaddr):
-        return llmemory.cast_adr_to_ptr(pyobjaddr, lltype.Ptr(PyObject.TO))
+            return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
 
-    def rawrefcount_init(self, dealloc_trigger_callback):
+    def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse):
         # see pypy/doc/discussion/rawrefcount.rst
         if not self.rrc_enabled:
             self.rrc_p_list_young = self.AddressStack()
@@ -3035,6 +3016,7 @@
             self.rrc_p_dict       = self.AddressDict()  # non-nursery keys only
             self.rrc_p_dict_nurs  = self.AddressDict()  # nursery keys only
             self.rrc_dealloc_trigger_callback = dealloc_trigger_callback
+            self.rrc_tp_traverse = tp_traverse
             self.rrc_dealloc_pending = self.AddressStack()
             self.rrc_pyobjects_to_scan = self.AddressStack()
             self.rrc_more_pyobjects_to_scan = self.AddressStack()
@@ -3214,10 +3196,13 @@
     NO_CYCLE_DETECTION = False
 
     def rrc_major_collection_trace(self):
+        debug_start("gc-rrc-trace")
         if self.NO_CYCLE_DETECTION:
             self.rrc_p_list_old.foreach(self._rrc_major_trace, None)
         else:
             self.rrc_major_collection_trace_cycle()
+            self.rrc_p_list_old.foreach(self._rrc_major_trace, None) # for now, remove later
+        debug_stop("gc-rrc-trace")
 
     def _rrc_major_trace(self, pyobject, ignore):
         from rpython.rlib.rawrefcount import REFCNT_MASK
@@ -3238,20 +3223,22 @@
         assert not self.rrc_more_pyobjects_to_scan.non_empty()
         assert not self.rrc_pyobjects_to_trace.non_empty()
 
-        # initially, scan all old pyobjects which are linked to objects
-        self.rrc_p_list_old.foreach(self._rrc_major_scan_non_rc_roots, None)
+        # initially, scan all real pyobjects (not proxies) which are linked to objects
+        #self.rrc_p_list_old.foreach(self._rrc_major_scan_non_rc_roots, None)
+        self.rrc_o_list_old.foreach(self._rrc_major_scan_non_rc_roots, None)
 
         # as long as we find new pyobjects which should be marked, recursively
         # mark them
         while self.rrc_pyobjects_to_trace.non_empty():
             while self.rrc_pyobjects_to_trace.non_empty():
-                pyobj = self.rrc_pyobjects_to_trace.pop()
-                self._rrc_major_trace_non_rc_roots(pyobj)
+                pyobject = self.rrc_pyobjects_to_trace.pop()
+                self._rrc_traverse(pyobject)
 
             # see if we found new pypy objects to trace
             if self.objects_to_trace.non_empty():
                 self.visit_all_objects()
             self.objects_to_trace.delete()
+            self.objects_to_trace = self.AddressStack()
 
             # look if there are some pyobjects with linked objects which were
             # not marked previously, but are marked now
@@ -3261,26 +3248,29 @@
             self.rrc_pyobjects_to_scan.foreach(
                 self._rrc_major_scan_non_rc_roots, None)
             self.rrc_pyobjects_to_scan.delete()
+            self.rrc_pyobjects_to_scan = self.AddressStack()
 
-    def traverse(self, pyobject, func_ptr):
-        from pypy.module.cpyext.api import generic_cpy_call_gc
-        from pypy.module.cpyext.typeobjectdefs import visitproc
-        from rpython.rtyper.annlowlevel import cast_nongc_instance_to_adr
-        self_addr = cast_nongc_instance_to_adr(self)
-        pyobj = self._pyobj(pyobject)
-        if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse:
-            visitproc_ptr = rffi.cast(visitproc, func_ptr)
-            generic_cpy_call_gc(pyobj.c_ob_type.c_tp_traverse, pyobj,
-                            visitproc_ptr, rffi.cast(rffi.VOIDP, self_addr))
-            #cast_nongc_instance_to_adr(self)
+        self.rrc_more_pyobjects_to_scan.delete()
+        self.rrc_more_pyobjects_to_scan = self.AddressStack()
 
-    def _rrc_major_trace_non_rc_roots(self, pyobject):
-        from rpython.rtyper.annlowlevel import llhelper
-        func_ptr = llhelper(VISIT_FUNCTYPE, visit_trace_non_rc_roots)
-        self.traverse(pyobject, func_ptr)
+    def _rrc_mark_cpyobj(self, pyobj):
+        from rpython.rlib.rawrefcount import (REFCNT_CLR_GRAY,
+                                              REFCNT_CLR_MASK)
+        # if the pyobj is not marked, remember it and if there is a linked pypy
+        # object also remember it
+        if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_GRAY:
+            pyobj.c_ob_refcnt = REFCNT_CLR_GRAY
+            pyobject = llmemory.cast_ptr_to_adr(pyobj)
+            self.rrc_more_pyobjects_to_scan.append(pyobject)
+            intobj = pyobj.c_ob_pypy_link
+            if intobj != 0:
+                obj = llmemory.cast_int_to_adr(intobj)
+                hdr = self.header(obj)
+                if not (hdr.tid & GCFLAG_VISITED):
+                    self.objects_to_trace.append(obj)
 
     def _rrc_major_scan_non_rc_roots(self, pyobject, ignore):
-        from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK,
+        from rpython.rlib.rawrefcount import (REFCNT_CLR_GRAY,
                                               REFCNT_CLR_MASK)
         # check in the object header of the linked pypy object, if it is marked
         # or not
@@ -3289,8 +3279,9 @@
         obj = llmemory.cast_int_to_adr(intobj)
         hdr = self.header(obj)
         if hdr.tid & GCFLAG_VISITED:
-            if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_BLACK:
+            if pyobj.c_ob_refcnt & REFCNT_CLR_MASK != REFCNT_CLR_GRAY: # TODO change to black, but make white default
                 # process the pyobject now
+                pyobj.c_ob_refcnt = REFCNT_CLR_GRAY
                 self.rrc_pyobjects_to_trace.append(pyobject)
         else:
             # save the pyobject for later, in case its linked object becomes
@@ -3330,3 +3321,22 @@
                 surviving_dict.insertclean(obj, pyobject)
         else:
             self._rrc_free(pyobject)
+
+    def _rrc_visit(pyobj, self_ptr):
+        from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
+        #
+        debug_print("visit called!")
+        self_adr = rffi.cast(llmemory.Address, self_ptr)
+        self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr)
+        self._rrc_mark_cpyobj(pyobj)
+        return rffi.cast(rffi.INT_real, 0)
+
+    def _rrc_traverse(self, pyobject):
+        from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
+                                                llhelper)
+        #
+        pyobj = self._pyobj(pyobject)
+        callback_ptr = llhelper(self.VISIT_FUNCTYPE,
+                                IncrementalMiniMarkGC._rrc_visit)
+        self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
+        self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr)
\ 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
@@ -53,7 +53,9 @@
         else:
             rc = REFCNT_FROM_PYPY
         self.trigger = []
-        self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+        self.trigger2 = []
+        self.gc.rawrefcount_init(lambda: self.trigger.append(1),
+                                 lambda: self.trigger2.append(1))
         #
         if create_immortal:
             p1 = lltype.malloc(S, immortal=True)
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
@@ -475,7 +475,8 @@
         if hasattr(GCClass, 'rawrefcount_init'):
             self.rawrefcount_init_ptr = getfn(
                 GCClass.rawrefcount_init,
-                [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER)],
+                [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER),
+                 SomePtr(GCClass.RAWREFCOUNT_TRAVERSE)],
                 annmodel.s_None)
             self.rawrefcount_create_link_pypy_ptr = getfn(
                 GCClass.rawrefcount_create_link_pypy,
@@ -1314,10 +1315,12 @@
         self.pop_roots(hop, livevars)
 
     def gct_gc_rawrefcount_init(self, hop):
-        [v_fnptr] = hop.spaceop.args
+        [v_fnptr, v_fnptr2] = hop.spaceop.args
         assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER
+        assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE
         hop.genop("direct_call",
-                  [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr])
+                  [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr,
+                   v_fnptr2])
 
     def gct_gc_rawrefcount_create_link_pypy(self, hop):
         [v_gcobj, v_pyobject] = hop.spaceop.args
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -10,7 +10,6 @@
 from rpython.rtyper.extregistry import ExtRegistryEntry
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rlib import rgc, objectmodel
-from pypy.interpreter.baseobjspace import W_Root
 
 
 MAX_BIT = int(math.log(sys.maxint, 2))
@@ -44,9 +43,17 @@
 REFCNT_OVERFLOW = 1 << REFCNT_BITS
 REFCNT_MASK = (1 << REFCNT_BITS + 1) - 1
 
-
+PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
+                          ('c_ob_refcnt', lltype.Signed),
+                          ('c_ob_pypy_link', lltype.Signed))
+PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
 RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
-W_MARKER_DEALLOCATING = W_Root()
+VISIT_FUNCTYPE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
+                                            rffi.INT_real))
+RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR,
+                                                   VISIT_FUNCTYPE,
+                                                   rffi.VOIDP],
+                                                  lltype.Void))
 
 
 def _build_pypy_link(p):
@@ -97,12 +104,12 @@
 # TODO: _cyclic_refcount_overflow = dict()
 
 @not_rpython
-def init(dealloc_trigger_callback=None):
+def init(dealloc_trigger_callback=None, tp_traverse=None):
     """set up rawrefcount with the GC.  This is only used
     for tests; it should not be called at all during translation.
     """
     global _p_list, _o_list, _adr2pypy, _pypy2ob, _pypy2ob_rev
-    global _d_list, _dealloc_trigger_callback
+    global _d_list, _dealloc_trigger_callback, _tp_traverse
     _p_list = []
     _o_list = []
     _adr2pypy = [None]
@@ -111,6 +118,7 @@
     _d_list = []
     _d_marker = None
     _dealloc_trigger_callback = dealloc_trigger_callback
+    _tp_traverse = tp_traverse
 
 # def init_traverse(traverse_cpy_call):
 #     global _traverse_cpy_call
@@ -308,14 +316,15 @@
 class Entry(ExtRegistryEntry):
     _about_ = init
 
-    def compute_result_annotation(self, s_dealloc_callback):
+    def compute_result_annotation(self, s_dealloc_callback, tp_traverse):
         from rpython.rtyper.llannotation import SomePtr
         assert isinstance(s_dealloc_callback, SomePtr)   # ll-ptr-to-function
+        # add assert?
 
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
-        [v_dealloc_callback] = hop.inputargs(hop.args_r[0])
-        hop.genop('gc_rawrefcount_init', [v_dealloc_callback])
+        v_dealloc_callback, v_tp_traverse = hop.inputargs(*hop.args_r)
+        hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse])
 
 
 class Entry(ExtRegistryEntry):


More information about the pypy-commit mailing list