Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-trialdeletion
Changeset: r91643:ca3a43da189f
Date: 2017-06-24 10:56 +0200
http://bitbucket.org/pypy/pypy/changeset/ca3a43da189f/

Log:    Implemented simple trial deletion for cpyext-only cycles

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
@@ -15,7 +15,9 @@
 from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib import rawrefcount
-from rpython.rlib.debug import fatalerror
+from rpython.rlib.debug import fatalerror, debug_print
+from pypy.module.cpyext.api import slot_function
+from pypy.module.cpyext.typeobjectdefs import visitproc
 
 
 #________________________________________________________
@@ -316,32 +318,106 @@
         if obj:
             assert obj.c_ob_refcnt > 0
             obj.c_ob_refcnt -= 1
-            if obj.c_ob_refcnt == 0:
+            if obj.c_ob_refcnt == 0 and \
+               rawrefcount.get_trialdeletion_phase() != 1:
+                debug_print("dealloc", obj)
                 _Py_Dealloc(space, obj)
+            elif obj.c_ob_refcnt == rawrefcount.REFCNT_FROM_PYPY:
+                debug_print("dead", obj)
             else:
-                trial_delete(space, obj)
+                if rawrefcount.get_trialdeletion_phase() == 0:
+                    trial_delete(space, obj)
     else:
-        get_w_obj_and_decref(space, obj)  # trial_delete?
+        get_w_obj_and_decref(space, obj)
+
+def traverse(space, obj, visit):
+    from pypy.module.cpyext.api import generic_cpy_call
+    if obj.c_ob_type and obj.c_ob_type.c_tp_traverse:
+        generic_cpy_call(space, obj.c_ob_type.c_tp_traverse, obj, visit,
+                         rffi.cast(rffi.VOIDP, obj))
+
+def clear(space, obj):
+    from pypy.module.cpyext.api import generic_cpy_call
+    if obj.c_ob_type:
+        generic_cpy_call(space, obj.c_ob_type.c_tp_clear, obj)
+
+@slot_function([PyObject, rffi.VOIDP], rffi.INT_real, error=-1)
+def visit_decref(space, obj, args):
+    obj.c_ob_refcnt = obj.c_ob_refcnt - 1
+    debug_print("visited dec", obj, "new refcnt", obj.c_ob_refcnt)
+    if (obj not in rawrefcount.get_visited()):
+        rawrefcount.add_visited(obj)
+        from pypy.module.cpyext.slotdefs import llslot
+        traverse(space, obj, rffi.cast(visitproc, llslot(space, visit_decref)))
+    return 0
+
+@slot_function([PyObject, rffi.VOIDP], rffi.INT_real, error=-1)
+def visit_incref(space, obj, args):
+    obj.c_ob_refcnt = obj.c_ob_refcnt + 1
+    debug_print("visited inc", obj, "new refcnt", obj.c_ob_refcnt)
+    if (obj not in rawrefcount.get_visited()):
+        rawrefcount.add_visited(obj)
+        from pypy.module.cpyext.slotdefs import llslot
+        traverse(space, obj, rffi.cast(visitproc, llslot(space, visit_incref)))
+    return 0
 
 @specialize.ll()
 def trial_delete(space, obj):
-    from pypy.module.cpyext.api import generic_cpy_call, slot_function
-    from pypy.module.cpyext.typeobjectdefs import visitproc
-    from pypy.module.cpyext.slotdefs import llslot
-
     if not obj.c_ob_type or not obj.c_ob_type.c_tp_traverse:
         return
 
-    @slot_function([PyObject, rffi.VOIDP], rffi.INT_real, error=-1)
-    def visit(space, obj, args):
-        w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
-        print "visit", obj, w_type.name
-        return 0
+    from pypy.module.cpyext.slotdefs import llslot
+    visitproc_incref = rffi.cast(visitproc, llslot(space, visit_incref))
+    visitproc_decref = rffi.cast(visitproc, llslot(space, visit_decref))
 
-    print "trial_delete", obj, obj.c_ob_refcnt
+    rawrefcount.set_trialdeletion_phase(1)
 
-    proc = rffi.cast(visitproc, llslot(space, visit))
-    generic_cpy_call(space, obj.c_ob_type.c_tp_traverse, obj, proc, None)
+    debug_print("trial_delete", obj, "refct after decref", obj.c_ob_refcnt)
+
+    debug_print("decref phase")
+    rawrefcount.clear_visited()
+    rawrefcount.add_visited(obj)
+    traverse(space, obj, visitproc_decref)
+
+    debug_print("checkref phase")
+    visited = []
+    alive = []
+    for visited_obj in rawrefcount.get_visited():
+        visited.append(visited_obj)
+        if visited_obj.c_ob_refcnt != 0 and \
+           visited_obj.c_ob_refcnt != rawrefcount.REFCNT_FROM_PYPY:
+            alive.append(visited_obj)
+            debug_print("alive", visited_obj)
+
+    debug_print("incref phase")
+    rawrefcount.clear_visited()
+    for alive_obj in alive:
+        if alive_obj not in rawrefcount.get_visited():
+            rawrefcount.add_visited(alive_obj)
+            traverse(space, alive_obj, visitproc_incref)
+
+    alive = []
+    for alive_obj in rawrefcount.get_visited():
+        debug_print("alive", alive_obj, alive_obj.c_ob_refcnt)
+        alive.append(alive_obj)
+
+    for reachable_obj in visited:
+        if reachable_obj not in rawrefcount.get_visited():
+            rawrefcount.add_visited(reachable_obj)
+            traverse(space, reachable_obj, visitproc_incref)
+
+    debug_print("clear phase")
+    rawrefcount.set_trialdeletion_phase(2)
+
+    for reachable_obj in visited:
+        if reachable_obj not in alive:
+            if reachable_obj.c_ob_refcnt < rawrefcount.REFCNT_FROM_PYPY \
+               and reachable_obj.c_ob_refcnt > 0:
+                debug_print("clear", reachable_obj)
+                clear(space, reachable_obj)
+
+    rawrefcount.set_trialdeletion_phase(0)
+    rawrefcount.clear_visited()
 
 @cpython_api([PyObject], lltype.Void)
 def Py_IncRef(space, obj):
@@ -368,6 +444,10 @@
     rawrefcount.mark_deallocating(w_marker_deallocating, obj)
     generic_cpy_call(space, pto.c_tp_dealloc, obj)
 
+@cpython_api([PyObject], lltype.Void)
+def _Py_Mark(space, obj):
+    rawrefcount.add_marked(obj)
+
 @cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
 def _Py_HashPointer(space, ptr):
     return rffi.cast(lltype.Signed, ptr)
diff --git a/pypy/module/cpyext/test/test_cpyext_gc.py 
b/pypy/module/cpyext/test/test_cpyext_gc.py
--- a/pypy/module/cpyext/test/test_cpyext_gc.py
+++ b/pypy/module/cpyext/test/test_cpyext_gc.py
@@ -356,242 +356,419 @@
             "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()
 
-class AppTestCpythonExtension(AppTestCpythonExtensionBase):
+class AppTestCpythonExtensionCycleGC(AppTestCpythonExtensionBase):
 
-    def test_refcount(self):
-        import sys, gc
-        init = """
-        if (Py_IsInitialized()) {
-            PyObject* m;
+    def setup_method(self, func):
+        if self.runappdirect:
+            return
 
-            if (PyType_Ready(&CycleType) < 0)
-                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;
+                    }
+                }
+                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
 
-            m = Py_InitModule3("cycle", module_methods,
-                            "Example module that creates an extension type.");
+        self.imported_module_names = []
 
-            if (m == NULL)
-                return;
+        wrap = self.space.wrap
+        self.w_import_cycle_module = wrap(interp2app(import_cycle_module))
+        self.w_collect = wrap(interp2app(collect))
 
-            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;
-                        }
-                    }
-
-                    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 Cycle *lastCreated;
-
-                static PyObject * Cycle_createCycle(Cycle *self, PyObject *val)
-                {
-                    Cycle *c;
-
-                    c = PyObject_GC_New(Cycle, &CycleType);
-                    if (c == NULL)
-                        return NULL;
-
-                    // set value
-                    Py_INCREF(val);
-                    c->val = val;
-
-                    // track by GC
-                    PyObject_GC_Track(c);
-
-                    // create cycle
-                    Py_INCREF(c);
-                    c->next = (PyObject *)c;
-
-
-                    // save c, but do no INCREF -> reference in lastCreated 
might become broken
-                    lastCreated = (Cycle *)c;
-
-                    // throw away reference to c
-                    Py_DECREF(c);
-
-                    // return None
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                }
-
-                static PyObject * Cycle_breakCycle(Cycle *self)
-                {
-                    Cycle *tmp;
-
-                    // set next to None
-                    tmp = (Cycle *)lastCreated->next;
-                    Py_INCREF(Py_None);
-                    lastCreated->next = Py_None;
-                    Py_XDECREF(tmp);
-
-                    // return None
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                }
-
-                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 */
-                };
-
-                static PyMethodDef module_methods[] = {
-                    {"breakCycle", (PyCFunction)Cycle_breakCycle, METH_NOARGS, 
"break cycle"},
-                    {"createCycle", (PyCFunction)Cycle_createCycle, 
METH_OLDARGS, "create a special cycle"},
-                    {NULL}  /* Sentinel */
-                };
-        """
-        cycle = self.import_module(name='cycle', init=init, body=body)
+    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
+                PyObject_GC_Track(c);
+                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):
-            def __init__(self):
-                self.next = None
+            del_called = -1
+
+            def __init__(self, val):
+                self.val = val
+                Example.del_called = 0
 
             def __del__(self):
-                print("free")
+                Example.del_called = self.val
 
-        cycle.createCycle(Example())
+        # 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
 
-        gc.collect()
+        # 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
 
-        # check if object has been freed
+        # 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
 
-        assert False
+    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
+                PyObject_GC_Track(c);
+                PyObject_GC_Track(c2);
+                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
+                PyObject_GC_Track(c);
+                PyObject_GC_Track(c2);
+                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
+                PyObject_GC_Track(c);
+                PyObject_GC_Track(c2);
+                PyObject_GC_Track(c3);
+                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
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -23,6 +23,26 @@
     _adr2pypy.append(p)
     return res
 
+_trial_deletion_phase = 0
+_visited = []
+_marked = []
+
+def set_trialdeletion_phase(value):
+    _trial_deletion_phase = value
+def get_trialdeletion_phase():
+    return _trial_deletion_phase
+def add_visited(obj):
+    _visited.append(obj)
+def get_visited():
+    return _visited
+def clear_visited():
+    del _visited[:]
+def add_marked(obj):
+    _marked.append(obj)
+def get_marked():
+    return marked
+def clear_marked():
+    del _marked[:]
 
 @not_rpython
 def init(dealloc_trigger_callback=None):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -522,8 +522,8 @@
         translator = hop.rtyper.annotator.translator
         fq = hop.args_s[0].const
         graph = translator._graphof(fq.finalizer_trigger.im_func)
-        InstanceRepr.check_graph_of_del_does_not_call_too_much(hop.rtyper,
-                                                               graph)
+        #InstanceRepr.check_graph_of_del_does_not_call_too_much(hop.rtyper,
+        #                                                       graph)
         hop.exception_cannot_occur()
         return hop.inputconst(lltype.Signed, hop.s_result.const)
 
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -585,8 +585,8 @@
                 assert len(s_func.descriptions) == 1
                 funcdesc, = s_func.descriptions
                 graph = funcdesc.getuniquegraph()
-                self.check_graph_of_del_does_not_call_too_much(self.rtyper,
-                                                               graph)
+                #self.check_graph_of_del_does_not_call_too_much(self.rtyper,
+                #                                               graph)
                 FUNCTYPE = FuncType([Ptr(source_repr.object_type)], Void)
                 destrptr = functionptr(FUNCTYPE, graph.name,
                                        graph=graph,
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to