Author: Stefan Beyer <h...@sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r95607:a189719f68e4
Date: 2018-07-06 23:56 +0200
http://bitbucket.org/pypy/pypy/changeset/a189719f68e4/

Log:    Implemented pyobj as gc and vice-versa Cleaned cpyext and
        gc/rawrefcount tests Cleaned translation options

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
@@ -43,7 +43,6 @@
 from rpython.rlib import rstackovf
 from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 from pypy.module.cpyext.cparser import CTypeSpace
-from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
 
 DEBUG_WRAPPER = True
 
@@ -753,6 +752,7 @@
 PyVarObject = cts.gettype('PyVarObject *')
 PyGC_Head = cts.gettype('PyGC_Head')
 PyGC_HeadPtr = cts.gettype('PyGC_Head *')
+GCHdr_PyObject = cts.gettype('GCHdr_PyObject *')
 
 Py_buffer = cts.gettype('Py_buffer')
 Py_bufferP = cts.gettype('Py_buffer *')
@@ -1175,8 +1175,17 @@
     state.C._PyPy_object_dealloc = rffi.llexternal(
         '_PyPy_object_dealloc', [PyObject], lltype.Void,
         compilation_info=eci, _nowrapper=True)
-    state.C._PyPy_InitPyObjList = rffi.llexternal(
-        '_PyPy_InitPyObjList', [], PyGC_HeadPtr,
+    state.C._PyPy_subtype_dealloc = rffi.llexternal(
+        '_PyPy_subtype_dealloc', [PyObject], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
+    state.C._PyPy_init_pyobj_list = rffi.llexternal(
+        '_PyPy_init_pyobj_list', [], PyGC_HeadPtr,
+        compilation_info=eci, _nowrapper=True)
+    state.C._PyPy_gc_as_pyobj = rffi.llexternal(
+        '_PyPy_gc_as_pyobj', [PyGC_HeadPtr], GCHdr_PyObject,
+        compilation_info=eci, _nowrapper=True)
+    state.C._PyPy_pyobj_as_gc = rffi.llexternal(
+        '_PyPy_pyobj_as_gc', [GCHdr_PyObject], PyGC_HeadPtr,
         compilation_info=eci, _nowrapper=True)
 
 
@@ -1300,7 +1309,7 @@
                 ctypes.c_void_p)
 
     # initialize the pyobj_list for the gc
-    pyobj_list = space.fromcache(State).C._PyPy_InitPyObjList()
+    pyobj_list = space.fromcache(State).C._PyPy_init_pyobj_list()
     rawrefcount._init_pyobj_list(pyobj_list)
 
     # we need to call this *after* the init code above, because it might
@@ -1309,9 +1318,6 @@
     # _PyPy_Malloc)
     builder.attach_all(space)
 
-    #import rpython.rlib.rawrefcount
-    #rawrefcount.init_traverse(generic_cpy_call_gc)
-
     setup_init_functions(eci, prefix)
     return modulename.new(ext='')
 
diff --git a/pypy/module/cpyext/include/object.h 
b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -326,8 +326,8 @@
 
 #define _PyGC_REFS(o) _PyGCHead_REFS(_Py_AS_GC(o))
 
-#define _PyGC_REFS_UNTRACKED                    (-2)
-#define _PyGC_REFS_REACHABLE                    (-3)
+#define _PyGC_REFS_UNTRACKED               (-2)
+#define _PyGC_REFS_REACHABLE               (-3)
 #define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4)
 
 #define _PyGC_IS_TRACKED(o) (_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)
@@ -435,7 +435,9 @@
 #define _PyObject_GC_Del PyObject_GC_Del
 PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *);
 PyAPI_FUNC(void) _PyPy_object_dealloc(PyObject *);
-PyAPI_FUNC(PyGC_Head *) _PyPy_InitPyObjList();
+PyAPI_FUNC(PyGC_Head *) _PyPy_init_pyobj_list();
+PyAPI_FUNC(GCHdr_PyObject *) _PyPy_gc_as_pyobj(PyGC_Head *);
+PyAPI_FUNC(PyGC_Head *) _PyPy_pyobj_as_gc(GCHdr_PyObject *);
 
 #ifdef __cplusplus
 }
diff --git a/pypy/module/cpyext/parse/cpyext_object.h 
b/pypy/module/cpyext/parse/cpyext_object.h
--- a/pypy/module/cpyext/parse/cpyext_object.h
+++ b/pypy/module/cpyext/parse/cpyext_object.h
@@ -324,7 +324,12 @@
 
 
 typedef struct _gc_head {
-    void *gc_next;
-    void *gc_prev;
+    struct _gc_head *gc_next;
+    struct _gc_head *gc_prev;
     Py_ssize_t gc_refs;
-} PyGC_Head;
\ No newline at end of file
+} PyGC_Head;
+
+typedef struct _gchdr_pyobject {
+    Py_ssize_t ob_refcnt;
+    Py_ssize_t ob_pypy_link;
+} GCHdr_PyObject;
\ No newline at end of file
diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c
--- a/pypy/module/cpyext/src/object.c
+++ b/pypy/module/cpyext/src/object.c
@@ -38,13 +38,25 @@
 PyGC_Head *_pypy_rawrefcount_pyobj_list = &_internal_pyobj_list;
 
 PyGC_Head *
-_PyPy_InitPyObjList()
+_PyPy_init_pyobj_list()
 {
     _pypy_rawrefcount_pyobj_list->gc_next = _pypy_rawrefcount_pyobj_list;
     _pypy_rawrefcount_pyobj_list->gc_prev = _pypy_rawrefcount_pyobj_list;
     return _pypy_rawrefcount_pyobj_list;
 }
 
+GCHdr_PyObject *
+_PyPy_gc_as_pyobj(PyGC_Head *g)
+{
+    return (GCHdr_PyObject *)FROM_GC(g);
+}
+
+PyGC_Head *
+_PyPy_pyobj_as_gc(GCHdr_PyObject *obj)
+{
+    return AS_GC(obj);
+}
+
 void
 _Py_Dealloc(PyObject *obj)
 {
@@ -118,7 +130,7 @@
     if (type->tp_itemsize)
         size += nitems * type->tp_itemsize;
 
-    g = (PyObject*)_PyPy_Malloc(size);
+    g = (PyGC_Head*)_PyPy_Malloc(size);
     if (g == NULL)
         return NULL;
     g->gc_refs = 0;
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
@@ -2,7 +2,7 @@
 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, llhelper_args
+from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rdynload import DLLHANDLE
 from rpython.rlib import rawrefcount
 import sys
@@ -85,7 +85,7 @@
                 pyobj_dealloc_action = PyObjDeallocAction(space)
                 self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
 
-                def _rawrefcount_tp_traverse(pyobj_ptr, callback, args):
+                def _tp_traverse(pyobj_ptr, callback, args):
                     from pypy.module.cpyext.api import PyObject
                     from pypy.module.cpyext.typeobjectdefs import visitproc
                     # convert to pointers with correct types (PyObject)
@@ -98,8 +98,8 @@
                     if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse:
                         pyobj.c_ob_type.c_tp_traverse(pyobj, callback_ptr,
                                                       args)
-                self.tp_traverse = (lambda o, v, a:
-                                    _rawrefcount_tp_traverse(o, v, a))
+
+                self.tp_traverse = (lambda o, v, a:_tp_traverse(o, v, a))
 
     def build_api(self):
         """NOT_RPYTHON
@@ -130,13 +130,14 @@
             if space.config.translation.gc != "boehm":
                 # This must be called in RPython, the untranslated version
                 # does something different. Sigh.
-                pypyobj_list = self.C._PyPy_InitPyObjList()
+                pypyobj_list = self.C._PyPy_init_pyobj_list()
                 rawrefcount.init(
                     llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
-                    self.dealloc_trigger),
+                             self.dealloc_trigger),
                     llhelper(rawrefcount.RAWREFCOUNT_TRAVERSE,
-                    self.tp_traverse),
-                    pypyobj_list)
+                             self.tp_traverse),
+                    pypyobj_list,
+                    self.C._PyPy_gc_as_pyobj, self.C._PyPy_pyobj_as_gc)
             self.builder.attach_all(space)
 
         setup_new_method_def(space)
diff --git a/pypy/module/cpyext/test/test_bytesobject.py 
b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -9,8 +9,7 @@
     PyString_ConcatAndDel, PyString_Format, PyString_InternFromString,
     PyString_AsEncodedObject, PyString_AsDecodedObject, _PyString_Eq,
     _PyString_Join)
-from pypy.module.cpyext.api import PyObjectP, PyObject, Py_ssize_tP, \
-    generic_cpy_call
+from pypy.module.cpyext.api import PyObjectP, PyObject, Py_ssize_tP, 
generic_cpy_call
 from pypy.module.cpyext.pyobject import decref, from_ref, make_ref
 from pypy.module.cpyext.buffer import PyObject_AsCharBuffer
 from pypy.module.cpyext.api import PyTypeObjectPtr
diff --git a/pypy/module/cpyext/test/test_cpyext.py 
b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -214,6 +214,8 @@
 def debug_collect(space):
     rawrefcount._collect()
 
+def print_pyobj_list(space):
+    rawrefcount._print_pyobj_list()
 
 class AppTestCpythonExtensionBase(LeakCheckingTest):
 
@@ -225,6 +227,7 @@
         if not cls.runappdirect:
             cls.sys_info = get_cpyext_info(space)
             cls.w_debug_collect = space.wrap(interp2app(debug_collect))
+            cls.w_print_pyobj_list = space.wrap(interp2app(print_pyobj_list))
             cls.preload_builtins(space)
         else:
             def w_import_module(self, name, init=None, body='', filename=None,
@@ -319,7 +322,6 @@
             self.record_imported_module(name)
             return w_result
 
-
         @unwrap_spec(mod='text', name='text')
         def load_module(space, mod, name):
             return self.sys_info.load_module(mod, name)
@@ -926,3 +928,179 @@
              '''
              ),
         ])
+
+    def test_gc_pyobj_list(self):
+        """
+        Test if Py_GC_Track and Py_GC_Untrack are adding and removing container
+        objects from the list of all garbage-collected PyObjects.
+        """
+        if self.runappdirect:
+            skip('cannot import module with undefined functions')
+
+        # TODO: remove unnecessary stuff, add tests for gc_untrack, add asserts
+        init = """
+        if (Py_IsInitialized()) {
+            PyObject* m;
+            if (PyType_Ready(&CycleType) < 0)
+                return;
+            m = Py_InitModule("cycle", module_methods);
+            if (m == NULL)
+                return;
+            Py_INCREF(&CycleType);
+            PyModule_AddObject(m, "Cycle", (PyObject *)&CycleType);
+        }
+        """
+        body = """
+        #include <Python.h>
+        #include "structmember.h"
+        typedef struct {
+            PyObject_HEAD
+            PyObject *next;
+            PyObject *val;
+        } Cycle;
+        static PyTypeObject CycleType;
+        static int Cycle_traverse(Cycle *self, visitproc visit, void *arg)
+        {
+            int vret;
+            if (self->next) {
+                vret = visit(self->next, arg);
+                if (vret != 0)
+                    return vret;
+            }
+            if (self->val) {
+                vret = visit(self->val, arg);
+                if (vret != 0)
+                    return vret;
+            }
+            return 0;
+        }
+        static int Cycle_clear(Cycle *self)
+        {
+            PyObject *tmp;
+            tmp = self->next;
+            self->next = NULL;
+            Py_XDECREF(tmp);
+            tmp = self->val;
+            self->val = NULL;
+            Py_XDECREF(tmp);
+            return 0;
+        }
+        static void Cycle_dealloc(Cycle* self)
+        {
+            Cycle_clear(self);
+            Py_TYPE(self)->tp_free((PyObject*)self);
+        }
+        static PyObject* Cycle_new(PyTypeObject *type, PyObject *args,
+                                   PyObject *kwds)
+        {
+            Cycle *self;
+            self = (Cycle *)type->tp_alloc(type, 0);
+            if (self != NULL) {
+                self->next = PyString_FromString("");
+                if (self->next == NULL) {
+                    Py_DECREF(self);
+                    return NULL;
+                }
+            }
+            PyObject_GC_Track(self);
+            return (PyObject *)self;
+        }
+        static int Cycle_init(Cycle *self, PyObject *args, PyObject *kwds)
+        {
+            PyObject *next=NULL, *tmp;
+            static char *kwlist[] = {"next", NULL};
+            if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
+                                              &next))
+                return -1;
+            if (next) {
+                tmp = self->next;
+                Py_INCREF(next);
+                self->next = next;
+                Py_XDECREF(tmp);
+            }
+            return 0;
+        }
+        static PyMemberDef Cycle_members[] = {
+            {"next", T_OBJECT_EX, offsetof(Cycle, next), 0, "next"},
+            {"val", T_OBJECT_EX, offsetof(Cycle, val), 0, "val"},
+            {NULL}  /* Sentinel */
+        };
+        static PyMethodDef Cycle_methods[] = {
+            {NULL}  /* Sentinel */
+        };
+        static PyTypeObject CycleType = {
+            PyVarObject_HEAD_INIT(NULL, 0)
+            "Cycle.Cycle",             /* tp_name */
+            sizeof(Cycle),             /* tp_basicsize */
+            0,                         /* tp_itemsize */
+            (destructor)Cycle_dealloc, /* tp_dealloc */
+            0,                         /* tp_print */
+            0,                         /* tp_getattr */
+            0,                         /* tp_setattr */
+            0,                         /* tp_compare */
+            0,                         /* tp_repr */
+            0,                         /* tp_as_number */
+            0,                         /* tp_as_sequence */
+            0,                         /* tp_as_mapping */
+            0,                         /* tp_hash */
+            0,                         /* tp_call */
+            0,                         /* tp_str */
+            0,                         /* tp_getattro */
+            0,                         /* tp_setattro */
+            0,                         /* tp_as_buffer */
+            Py_TPFLAGS_DEFAULT |
+                Py_TPFLAGS_BASETYPE |
+                Py_TPFLAGS_HAVE_GC,    /* tp_flags */
+            "Cycle objects",           /* tp_doc */
+            (traverseproc)Cycle_traverse,   /* tp_traverse */
+            (inquiry)Cycle_clear,           /* tp_clear */
+            0,                         /* tp_richcompare */
+            0,                         /* tp_weaklistoffset */
+            0,                         /* tp_iter */
+            0,                         /* tp_iternext */
+            Cycle_methods,             /* tp_methods */
+            Cycle_members,             /* tp_members */
+            0,                         /* tp_getset */
+            0,                         /* tp_base */
+            0,                         /* tp_dict */
+            0,                         /* tp_descr_get */
+            0,                         /* tp_descr_set */
+            0,                         /* tp_dictoffset */
+            (initproc)Cycle_init,      /* tp_init */
+            0,                         /* tp_alloc */
+            Cycle_new,                 /* tp_new */
+        };
+        
+        extern PyGC_Head *_pypy_rawrefcount_pyobj_list;
+
+         static PyObject * Cycle_Create(Cycle *self, PyObject *val)
+         {
+             Cycle *c = PyObject_GC_New(Cycle, &CycleType);
+             if (c == NULL)
+                 return NULL;
+             c->next = val;
+
+             // TODO: check if _pypy_rawrefcount_pyobj_list contains c
+
+             return (PyObject *)c;
+         }
+         static PyMethodDef module_methods[] = {
+             {"create", (PyCFunction)Cycle_Create, METH_OLDARGS, ""},
+             {NULL}  /* Sentinel */
+         };
+        """
+        module = self.import_module(name='cycle', init=init, body=body)
+
+        class Example(object):
+            def __init__(self, val):
+                self.val = val
+
+        c = module.create(Example(41))
+
+        self.print_pyobj_list()
+        c = module.create(Example(42))
+        self.print_pyobj_list()
+
+        # TODO: fix rawrefcount, so that the Cycle objects are properly added
+        #       to the ALLOCATED list of leakfinder or alternatively not freed
+        #       by collect
diff --git a/pypy/module/cpyext/test/test_cpyext_gc.py 
b/pypy/module/cpyext/test/test_cpyext_gc.py
deleted file mode 100644
--- a/pypy/module/cpyext/test/test_cpyext_gc.py
+++ /dev/null
@@ -1,801 +0,0 @@
-import sys
-import weakref
-
-import pytest
-
-from pypy.tool.cpyext.extbuild import (
-    SystemCompilationInfo, HERE, get_sys_info_app)
-from pypy.interpreter.gateway import unwrap_spec, interp2app
-from rpython.rtyper.lltypesystem import lltype, ll2ctypes
-from pypy.module.cpyext import api
-from pypy.module.cpyext.state import State
-from rpython.tool.identity_dict import identity_dict
-from rpython.tool import leakfinder
-from rpython.rlib import rawrefcount
-from rpython.tool.udir import udir
-
-only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names"
-
-@api.cpython_api([], api.PyObject)
-def PyPy_Crash1(space):
-    1/0
-
-@api.cpython_api([], lltype.Signed, error=-1)
-def PyPy_Crash2(space):
-    1/0
-
-class SpaceCompiler(SystemCompilationInfo):
-    """Extension compiler for regular (untranslated PyPy) mode"""
-    def __init__(self, space, *args, **kwargs):
-        self.space = space
-        SystemCompilationInfo.__init__(self, *args, **kwargs)
-
-    def load_module(self, mod, name):
-        space = self.space
-        api.load_extension_module(space, mod, name)
-        return space.getitem(
-            space.sys.get('modules'), space.wrap(name))
-
-
-def get_cpyext_info(space):
-    from pypy.module.imp.importing import get_so_extension
-    state = space.fromcache(State)
-    api_library = state.api_lib
-    if sys.platform == 'win32':
-        libraries = [api_library]
-        # '%s' undefined; assuming extern returning int
-        compile_extra = ["/we4013"]
-        # prevent linking with PythonXX.lib
-        w_maj, w_min = space.fixedview(space.sys.get('version_info'), 5)[:2]
-        link_extra = ["/NODEFAULTLIB:Python%d%d.lib" %
-            (space.int_w(w_maj), space.int_w(w_min))]
-    else:
-        libraries = []
-        if sys.platform.startswith('linux'):
-            compile_extra = [
-                "-Werror", "-g", "-O0", "-Wp,-U_FORTIFY_SOURCE", "-fPIC"]
-            link_extra = ["-g"]
-        else:
-            compile_extra = link_extra = None
-    return SpaceCompiler(space,
-        builddir_base=udir,
-        include_extra=api.include_dirs,
-        compile_extra=compile_extra,
-        link_extra=link_extra,
-        extra_libs=libraries,
-        ext=get_so_extension(space))
-
-
-def freeze_refcnts(self):
-    rawrefcount._dont_free_any_more()
-    return #ZZZ
-    state = self.space.fromcache(RefcountState)
-    self.frozen_refcounts = {}
-    for w_obj, obj in state.py_objects_w2r.iteritems():
-        self.frozen_refcounts[w_obj] = obj.c_ob_refcnt
-    #state.print_refcounts()
-    self.frozen_ll2callocations = set(ll2ctypes.ALLOCATED.values())
-
-class LeakCheckingTest(object):
-    """Base class for all cpyext tests."""
-    spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array',
-                                   'itertools', 'time', 'binascii',
-                                   'micronumpy', 'mmap'
-                                   ])
-
-    enable_leak_checking = True
-
-    @staticmethod
-    def cleanup_references(space):
-        return #ZZZ
-        state = space.fromcache(RefcountState)
-
-        import gc; gc.collect()
-        # Clear all lifelines, objects won't resurrect
-        for w_obj, obj in state.lifeline_dict._dict.items():
-            if w_obj not in state.py_objects_w2r:
-                state.lifeline_dict.set(w_obj, None)
-            del obj
-        import gc; gc.collect()
-
-
-        for w_obj in state.non_heaptypes_w:
-            w_obj.c_ob_refcnt -= 1
-        state.non_heaptypes_w[:] = []
-        state.reset_borrowed_references()
-
-    def check_and_print_leaks(self):
-        rawrefcount._collect()
-        # check for sane refcnts
-        import gc
-
-        if 1:  #ZZZ  not self.enable_leak_checking:
-            leakfinder.stop_tracking_allocations(check=False)
-            return False
-
-        leaking = False
-        state = self.space.fromcache(RefcountState)
-        gc.collect()
-        lost_objects_w = identity_dict()
-        lost_objects_w.update((key, None) for key in 
self.frozen_refcounts.keys())
-
-        for w_obj, obj in state.py_objects_w2r.iteritems():
-            base_refcnt = self.frozen_refcounts.get(w_obj)
-            delta = obj.c_ob_refcnt
-            if base_refcnt is not None:
-                delta -= base_refcnt
-                lost_objects_w.pop(w_obj)
-            if delta != 0:
-                leaking = True
-                print >>sys.stderr, "Leaking %r: %i references" % (w_obj, 
delta)
-                try:
-                    weakref.ref(w_obj)
-                except TypeError:
-                    lifeline = None
-                else:
-                    lifeline = state.lifeline_dict.get(w_obj)
-                if lifeline is not None:
-                    refcnt = lifeline.pyo.c_ob_refcnt
-                    if refcnt > 0:
-                        print >>sys.stderr, "\tThe object also held by C code."
-                    else:
-                        referrers_repr = []
-                        for o in gc.get_referrers(w_obj):
-                            try:
-                                repr_str = repr(o)
-                            except TypeError as e:
-                                repr_str = "%s (type of o is %s)" % (str(e), 
type(o))
-                            referrers_repr.append(repr_str)
-                        referrers = ", ".join(referrers_repr)
-                        print >>sys.stderr, "\tThe object is referenced by 
these objects:", \
-                                referrers
-        for w_obj in lost_objects_w:
-            print >>sys.stderr, "Lost object %r" % (w_obj, )
-            leaking = True
-        # the actual low-level leak checking is done by pypy.tool.leakfinder,
-        # enabled automatically by pypy.conftest.
-        return leaking
-
-class AppTestApi(LeakCheckingTest):
-    def setup_class(cls):
-        from rpython.rlib.clibffi import get_libc_name
-        if cls.runappdirect:
-            cls.libc = get_libc_name()
-        else:
-            cls.w_libc = cls.space.wrap(get_libc_name())
-
-    def setup_method(self, meth):
-        if not self.runappdirect:
-            freeze_refcnts(self)
-
-    def teardown_method(self, meth):
-        if self.runappdirect:
-            return
-        self.space.getexecutioncontext().cleanup_cpyext_state()
-        self.cleanup_references(self.space)
-        # XXX: like AppTestCpythonExtensionBase.teardown_method:
-        # find out how to disable check_and_print_leaks() if the
-        # test failed
-        assert not self.check_and_print_leaks(), (
-            "Test leaks or loses object(s).  You should also check if "
-            "the test actually passed in the first place; if it failed "
-            "it is likely to reach this place.")
-
-
-def _unwrap_include_dirs(space, w_include_dirs):
-    if w_include_dirs is None:
-        return None
-    else:
-        return [space.str_w(s) for s in space.listview(w_include_dirs)]
-
-def debug_collect(space):
-    rawrefcount._collect()
-
-class AppTestCpythonExtensionBase(LeakCheckingTest):
-
-    def setup_class(cls):
-        space = cls.space
-        cls.w_here = space.wrap(str(HERE))
-        cls.w_udir = space.wrap(str(udir))
-        cls.w_runappdirect = space.wrap(cls.runappdirect)
-        if not cls.runappdirect:
-            cls.sys_info = get_cpyext_info(space)
-            space.getbuiltinmodule("cpyext")
-            # 'import os' to warm up reference counts
-            w_import = space.builtin.getdictvalue(space, '__import__')
-            space.call_function(w_import, space.wrap("os"))
-            #state = cls.space.fromcache(RefcountState) ZZZ
-            #state.non_heaptypes_w[:] = []
-            cls.w_debug_collect = space.wrap(interp2app(debug_collect))
-        else:
-            def w_import_module(self, name, init=None, body='', filename=None,
-                    include_dirs=None, PY_SSIZE_T_CLEAN=False):
-                from extbuild import get_sys_info_app
-                sys_info = get_sys_info_app(self.udir)
-                return sys_info.import_module(
-                    name, init=init, body=body, filename=filename,
-                    include_dirs=include_dirs,
-                    PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
-            cls.w_import_module = w_import_module
-
-            def w_import_extension(self, modname, functions, prologue="",
-                include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
-                from extbuild import get_sys_info_app
-                sys_info = get_sys_info_app(self.udir)
-                return sys_info.import_extension(
-                    modname, functions, prologue=prologue,
-                    include_dirs=include_dirs, more_init=more_init,
-                    PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
-            cls.w_import_extension = w_import_extension
-
-            def w_compile_module(self, name,
-                    source_files=None, source_strings=None):
-                from extbuild import get_sys_info_app
-                sys_info = get_sys_info_app(self.udir)
-                return sys_info.compile_extension_module(name,
-                    source_files=source_files, source_strings=source_strings)
-            cls.w_compile_module = w_compile_module
-
-            def w_load_module(self, mod, name):
-                from extbuild import get_sys_info_app
-                sys_info = get_sys_info_app(self.udir)
-                return sys_info.load_module(mod, name)
-            cls.w_load_module = w_load_module
-
-            def w_debug_collect(self):
-                import gc
-                gc.collect()
-                gc.collect()
-                gc.collect()
-            cls.w_debug_collect = w_debug_collect
-
-
-    def record_imported_module(self, name):
-        """
-        Record a module imported in a test so that it can be cleaned up in
-        teardown before the check for leaks is done.
-
-        name gives the name of the module in the space's sys.modules.
-        """
-        self.imported_module_names.append(name)
-
-    def setup_method(self, func):
-        if self.runappdirect:
-            return
-
-        @unwrap_spec(name='text')
-        def compile_module(space, name,
-                           w_source_files=None,
-                           w_source_strings=None):
-            """
-            Build an extension module linked against the cpyext api library.
-            """
-            if not space.is_none(w_source_files):
-                source_files = space.listview_bytes(w_source_files)
-            else:
-                source_files = None
-            if not space.is_none(w_source_strings):
-                source_strings = space.listview_bytes(w_source_strings)
-            else:
-                source_strings = None
-            pydname = self.sys_info.compile_extension_module(
-                name,
-                source_files=source_files,
-                source_strings=source_strings)
-
-            # hackish, but tests calling compile_module() always end up
-            # importing the result
-            self.record_imported_module(name)
-
-            return space.wrap(pydname)
-
-        @unwrap_spec(name='text', init='text_or_none', body='text',
-                     filename='fsencode_or_none', PY_SSIZE_T_CLEAN=bool)
-        def import_module(space, name, init=None, body='',
-                          filename=None, w_include_dirs=None,
-                          PY_SSIZE_T_CLEAN=False):
-            include_dirs = _unwrap_include_dirs(space, w_include_dirs)
-            w_result = self.sys_info.import_module(
-                name, init, body, filename, include_dirs, PY_SSIZE_T_CLEAN)
-            self.record_imported_module(name)
-            return w_result
-
-
-        @unwrap_spec(mod='text', name='text')
-        def load_module(space, mod, name):
-            return self.sys_info.load_module(mod, name)
-
-        @unwrap_spec(modname='text', prologue='text',
-                             more_init='text', PY_SSIZE_T_CLEAN=bool)
-        def import_extension(space, modname, w_functions, prologue="",
-                             w_include_dirs=None, more_init="", 
PY_SSIZE_T_CLEAN=False):
-            functions = space.unwrap(w_functions)
-            include_dirs = _unwrap_include_dirs(space, w_include_dirs)
-            w_result = self.sys_info.import_extension(
-                modname, functions, prologue, include_dirs, more_init,
-                PY_SSIZE_T_CLEAN)
-            self.record_imported_module(modname)
-            return w_result
-
-        # A list of modules which the test caused to be imported (in
-        # self.space).  These will be cleaned up automatically in teardown.
-        self.imported_module_names = []
-
-        wrap = self.space.wrap
-        self.w_compile_module = wrap(interp2app(compile_module))
-        self.w_load_module = wrap(interp2app(load_module))
-        self.w_import_module = wrap(interp2app(import_module))
-        self.w_import_extension = wrap(interp2app(import_extension))
-
-        # create the file lock before we count allocations
-        self.space.call_method(self.space.sys.get("stdout"), "flush")
-
-        freeze_refcnts(self)
-        #self.check_and_print_leaks()
-
-    def unimport_module(self, name):
-        """
-        Remove the named module from the space's sys.modules.
-        """
-        w_modules = self.space.sys.get('modules')
-        w_name = self.space.wrap(name)
-        self.space.delitem(w_modules, w_name)
-
-    def teardown_method(self, func):
-        if self.runappdirect:
-            return
-        for name in self.imported_module_names:
-            self.unimport_module(name)
-        self.space.getexecutioncontext().cleanup_cpyext_state()
-        self.cleanup_references(self.space)
-        # XXX: find out how to disable check_and_print_leaks() if the
-        # test failed...
-        assert not self.check_and_print_leaks(), (
-            "Test leaks or loses object(s).  You should also check if "
-            "the test actually passed in the first place; if it failed "
-            "it is likely to reach this place.")
-
-def collect(space):
-    import gc
-    rawrefcount._collect()
-    gc.collect(2)
-
-def print_pyobj_list(space):
-    rawrefcount._print_pyobj_list()
-
-class AppTestCpythonExtensionCycleGC(AppTestCpythonExtensionBase):
-
-    def setup_method(self, func):
-        if self.runappdirect:
-            return
-
-        @unwrap_spec(methods='text')
-        def import_cycle_module(space, methods):
-            init = """
-            if (Py_IsInitialized()) {
-                PyObject* m;
-                if (PyType_Ready(&CycleType) < 0)
-                    return;
-                m = Py_InitModule("cycle", module_methods);
-                if (m == NULL)
-                    return;
-                Py_INCREF(&CycleType);
-                PyModule_AddObject(m, "Cycle", (PyObject *)&CycleType);
-            }
-            """
-            body = """
-            #include <Python.h>
-            #include "structmember.h"
-            typedef struct {
-                PyObject_HEAD
-                PyObject *next;
-                PyObject *val;
-            } Cycle;
-            static PyTypeObject CycleType;
-            static int Cycle_traverse(Cycle *self, visitproc visit, void *arg)
-            {
-                int vret;
-                if (self->next) {
-                    vret = visit(self->next, arg);
-                    if (vret != 0)
-                        return vret;
-                }
-                if (self->val) {
-                    vret = visit(self->val, arg);
-                    if (vret != 0)
-                        return vret;
-                }
-                return 0;
-            }
-            static int Cycle_clear(Cycle *self)
-            {
-                PyObject *tmp;
-                tmp = self->next;
-                self->next = NULL;
-                Py_XDECREF(tmp);
-                tmp = self->val;
-                self->val = NULL;
-                Py_XDECREF(tmp);
-                return 0;
-            }
-            static void Cycle_dealloc(Cycle* self)
-            {
-                Cycle_clear(self);
-                Py_TYPE(self)->tp_free((PyObject*)self);
-            }
-            static PyObject* Cycle_new(PyTypeObject *type, PyObject *args,
-                                       PyObject *kwds)
-            {
-                Cycle *self;
-                self = (Cycle *)type->tp_alloc(type, 0);
-                if (self != NULL) {
-                    self->next = PyString_FromString("");
-                    if (self->next == NULL) {
-                        Py_DECREF(self);
-                        return NULL;
-                    }
-                }
-                PyObject_GC_Track(self);
-                return (PyObject *)self;
-            }
-            static int Cycle_init(Cycle *self, PyObject *args, PyObject *kwds)
-            {
-                PyObject *next=NULL, *tmp;
-                static char *kwlist[] = {"next", NULL};
-                if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
-                                                  &next))
-                    return -1;
-                if (next) {
-                    tmp = self->next;
-                    Py_INCREF(next);
-                    self->next = next;
-                    Py_XDECREF(tmp);
-                }
-                return 0;
-            }
-            static PyMemberDef Cycle_members[] = {
-                {"next", T_OBJECT_EX, offsetof(Cycle, next), 0, "next"},
-                {"val", T_OBJECT_EX, offsetof(Cycle, val), 0, "val"},
-                {NULL}  /* Sentinel */
-            };
-            static PyMethodDef Cycle_methods[] = {
-                {NULL}  /* Sentinel */
-            };
-            static PyTypeObject CycleType = {
-                PyVarObject_HEAD_INIT(NULL, 0)
-                "Cycle.Cycle",             /* tp_name */
-                sizeof(Cycle),             /* tp_basicsize */
-                0,                         /* tp_itemsize */
-                (destructor)Cycle_dealloc, /* tp_dealloc */
-                0,                         /* tp_print */
-                0,                         /* tp_getattr */
-                0,                         /* tp_setattr */
-                0,                         /* tp_compare */
-                0,                         /* tp_repr */
-                0,                         /* tp_as_number */
-                0,                         /* tp_as_sequence */
-                0,                         /* tp_as_mapping */
-                0,                         /* tp_hash */
-                0,                         /* tp_call */
-                0,                         /* tp_str */
-                0,                         /* tp_getattro */
-                0,                         /* tp_setattro */
-                0,                         /* tp_as_buffer */
-                Py_TPFLAGS_DEFAULT |
-                    Py_TPFLAGS_BASETYPE |
-                    Py_TPFLAGS_HAVE_GC,    /* tp_flags */
-                "Cycle objects",           /* tp_doc */
-                (traverseproc)Cycle_traverse,   /* tp_traverse */
-                (inquiry)Cycle_clear,           /* tp_clear */
-                0,                         /* tp_richcompare */
-                0,                         /* tp_weaklistoffset */
-                0,                         /* tp_iter */
-                0,                         /* tp_iternext */
-                Cycle_methods,             /* tp_methods */
-                Cycle_members,             /* tp_members */
-                0,                         /* tp_getset */
-                0,                         /* tp_base */
-                0,                         /* tp_dict */
-                0,                         /* tp_descr_get */
-                0,                         /* tp_descr_set */
-                0,                         /* tp_dictoffset */
-                (initproc)Cycle_init,      /* tp_init */
-                0,                         /* tp_alloc */
-                Cycle_new,                 /* tp_new */
-            };
-            """
-            w_result = self.sys_info.import_module("cycle", init,
-                                                   body + methods,
-                                                   None, None, False)
-            return w_result
-
-        self.imported_module_names = []
-
-        wrap = self.space.wrap
-        self.w_import_cycle_module = wrap(interp2app(import_cycle_module))
-        self.w_collect = wrap(interp2app(collect))
-        self.w_print_pyobj_list = wrap(interp2app(print_pyobj_list))
-
-    # def test_free_self_reference_cycle_child_pypyobj(self):
-    #     cycle = self.import_cycle_module("""
-    #         static Cycle *c;
-    #         static PyObject * Cycle_cc(Cycle *self, PyObject *val)
-    #         {
-    #             c = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c == NULL)
-    #                 return NULL;
-    #             Py_INCREF(val);
-    #             c->val = val;                // set value
-    #             Py_INCREF(c);
-    #             c->next = (PyObject *)c;     // create self reference
-    #             Py_INCREF(Py_None);
-    #             return Py_None;
-    #         }
-    #         static PyObject * Cycle_cd(Cycle *self)
-    #         {
-    #             Py_DECREF(c);                // throw cycle away
-    #             Py_INCREF(Py_None);
-    #             return Py_None;
-    #         }
-    #         static PyMethodDef module_methods[] = {
-    #             {"createCycle", (PyCFunction)Cycle_cc, METH_OLDARGS, ""},
-    #             {"discardCycle", (PyCFunction)Cycle_cd, METH_NOARGS, ""},
-    #             {NULL}  /* Sentinel */
-    #         };
-    #         """)
-    #
-    #     class Example(object):
-    #         del_called = -1
-    #
-    #         def __init__(self, val):
-    #             self.val = val
-    #             Example.del_called = 0
-    #
-    #         def __del__(self):
-    #             Example.del_called = self.val
-    #
-    #     # don't keep any reference in pypy
-    #     cycle.createCycle(Example(42))
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     cycle.discardCycle()
-    #     self.collect()
-    #     assert Example.del_called == 42
-    #
-    #     # keep a temporary reference in pypy
-    #     e = Example(43)
-    #     cycle.createCycle(e)
-    #     cycle.discardCycle()
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     e = None
-    #     self.collect()
-    #     assert Example.del_called == 43
-    #
-    #     # keep a reference in pypy, free afterwards
-    #     e = Example(44)
-    #     cycle.createCycle(e)
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     e = None
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     cycle.discardCycle()
-    #     self.collect()
-    #     assert Example.del_called == 44
-    #
-    # def test_free_self_reference_cycle_parent_pypyobj(self):
-    #     # create and return a second object which references the cycle, 
because
-    #     # otherwise we will end up with a cycle that spans across cpy/pypy,
-    #     # which we don't want to test here
-    #     cycle = self.import_cycle_module("""
-    #         static PyObject * Cycle_cc(Cycle *self, PyObject *val)
-    #         {
-    #             Cycle *c = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c == NULL)
-    #                 return NULL;
-    #             Cycle *c2 = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c2 == NULL)
-    #                 return NULL;
-    #             Py_INCREF(val);
-    #             c2->val = val;                // set value
-    #             Py_INCREF(c2);
-    #             c2->next = (PyObject *)c2;    // create self reference
-    #             c->next = (PyObject *)c2;
-    #             return (PyObject *)c;         // return other object
-    #         }
-    #         static PyMethodDef module_methods[] = {
-    #             {"createCycle", (PyCFunction)Cycle_cc, METH_OLDARGS, ""},
-    #             {NULL}  /* Sentinel */
-    #         };
-    #         """)
-    #
-    #     class Example(object):
-    #         del_called = -1
-    #
-    #         def __init__(self, val):
-    #             self.val = val
-    #             Example.del_called = 0
-    #
-    #         def __del__(self):
-    #             Example.del_called = self.val
-    #
-    #     c = cycle.createCycle(Example(42))
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     c = None
-    #     self.collect()
-    #     assert Example.del_called == 42
-    #
-    # def test_free_simple_cycle_child_pypyobj(self):
-    #     cycle = self.import_cycle_module("""
-    #         static Cycle *c;
-    #         static PyObject * Cycle_cc(Cycle *self, PyObject *val)
-    #         {
-    #             c = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c == NULL)
-    #                 return NULL;
-    #             Cycle *c2 = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c2 == NULL)
-    #                 return NULL;
-    #             Py_INCREF(val);
-    #             c->val = val;                // set value
-    #             c->next = (PyObject *)c2;
-    #             Py_INCREF(c);
-    #             c2->next = (PyObject *)c;    // simple cycle across two 
objects
-    #             Py_INCREF(Py_None);
-    #             return Py_None;
-    #         }
-    #         static PyObject * Cycle_cd(Cycle *self)
-    #         {
-    #             Py_DECREF(c);                // throw cycle away
-    #             Py_INCREF(Py_None);
-    #             return Py_None;
-    #         }
-    #         static PyMethodDef module_methods[] = {
-    #             {"createCycle", (PyCFunction)Cycle_cc, METH_OLDARGS, ""},
-    #             {"discardCycle", (PyCFunction)Cycle_cd, METH_NOARGS, ""},
-    #             {NULL}  /* Sentinel */
-    #         };
-    #         """)
-    #
-    #     class Example(object):
-    #         del_called = -1
-    #
-    #         def __init__(self, val):
-    #             self.val = val
-    #             Example.del_called = 0
-    #
-    #         def __del__(self):
-    #             Example.del_called = self.val
-    #
-    #     # don't keep any reference in pypy
-    #     cycle.createCycle(Example(42))
-    #     self.collect()
-    #     cycle.discardCycle()
-    #     assert Example.del_called == 0
-    #     self.collect()
-    #     assert Example.del_called == 42
-    #
-    #     # keep a temporary reference in pypy
-    #     e = Example(43)
-    #     cycle.createCycle(e)
-    #     cycle.discardCycle()
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     e = None
-    #     self.collect()
-    #     assert Example.del_called == 43
-    #
-    #     # keep a reference in pypy, free afterwards
-    #     e = Example(44)
-    #     cycle.createCycle(e)
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     e = None
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     cycle.discardCycle()
-    #     self.collect()
-    #     assert Example.del_called == 44
-    #
-    #
-    # def test_free_complex_cycle_child_pypyobj(self):
-    #     cycle = self.import_cycle_module("""
-    #         static PyObject * Cycle_cc(Cycle *self, PyObject *val)
-    #         {
-    #             Cycle *c = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c == NULL)
-    #                 return NULL;
-    #             Cycle *c2 = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c2 == NULL)
-    #                 return NULL;
-    #             Cycle *c3 = PyObject_GC_New(Cycle, &CycleType);
-    #             if (c3 == NULL)
-    #                 return NULL;
-    #             Py_INCREF(val);
-    #             c->val = val;                // set value
-    #             Py_INCREF(val);
-    #             c3->val = val;                // set value
-    #             Py_INCREF(c2);
-    #             c->next = (PyObject *)c2;
-    #             Py_INCREF(c);
-    #             c2->next = (PyObject *)c;    // inner cycle
-    #             Py_INCREF(c3);
-    #             c2->val = (PyObject *)c3;
-    #             Py_INCREF(c);
-    #             c3->next = (PyObject *)c;     // outer cycle
-    #             Py_DECREF(c);
-    #             Py_DECREF(c2);
-    #             Py_DECREF(c3);               // throw all objects away
-    #             Py_INCREF(Py_None);
-    #             return Py_None;
-    #         }
-    #         static PyMethodDef module_methods[] = {
-    #             {"createCycle", (PyCFunction)Cycle_cc, METH_OLDARGS, ""},
-    #             {NULL}  /* Sentinel */
-    #         };
-    #         """)
-    #
-    #     class Example(object):
-    #         del_called = -1
-    #
-    #         def __init__(self, val):
-    #             self.val = val
-    #             Example.del_called = 0
-    #
-    #         def __del__(self):
-    #             Example.del_called = self.val
-    #
-    #     # don't keep any reference in pypy
-    #     cycle.createCycle(Example(42))
-    #     assert Example.del_called == 0
-    #     self.collect()
-    #     assert Example.del_called == 42
-    #
-    #     # keep a temporary reference in pypy
-    #     e = Example(43)
-    #     cycle.createCycle(e)
-    #     e = None
-    #     assert Example.del_called == 0
-    #     self.collect()
-    #     assert Example.del_called == 43
-    #
-    #     # keep a reference in pypy, free afterwards
-    #     e = Example(44)
-    #     cycle.createCycle(e)
-    #     self.collect()
-    #     assert Example.del_called == 0
-    #     e = None
-    #     self.collect()
-    #     assert Example.del_called == 44
-
-    def test_objects_in_global_list(self):
-        cycle = self.import_cycle_module("""
-            static PyObject * Cycle_Create(Cycle *self, PyObject *val)
-            {
-                Cycle *c = PyObject_GC_New(Cycle, &CycleType);
-                if (c == NULL)
-                    return NULL;
-                c->next = val;
-                return (PyObject *)c;
-            }
-            static PyMethodDef module_methods[] = {
-                {"create", (PyCFunction)Cycle_Create, METH_OLDARGS, ""},
-                {NULL}  /* Sentinel */
-            };
-            """)
-
-        class Example(object):
-            def __init__(self, val):
-                self.val = val
-
-        c = cycle.create(Example(41))
-
-        self.print_pyobj_list()
-        c = cycle.create(Example(42))
-        self.print_pyobj_list()
-
-        # TODO: fix rawrefcount, so that the Cycle objects are properly added
-        #       to the ALLOCATED list of leakfinder or alternatively not freed
-        #       by collect
diff --git a/rpython/config/translationoption.py 
b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -106,11 +106,11 @@
                                 ("translation.backend", "c")],
                     }),
     ChoiceOption("cpyextgc", "Garbage Collection Strategy for cpyext",
-                 ["boehm", "ref", "ref_trialdel", "none"],
-                 default="ref",
+                 ["boehm", "trialdeletion", "none"],
+                 default="trialdeletion",
                  requires={
                     "boehm": [("translation.gc", "incminimark")],
-                    "ref_trialdel": [("translation.gc", "incminimark")],
+                    "trialdeletion": [("translation.gc", "incminimark")],
                  },
                  cmdline="--cpyextgc"),
 
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
@@ -2335,7 +2335,7 @@
                 self.visit_all_objects()
                 #
                 if self.rrc_enabled:
-                   self.rrc_major_collection_trace()
+                    self.rrc_major_collection_trace()
                 #
                 ll_assert(not (self.probably_young_objects_with_finalizers
                                .non_empty()),
@@ -2994,43 +2994,48 @@
                               ('c_ob_refcnt', lltype.Signed),
                               ('c_ob_pypy_link', lltype.Signed))
     PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
-    PYOBJ_GC_HDR = lltype.Struct('PyGC_Head',
-                                 ('c_gc_next', rffi.VOIDP),
-                                 ('c_gc_prev', rffi.VOIDP),
-                                 ('c_gc_refs', lltype.Signed))
-    PYOBJ_GC_HDR_PTR = lltype.Ptr(PYOBJ_GC_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_VISIT = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
+                                                   rffi.INT_real))
     RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR,
-                                                       VISIT_FUNCTYPE,
+                                                       RAWREFCOUNT_VISIT,
                                                        rffi.VOIDP],
                                                       lltype.Void))
+    PYOBJ_GC_HDR_PTR = lltype.Ptr(lltype.ForwardReference())
+    PYOBJ_GC_HDR = lltype.Struct('PyGC_Head',
+                                 ('c_gc_next', PYOBJ_GC_HDR_PTR),
+                                 ('c_gc_prev', PYOBJ_GC_HDR_PTR),
+                                 ('c_gc_refs', lltype.Signed))
+    PYOBJ_GC_HDR_PTR.TO.become(PYOBJ_GC_HDR)
+    RAWREFCOUNT_GC_AS_PYOBJ = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
+                                                         PYOBJ_HDR_PTR))
+    RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR],
+                                                         PYOBJ_GC_HDR_PTR))
 
     def _pyobj(self, pyobjaddr):
-            return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
+        return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
     def _pygchdr(self, pygchdraddr):
-            return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
+        return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
 
     def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse,
-                         pyobj_list):
+                         pyobj_list, gc_as_pyobj, pyobj_as_gc):
         # see pypy/doc/discussion/rawrefcount.rst
         if not self.rrc_enabled:
             self.rrc_p_list_young = self.AddressStack()
             self.rrc_p_list_old   = self.AddressStack()
             self.rrc_o_list_young = self.AddressStack()
             self.rrc_o_list_old   = self.AddressStack()
-            self.rrc_buffered     = self.AddressStack()
             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_dealloc_pending = self.AddressStack()
             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()
             self.rrc_pyobjects_to_trace = self.AddressStack()
             self.rrc_pyobj_list = self._pygchdr(pyobj_list)
+            self.rrc_gc_as_pyobj = gc_as_pyobj
+            self.rrc_pyobj_as_gc = pyobj_as_gc
             self.rrc_enabled = True
 
     def check_no_more_rawrefcount_state(self):
@@ -3344,18 +3349,21 @@
                                                 llhelper)
         #
         pyobj = self._pyobj(pyobject)
-        callback_ptr = llhelper(self.VISIT_FUNCTYPE,
+        callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
                                 IncrementalMiniMarkGC._rrc_visit)
         self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
         self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr)
 
     def _rrc_gc_list_init(self, pygclist):
-        pygclist.c_gc_next = rffi.cast(rffi.VOIDP, pygclist)
-        pygclist.c_gc_prev = rffi.cast(rffi.VOIDP, pygclist)
+        pygclist.c_gc_next = pygclist
+        pygclist.c_gc_prev = pygclist
 
     def _rrc_gc_print_list(self):
         debug_print("gc_print_list start!")
-        curr = rffi.cast(self.PYOBJ_GC_HDR_PTR, self.rrc_pyobj_list.c_gc_next)
+        curr = self.rrc_pyobj_list.c_gc_next
         while curr != self.rrc_pyobj_list:
-            debug_print("gc_print_list: ", curr)
-            curr = rffi.cast(self.PYOBJ_GC_HDR_PTR, curr.c_gc_next)
+            currobj = self.rrc_gc_as_pyobj(curr)
+            curr2 = self.rrc_pyobj_as_gc(currobj)
+            debug_print("gc_print_list: ", curr, ", obj:", currobj, ", curr: ",
+                        curr2)
+            curr = curr.c_gc_next
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,15 +1,13 @@
 import py
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
 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
-from rpython.rtyper.lltypesystem import rffi
-from rpython.rtyper.annlowlevel import llhelper
-#from pypy.module.cpyext.api import (PyTypeObject)
-#from pypy.module.cpyext.typeobjectdefs import visitproc, traverseproc
 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
 
 S = lltype.GcForwardReference()
@@ -18,17 +16,6 @@
                          ('prev', lltype.Ptr(S)),
                          ('next', lltype.Ptr(S))))
 
-T = lltype.Ptr(lltype.ForwardReference())
-T.TO.become(lltype.Struct('test',
-                          ('base', PYOBJ_HDR_PTR.TO),
-                          ('next', T),
-                          ('prev', T),
-                          ('value', lltype.Signed)))
-
-#TRAVERSE_FUNCTYPE = rffi.CCallback([PYOBJ_HDR_PTR, visitproc, rffi.VOIDP],
-#                                   rffi.INT_real)
-#t1 = lltype.malloc(PyTypeObject, flavor='raw', immortal=True)
-
 
 class TestRawRefCount(BaseDirectGCTest):
     GCClass = IncrementalMiniMarkGC
@@ -51,14 +38,37 @@
         else:
             rc = REFCNT_FROM_PYPY
         self.trigger = []
-        self.trigger2 = []
+        visit = self.gc._rrc_visit
+        self.pyobj_gc_map = {}
+        self.gc_pyobj_map = {}
+
+        def rawrefcount_tp_traverse(obj, foo, args):
+            print "VISITED!!!!!!!!!!!!!!!!!!!!!1"
+            test = rffi.cast(S, obj)
+            if llmemory.cast_ptr_to_adr(test.next).ptr is not None:
+                next = rffi.cast(PYOBJ_HDR_PTR, test.next)
+                vret = visit(next, args)
+                if vret != 0:
+                    return
+            if llmemory.cast_ptr_to_adr(test.prev).ptr is not None:
+                next = rffi.cast(PYOBJ_HDR_PTR, test.prev)
+                visit(next, args)
+
+        def rawrefcount_gc_as_pyobj(gc):
+            return self.gc_pyobj_map[1] # TODO fix
+
+        def rawrefcount_pyobj_as_gc(pyobj):
+            return self.pyobj_gc_map[1] # TODO fix
+
         self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw',
                                         immortal=True)
-        self.pyobj_list.c_gc_next = rffi.cast(rffi.VOIDP, self.pyobj_list);
-        self.pyobj_list.c_gc_next = rffi.cast(rffi.VOIDP, self.pyobj_list);
+        self.pyobj_list.c_gc_next = self.pyobj_list
+        self.pyobj_list.c_gc_next = self.pyobj_list
         self.gc.rawrefcount_init(lambda: self.trigger.append(1),
-                                 lambda: self.trigger2.append(1),
-                                 llmemory.cast_ptr_to_adr(self.pyobj_list))
+                                 rawrefcount_tp_traverse,
+                                 llmemory.cast_ptr_to_adr(self.pyobj_list),
+                                 rawrefcount_gc_as_pyobj,
+                                 rawrefcount_pyobj_as_gc)
         #
         if create_immortal:
             p1 = lltype.malloc(S, immortal=True)
@@ -78,11 +88,22 @@
             self._collect(major=False)
             p1 = self.stackroots.pop()
         p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1)
-        r1 = lltype.malloc(PYOBJ_HDR_PTR.TO, flavor='raw', 
immortal=create_immortal)
+        r1 = lltype.malloc(PYOBJ_HDR, flavor='raw',
+                           immortal=create_immortal)
         r1.c_ob_refcnt = rc
         r1.c_ob_pypy_link = 0
-        #r1.c_ob_type = lltype.nullptr(PyTypeObject)
         r1addr = llmemory.cast_ptr_to_adr(r1)
+
+        r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
+                             immortal=True)
+        r1gc.c_gc_next = self.pyobj_list
+        r1gc.c_gc_prev = self.pyobj_list
+        self.pyobj_list.c_gc_next = r1gc
+        self.pyobj_list.c_gc_prev = r1gc
+
+        self.pyobj_gc_map[1] = r1gc
+        self.gc_pyobj_map[1] = r1
+
         if is_pyobj:
             assert not is_light
             self.gc.rawrefcount_create_link_pyobj(p1ref, r1addr)
@@ -313,50 +334,24 @@
         self._collect(major=True)
         check_alive(0)
 
-    # def _rawrefcount_cycle_obj(self):
-    #
-    #     def test_tp_traverse(obj, visit, args):
-    #         test = rffi.cast(T, obj)
-    #         vret = 0
-    #         if llmemory.cast_ptr_to_adr(test.next).ptr is not None:
-    #             next = rffi.cast(PYOBJ_HDR_PTR, test.next)
-    #             vret = visit(next, args)
-    #             if vret != 0:
-    #                 return vret
-    #         if llmemory.cast_ptr_to_adr(test.prev).ptr is not None:
-    #             next = rffi.cast(PYOBJ_HDR_PTR, test.prev)
-    #             vret = visit(next, args)
-    #             if vret != 0:
-    #                 return vret
-    #         return vret
-    #
-    #     func_ptr = llhelper(TRAVERSE_FUNCTYPE, test_tp_traverse)
-    #     rffi_func_ptr = rffi.cast(traverseproc, func_ptr)
-    #     t1.c_tp_traverse = rffi_func_ptr
-    #
-    #     r1 = lltype.malloc(T.TO, flavor='raw', immortal=True)
-    #     r1.base.c_ob_pypy_link = 0
-    #     r1.base.c_ob_type = t1
-    #     r1.base.c_ob_refcnt = 1
-    #     return r1
-    #
-    # def test_cycle_self_reference_free(self):
-    #     self.gc.rawrefcount_init(lambda: self.trigger.append(1))
-    #     r1 = self._rawrefcount_cycle_obj()
-    #     r1.next = r1
-    #     self._rawrefcount_buffer_obj(r1)
-    #     self.gc.rrc_collect_cycles()
-    #     assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
-    #
-    # def test_cycle_self_reference_not_free(self):
-    #     self.gc.rawrefcount_init(lambda: self.trigger.append(1))
-    #     r1 = self._rawrefcount_cycle_obj()
-    #     r1.base.c_ob_refcnt += 1
-    #     r1.next = r1
-    #     self._rawrefcount_buffer_obj(r1)
-    #     self.gc.rrc_collect_cycles()
-    #     assert r1.base.c_ob_refcnt & REFCNT_MASK == 2
-    #
+    def test_cycle_self_reference_free(self):
+        p1, p1ref, r1, r1addr, check_alive = (
+            self._rawrefcount_pair(42, create_immortal=True))
+        p1.next = p1
+        check_alive(0)
+        self._collect(major=True)
+        py.test.raises(RuntimeError, "r1.c_ob_refcnt")  # dead
+        py.test.raises(RuntimeError, "p1.x")  # dead
+
+    def test_cycle_self_reference_not_free(self):
+        p1, p1ref, r1, r1addr, check_alive = (
+            self._rawrefcount_pair(42, create_immortal=True))
+        r1.c_ob_refcnt += 1  # the pyobject is kept alive
+        p1.next = p1
+        check_alive(+1)
+        self._collect(major=True)
+        check_alive(+1)
+
     # def test_simple_cycle_free(self):
     #     self.gc.rawrefcount_init(lambda: self.trigger.append(1))
     #     r1 = self._rawrefcount_cycle_obj()
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
@@ -476,7 +476,9 @@
             self.rawrefcount_init_ptr = getfn(
                 GCClass.rawrefcount_init,
                 [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER),
-                 SomePtr(GCClass.RAWREFCOUNT_TRAVERSE), SomeAddress()],
+                 SomePtr(GCClass.RAWREFCOUNT_TRAVERSE), SomeAddress(),
+                 SomePtr(GCClass.RAWREFCOUNT_GC_AS_PYOBJ),
+                 SomePtr(GCClass.RAWREFCOUNT_PYOBJ_AS_GC)],
                 annmodel.s_None)
             self.rawrefcount_create_link_pypy_ptr = getfn(
                 GCClass.rawrefcount_create_link_pypy,
@@ -1311,13 +1313,15 @@
         self.pop_roots(hop, livevars)
 
     def gct_gc_rawrefcount_init(self, hop):
-        [v_fnptr, v_fnptr2, v_pyobj_list] = hop.spaceop.args
+        [v_fnptr, v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4] = 
hop.spaceop.args
         assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER
         assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE
-        # TODO add assert for v_pyobj_list
+        # TODO add assert for v_pyobj_list, improve asserts (types not same 
but equal)
+        # assert v_fnptr3.concretetype == self.GCClass.RAWREFCOUNT_GC_AS_PYOBJ
+        # assert v_fnptr4.concretetype == self.GCClass.RAWREFCOUNT_PYOBJ_AS_GC
         hop.genop("direct_call",
                   [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr,
-                   v_fnptr2, v_pyobj_list])
+                   v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4])
 
     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
@@ -19,11 +19,12 @@
                           ('c_ob_refcnt', lltype.Signed),
                           ('c_ob_pypy_link', lltype.Signed))
 PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
+PYOBJ_GC_HDR_PTR = lltype.Ptr(lltype.ForwardReference())
 PYOBJ_GC_HDR = lltype.Struct('PyGC_Head',
-                             ('c_gc_next', rffi.VOIDP),
-                             ('c_gc_prev', rffi.VOIDP),
+                             ('c_gc_next', PYOBJ_GC_HDR_PTR),
+                             ('c_gc_prev', PYOBJ_GC_HDR_PTR),
                              ('c_gc_refs', lltype.Signed))
-PYOBJ_GC_HDR_PTR = lltype.Ptr(PYOBJ_GC_HDR)
+PYOBJ_GC_HDR_PTR.TO.become(PYOBJ_GC_HDR)
 RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
 VISIT_FUNCTYPE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
                                             rffi.INT_real))
@@ -31,6 +32,10 @@
                                                    VISIT_FUNCTYPE,
                                                    rffi.VOIDP],
                                                   lltype.Void))
+RAWREFCOUNT_GC_AS_PYOBJ = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
+                                                     PYOBJ_HDR_PTR))
+RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR],
+                                                     PYOBJ_GC_HDR_PTR))
 
 
 def _build_pypy_link(p):
@@ -45,8 +50,7 @@
     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, _tp_traverse, _pygclist
-    global _pyobj_list
+    global _d_list, _dealloc_trigger_callback, _tp_traverse
     _p_list = []
     _o_list = []
     _adr2pypy = [None]
@@ -63,15 +67,6 @@
     global _pyobj_list
     _pyobj_list = rffi.cast(PYOBJ_GC_HDR_PTR, pyobj_list)
 
-# def init_traverse(traverse_cpy_call):
-#     global _traverse_cpy_call
-#     _traverse_cpy_call = traverse_cpy_call
-#
-# def traverse_cpy_call(pyobj, visitproc_ptr, arg):
-#     global _traverse_cpy_call
-#     _traverse_cpy_call(pyobj.c_ob_type.c_tp_traverse, pyobj,
-#                        visitproc_ptr, arg)
-
 @not_rpython
 def create_link_pypy(p, ob):
     "a link where the PyPy object contains some or all the data"
@@ -229,12 +224,13 @@
 def _print_pyobj_list():
     "for tests only"
     # TODO: change to get_pyobj_list, that returns a list of PyObjects
+    #       or alternatively checks if a certain object is in the list
     global _pyobj_list
     print "_print_pyobj_list start!"
-    curr = rffi.cast(PYOBJ_GC_HDR_PTR, _pyobj_list.c_gc_next)
+    curr = _pyobj_list.c_gc_next
     while curr != _pyobj_list:
         print "_print_pyobj_list: ", curr
-        curr = rffi.cast(PYOBJ_GC_HDR_PTR, curr.c_gc_next)
+        curr = curr.c_gc_next
 
 # ____________________________________________________________
 
@@ -263,18 +259,20 @@
 class Entry(ExtRegistryEntry):
     _about_ = init
 
-    def compute_result_annotation(self, s_dealloc_callback, tp_traverse,
-                                  pyobj_list):
+    def compute_result_annotation(self, s_dealloc_callback, s_tp_traverse,
+                                  s_pyobj_list, s_as_gc, s_as_pyobj):
         from rpython.rtyper.llannotation import SomePtr
         assert isinstance(s_dealloc_callback, SomePtr)   # ll-ptr-to-function
-        # add assert?
+        assert isinstance(s_tp_traverse, SomePtr)
+        assert isinstance(s_as_gc, SomePtr)
+        assert isinstance(s_as_pyobj, SomePtr)
 
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
-        v_dealloc_callback, v_tp_traverse, v_pyobj_list = \
-            hop.inputargs(*hop.args_r)
+        v_dealloc_callback, v_tp_traverse, v_pyobj_list, v_as_gc, \
+        v_as_pyobj = hop.inputargs(*hop.args_r)
         hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse,
-                                          v_pyobj_list])
+                                          v_pyobj_list, v_as_gc, v_as_pyobj])
 
 
 class Entry(ExtRegistryEntry):
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -915,7 +915,7 @@
                             lst.append(str(g))
                             g = seen.get(g)
                         lst.append('')
-                        # TODO: remove code (see below) to make this check pass
+                        # TODO: this check fails if this code is uncommented:
                         # pypy/module/cpyext/api.py:
                         #   print "start cpyext_call"
                         #   print "end cpyext_call"
diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py
--- a/rpython/translator/c/genc.py
+++ b/rpython/translator/c/genc.py
@@ -179,6 +179,8 @@
             defines['COUNT_OP_MALLOCS'] = 1
         if self.config.translation.cpyextgc == "boehm":
             defines['CPYEXT_BOEHM'] = 1
+        if self.config.translation.cpyextgc == "trialdeletion":
+            defines['CPYEXT_TRIALDELETION'] = 1
         if self.config.translation.sandbox:
             defines['RPY_SANDBOXED'] = 1
         if CBuilder.have___thread is None:
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to