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