Author: Stefan Beyer <[email protected]>
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"
-
[email protected]_api([], api.PyObject)
-def PyPy_Crash1(space):
- 1/0
-
[email protected]_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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit