Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r96623:074fd35b8463
Date: 2019-05-16 07:56 +0200
http://bitbucket.org/pypy/pypy/changeset/074fd35b8463/

Log:    Fixed tp_clear call in cpyext (GIL handling) Fixed some inheritance
        issues with cpyext objects Added debug output for rrc lists WIP:
        implement gc support for cpyext tuples WIP: handling of untracked
        rrc objects in incminimark (keep flag, etc)

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
@@ -1185,6 +1185,10 @@
     state.C._PyPy_tuple_free = rffi.llexternal(
         '_PyPy_tuple_free', [rffi.VOIDP], lltype.Void,
         compilation_info=eci, _nowrapper=True)
+    from pypy.module.cpyext.typeobjectdefs import visitproc
+    state.C._PyPy_tuple_traverse = rffi.llexternal(
+        '_PyPy_tuple_traverse', [PyObject, visitproc, rffi.VOIDP],
+        rffi.INT, compilation_info=eci, _nowrapper=True)
     state.C._PyPy_tuple_dealloc = rffi.llexternal(
         '_PyPy_tuple_dealloc', [PyObject], lltype.Void,
         compilation_info=eci, _nowrapper=True)
diff --git a/pypy/module/cpyext/include/tupleobject.h 
b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -19,6 +19,7 @@
 PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
 PyAPI_FUNC(void) _PyPy_tuple_dealloc(PyObject *);
 PyAPI_FUNC(void) _PyPy_tuple_free(void *);
+PyAPI_FUNC(int) _PyPy_tuple_traverse(PyObject *ob, visitproc visit, void *arg);
 
 /* defined in varargswrapper.c */
 PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
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
@@ -81,6 +81,12 @@
         state = space.fromcache(State)
         return state.C.PyObject_Free
 
+    def has_traverse(self, space):
+        return False
+
+    def get_traverse(self, space):
+        return 0
+
     def allocate(self, space, w_type, itemcount=0, immortal=False):
         state = space.fromcache(State)
         ob_type = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_type))
@@ -129,6 +135,7 @@
     tp_realize    = kw.pop('realize', None)
     tp_dealloc    = kw.pop('dealloc', None)
     tp_free       = kw.pop('free', None)
+    tp_traverse   = kw.pop('traverse', None)
     assert not kw, "Extra arguments to make_typedescr"
 
     null_dealloc = lltype.nullptr(lltype.FuncType([PyObject], lltype.Void))
@@ -159,6 +166,17 @@
         if tp_realize:
             def realize(self, space, ref):
                 return tp_realize(space, ref)
+
+        if tp_traverse:
+            def get_traverse(self, space):
+                from rpython.rtyper.lltypesystem import llmemory
+                from pypy.module.cpyext.typeobjectdefs import traverseproc
+                ptr = rffi.cast(traverseproc, tp_traverse)
+                obj = llmemory.cast_ptr_to_adr(ptr)
+                return llmemory.cast_adr_to_int(obj, "symbolic")
+
+            def has_traverse(self, space):
+                return True
     if typedef:
         CpyTypedescr.__name__ = "CpyTypedescr_%s" % (typedef.name,)
 
diff --git a/pypy/module/cpyext/src/tupleobject.c 
b/pypy/module/cpyext/src/tupleobject.c
--- a/pypy/module/cpyext/src/tupleobject.c
+++ b/pypy/module/cpyext/src/tupleobject.c
@@ -94,4 +94,15 @@
 _PyPy_tuple_free(void *obj)
 {
     PyObject_GC_Del(obj);
+}
+
+int
+_PyPy_tuple_traverse(PyObject *ob, visitproc visit, void *arg)
+{
+    PyTupleObject *o = (PyTupleObject *)ob;
+    Py_ssize_t i;
+
+    for (i = Py_SIZE(o); --i >= 0; )
+        Py_VISIT(o->ob_item[i]);
+    return 0;
 }
\ No newline at end of file
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
@@ -6,6 +6,7 @@
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rdynload import DLLHANDLE
 from rpython.rlib import rawrefcount
+from rpython.rlib.debug import debug_print
 import sys
 
 
@@ -177,6 +178,7 @@
                 def _tp_traverse(pyobj_ptr, callback, args):
                     from pypy.module.cpyext.api import PyObject
                     from pypy.module.cpyext.typeobjectdefs import visitproc
+                    from pypy.module.cpyext.pyobject import cts
                     # convert to pointers with correct types (PyObject)
                     callback_addr = llmemory.cast_ptr_to_adr(callback)
                     callback_ptr = llmemory.cast_adr_to_ptr(callback_addr,
@@ -184,9 +186,31 @@
                     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:
-                        pyobj.c_ob_type.c_tp_traverse(pyobj, callback_ptr,
-                                                      args)
+                    debug_print("rrc check traverse", pyobj)
+                    pto = pyobj.c_ob_type
+                    if pto and pto.c_tp_name:
+                        tp_name = pto.c_tp_name
+                        name = rffi.charp2str(cts.cast('char*', tp_name))
+                        debug_print("rrc try traverse", pyobj, ": type", pto,
+                                    ": name", name)
+                        pto2 = pto
+                        i = 1
+                        while pto2.c_tp_base:
+                            base = pto2.c_tp_base
+                            if base.c_tp_name:
+                                tp_name = base.c_tp_name
+                                name = rffi.charp2str(cts.cast('char*', 
tp_name))
+                                debug_print(" " * i * 3, "basetype",
+                                            base, ": name",
+                                            name, "traverse",
+                                            base.c_tp_traverse)
+                            else:
+                                debug_print(" " * i * 3, "unknown base")
+                            pto2 = base
+                            i += 1
+                    if pto and pto.c_tp_traverse:
+                        debug_print("rrc do traverse", pyobj)
+                        pto.c_tp_traverse(pyobj, callback_ptr, args)
 
                 self.tp_traverse = (lambda o, v, a:_tp_traverse(o, v, a))
 
@@ -308,7 +332,8 @@
 def _rawrefcount_perform(space):
     from pypy.interpreter.baseobjspace import W_Root
     from pypy.module.cpyext.pyobject import (PyObject, incref, decref,
-                                             finalize, from_ref)
+                                             finalize, from_ref, cts)
+    from pypy.module.cpyext.api import generic_cpy_call
 
     while True:
         py_obj = rawrefcount.next_dead(PyObject)
@@ -328,9 +353,15 @@
             break
         pyobj = rffi.cast(PyObject, py_obj)
         adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj))
-        if pyobj.c_ob_type.c_tp_clear:
+        pto = pyobj.c_ob_type
+        if pto.c_tp_clear:
             incref(space, py_obj)
-            pyobj.c_ob_type.c_tp_clear(pyobj)
+            if pto and pto.c_tp_name:
+                tp_name = pto.c_tp_name
+                name = rffi.charp2str(cts.cast('char*', tp_name))
+                debug_print("tp_clear", pyobj, ": type", pto,
+                            ": name", name)
+            generic_cpy_call(space, pto.c_tp_clear, pyobj)
             decref(space, py_obj)
         head = rawrefcount.cyclic_garbage_head(PyObject)
         if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)):
diff --git a/pypy/module/cpyext/tupleobject.py 
b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -46,7 +46,8 @@
                    alloc=tuple_alloc,
                    dealloc=state.C._PyPy_tuple_dealloc,
                    realize=tuple_realize,
-                   free=state.C._PyPy_tuple_free)
+                   free=state.C._PyPy_tuple_free,
+                   traverse=state.C._PyPy_tuple_traverse)
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers_flags("Tuple")
 
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -653,7 +653,13 @@
     else:
         pto.c_tp_free = pto.c_tp_base.c_tp_free
 
-    # TODO: traverse (for tuple)
+    from rpython.rtyper.lltypesystem import llmemory
+    from pypy.module.cpyext.typeobjectdefs import traverseproc
+    if typedescr.has_traverse(space):
+        traverse = typedescr.get_traverse(space)
+        obj = llmemory.cast_int_to_adr(traverse)
+        traverse_ptr = llmemory.cast_adr_to_ptr(obj, traverseproc)
+        pto.c_tp_traverse = traverse_ptr
 
     if builder.cpyext_type_init is not None:
         builder.cpyext_type_init.append((pto, w_type))
@@ -723,6 +729,10 @@
             pto.c_tp_init = base.c_tp_init
         if not pto.c_tp_alloc:
             pto.c_tp_alloc = base.c_tp_alloc
+        if not pto.c_tp_clear:
+            pto.c_tp_clear = base.c_tp_clear
+        if not pto.c_tp_traverse:
+            pto.c_tp_traverse = base.c_tp_traverse
         # XXX check for correct GC flags!
         if not pto.c_tp_free:
             pto.c_tp_free = base.c_tp_free
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
@@ -3119,6 +3119,7 @@
     RAWREFCOUNT_FINALIZER_LEGACY = 2
     RAWREFCOUNT_REFS_SHIFT = 1
     RAWREFCOUNT_REFS_MASK_FINALIZED = 1
+    RAWREFCOUNT_REFS_UNTRACKED = -2
 
     def _pyobj(self, pyobjaddr):
         return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
@@ -3379,6 +3380,9 @@
             rc -= REFCNT_FROM_PYPY
             self._pyobj(pyobject).c_ob_pypy_link = 0
             if rc == 0:
+                #pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
+                #if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
+                #    self._rrc_debug_check_list(pygchdr)
                 self.rrc_dealloc_pending.append(pyobject)
                 # an object with refcnt == 0 cannot stay around waiting
                 # for its deallocator to be called.  Some code (lxml)
@@ -3393,6 +3397,7 @@
     _rrc_free._always_inline_ = True
 
     def rrc_major_collection_trace(self):
+        self._rrc_debug_check_consistency(print_label="begin-mark")
         # Only trace and mark rawrefcounted object if we are not doing
         # something special, like building gc.garbage.
         if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
@@ -3401,38 +3406,57 @@
                 self._rrc_check_finalizer()
             # collect all rawrefcounted roots
             self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list)
+            self._rrc_debug_check_consistency(print_label="roots-marked")
             # mark all objects reachable from rawrefcounted roots
             self._rrc_mark_rawrefcount()
+            self._rrc_debug_check_consistency(print_label="before-fin")
             self.rrc_state = self.RAWREFCOUNT_STATE_MARKING
             if self._rrc_find_garbage(): # handle legacy finalizers
                 self._rrc_mark_garbage()
+                self._rrc_debug_check_consistency(print_label="end-legacy-fin")
             self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
             use_cylicrc = not self._rrc_find_finalizer() # modern finalizers
+            self._rrc_debug_check_consistency(print_label="end-mark-cyclic")
         else:
             use_cylicrc = False # don't sweep any objects in cyclic isolates
 
         # now mark all pypy objects at the border, depending on the results
+        debug_print("use_cylicrc", use_cylicrc)
         self.rrc_p_list_old.foreach(self._rrc_major_trace, use_cylicrc)
-        self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrc)
+
+        # TODO: check again, if some new border objects have been marked and
+        #       continue marking recursively... why needed? -> wrapper for
+        #       pypy-obj is no pygc-obj??? ...
+
+        self._rrc_debug_check_consistency(print_label="end-mark")
+        #self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrc) # 
TODO: remove?
 
     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
         #
+        pyobj = self._pyobj(pyobject)
+        cyclic_rc = -42
         if use_cylicrefcnt:
-            pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
+            pygchdr = self.rrc_pyobj_as_gc(pyobj)
             if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
+                #if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
                 rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
+                cyclic_rc = rc
+                #else:
+                #    rc = pyobj.c_ob_refcnt
             else:
-                rc = self._pyobj(pyobject).c_ob_refcnt
+                rc = pyobj.c_ob_refcnt
         else:
-            rc = self._pyobj(pyobject).c_ob_refcnt
+            rc = pyobj.c_ob_refcnt
 
         if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0:
             pass  # the corresponding object may die
         else:
             # force the corresponding object to be alive
-            intobj = self._pyobj(pyobject).c_ob_pypy_link
+            debug_print("pyobj stays alive", pyobj, "rc", rc, "cyclic_rc",
+                        cyclic_rc)
+            intobj = pyobj.c_ob_pypy_link
             obj = llmemory.cast_int_to_adr(intobj)
             self.objects_to_trace.append(obj)
             self.visit_all_objects()
@@ -3445,7 +3469,7 @@
         new_p_list = self.AddressStack()
         while self.rrc_p_list_old.non_empty():
             self._rrc_major_free(self.rrc_p_list_old.pop(), new_p_list,
-                                                            new_p_dict)
+                                                              new_p_dict)
         self.rrc_p_list_old.delete()
         self.rrc_p_list_old = new_p_list
         #
@@ -3453,15 +3477,17 @@
         no_o_dict = self.null_address_dict()
         while self.rrc_o_list_old.non_empty():
             self._rrc_major_free(self.rrc_o_list_old.pop(), new_o_list,
-                                                            no_o_dict)
+                                                              no_o_dict)
         self.rrc_o_list_old.delete()
         self.rrc_o_list_old = new_o_list
 
         if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
             if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+                self._rrc_debug_check_consistency()
                 self._rrc_clear_weakref_callbacks()
                 self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
                                         self.rrc_pyobj_dead_list)
+                self._rrc_debug_check_consistency(print_label="before-sweep")
 
     def _rrc_clear_weakref_callbacks(self):
         # Look for any weakrefs within the trash cycle and remove the callback.
@@ -3527,13 +3553,13 @@
                 refcnt -= REFCNT_FROM_PYPY_LIGHT
             elif refcnt >= REFCNT_FROM_PYPY:
                 refcnt -= REFCNT_FROM_PYPY
-            pygchdr.c_gc_refs = pygchdr.c_gc_refs & \
-                                self.RAWREFCOUNT_REFS_MASK_FINALIZED
-            pygchdr.c_gc_refs = refcnt << self.RAWREFCOUNT_REFS_SHIFT
+            self._rrc_pyobj_gc_refcnt_set(pygchdr, refcnt)
             pygchdr = pygchdr.c_gc_next
 
+        self._rrc_debug_check_consistency(print_label="rc-initialized")
+
         # For every object in this set, if it is marked, add 1 as a real
-        # refcount
+        # refcount (p_list => pyobj stays alive if obj stays alive).
         self.rrc_p_list_old.foreach(self._rrc_obj_fix_refcnt, None)
         self.rrc_o_list_old.foreach(self._rrc_obj_fix_refcnt, None)
 
@@ -3548,13 +3574,31 @@
         # now all rawrefcounted roots or live border objects have a
         # refcount > 0
 
+    def _rrc_pyobj_gc_refcnt_set(self, pygchdr, refcnt):
+        pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED
+        pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT
+
     def _rrc_obj_fix_refcnt(self, pyobject, ignore):
-        intobj = self._pyobj(pyobject).c_ob_pypy_link
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+        from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+        #
+        pyobj = self._pyobj(pyobject)
+        intobj = pyobj.c_ob_pypy_link
         obj = llmemory.cast_int_to_adr(intobj)
-        gchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
+        gchdr = self.rrc_pyobj_as_gc(pyobj)
         if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
-            if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
-                gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
+            #if gchdr.c_gc_refs == self.RAWREFCOUNT_REFS_UNTRACKED:
+            #    debug_print("gc obj not tracked", gchdr, ": obj", obj)
+            #else:
+            refcnt = pyobj.c_ob_refcnt
+            if refcnt >= REFCNT_FROM_PYPY_LIGHT:
+                refcnt -= REFCNT_FROM_PYPY_LIGHT
+            elif refcnt >= REFCNT_FROM_PYPY:
+                refcnt -= REFCNT_FROM_PYPY
+            if self.header(obj).tid & (GCFLAG_VISITED |
+                                       GCFLAG_NO_HEAP_PTRS):
+                refcnt += 1
+            self._rrc_pyobj_gc_refcnt_set(gchdr, refcnt)
 
     def _rrc_mark_rawrefcount(self):
         if self._rrc_gc_list_is_empty(self.rrc_pyobj_list):
@@ -3785,3 +3829,50 @@
 
     def _rrc_gc_list_is_empty(self, pygclist):
         return pygclist.c_gc_next == pygclist
+
+    def _rrc_debug_check_consistency(self, print_label=None):
+        if self.DEBUG:
+            should_print = print_label is not None
+            if should_print:
+                debug_start("rrc-lists " + print_label)
+            self._rrc_debug_check_list(self.rrc_pyobj_list,
+                                       should_print, "rrc_pyobj_list")
+            self._rrc_debug_check_list(self.rrc_pyobj_old_list,
+                                       should_print, "rrc_pyobj_old_list")
+            self._rrc_debug_check_list(self.rrc_pyobj_dead_list,
+                                       should_print, "rrc_pyobj_dead_list")
+            self._rrc_debug_check_list(self.rrc_pyobj_garbage_list,
+                                       should_print, "rrc_pyobj_garbage_list")
+            self._rrc_debug_check_list(self.rrc_pyobj_isolate_list,
+                                       should_print, "rrc_pyobj_isolate_list")
+            if should_print:
+                debug_stop("rrc-lists " + print_label)
+
+    def _rrc_debug_check_list(self, list, should_print, print_label):
+        if should_print:
+            debug_start(print_label)
+        gchdr = list.c_gc_next
+        prev = list
+        while gchdr <> list:
+            if should_print:
+                pyobj = self.rrc_gc_as_pyobj(gchdr)
+                intobj = pyobj.c_ob_pypy_link
+                debug_print("item", gchdr, ": pyobj", pyobj,
+                            "cyclic refcnt",
+                            gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT,
+                            "refcnt", pyobj.c_ob_refcnt,
+                            "link", intobj)
+                if intobj:
+                    obj = llmemory.cast_int_to_adr(intobj)
+                    marked = self.header(obj).tid & \
+                             (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS)
+                    debug_print("  linked obj", obj, ": marked", marked)
+
+            ll_assert(gchdr.c_gc_next != lltype.nullptr(self.PYOBJ_GC_HDR),
+                      "gc_next is null")
+            ll_assert(gchdr.c_gc_prev == prev, "gc_prev is inconsistent")
+            prev = gchdr
+            gchdr = gchdr.c_gc_next
+        ll_assert(list.c_gc_prev == prev, "gc_prev is inconsistent")
+        if should_print:
+            debug_stop(print_label)
\ No newline at end of file
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to