[pypy-commit] pypy cpyext-gc-cycle: Added additional flags for objects
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95598:c03fe327893a Date: 2018-03-19 10:52 +0100 http://bitbucket.org/pypy/pypy/changeset/c03fe327893a/ Log:Added additional flags for objects Implemented refcount overhead (for non-cyclic refcount) Implemented buffer for potential roots of cycles Fixed assert to allow for recursive cpyext calls Added some cycle detection tests from experimental branch (disabled now) 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 @@ -208,10 +208,11 @@ # running and should not themselves release the GIL). # # **make_generic_cpy_call():** RPython to C, with the GIL held. Before -# the call, must assert that the global variable is 0 and set the -# current thread identifier into the global variable. After the call, -# assert that the global variable still contains the current thread id, -# and reset it to 0. +# the call, must assert that the global variable is 0 or the current +# thread identifier (recursive call) and set the current thread identifier +# into the global variable. After the call, assert that the global variable +# still contains the current thread id, and reset it to the value it held +# before the call. # # **make_wrapper():** C to RPython; by default assume that the GIL is # held, but accepts gil="acquire", "release", "around", @@ -1763,7 +1764,8 @@ # see "Handling of the GIL" above tid = rthread.get_ident() -assert cpyext_glob_tid_ptr[0] == 0 +tid_before = cpyext_glob_tid_ptr[0] +assert tid_before == 0 or tid_before == tid cpyext_glob_tid_ptr[0] = tid preexist_error = PyErr_Occurred(space) @@ -1772,7 +1774,7 @@ result = call_external_function(func, *boxed_args) finally: assert cpyext_glob_tid_ptr[0] == tid -cpyext_glob_tid_ptr[0] = 0 +cpyext_glob_tid_ptr[0] = tid_before for i, ARG in unrolling_arg_types: # note that this loop is nicely unrolled statically by RPython _pyobj = to_decref[i] diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h --- a/pypy/module/cpyext/include/boolobject.h +++ b/pypy/module/cpyext/include/boolobject.h @@ -13,8 +13,8 @@ #define Py_True ((PyObject *) &_Py_TrueStruct) /* Macros for returning Py_True or Py_False, respectively */ -#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True -#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False +#define Py_RETURN_TRUE do { Py_INCREF(Py_True); return Py_True; } while(0) +#define Py_RETURN_FALSE do { Py_INCREF(Py_False); return Py_False; } while(0) #ifdef __cplusplus } 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 @@ -2,6 +2,7 @@ #define Py_OBJECT_H #include +#include #ifdef __cplusplus extern "C" { @@ -12,7 +13,13 @@ #define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1)) #define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) -#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#define PY_REFCNT_FROM_PYPY (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 2))) +#define PY_REFCNT_GREEN (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7))) +#define PY_REFCNT_OVERFLOW (1L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7) / 2L - 1L)) +#define PY_REFCNT_MASK ((PY_REFCNT_OVERFLOW << 1L) - 1L) +#define Py_RETURN_NONE return (PyObject *)(Py_None))->ob_refcnt & PY_REFCNT_OVERFLOW) == 0) ? \ + ((PyObject *)(Py_None))->ob_refcnt++ : Py_IncRef((PyObject *)(Py_None))), Py_None + /* CPython has this for backwards compatibility with really old extensions, and now @@ -34,14 +41,21 @@ #define Py_XDECREF(ob) (Py_DecRef((PyObject *)(ob))) #else /* Fast version */ -#define Py_INCREF(ob) (((PyObject *)(ob))->ob_refcnt++) -#define Py_DECREF(op) \ -do {\ -if (--((PyObject *)(op))->ob_refcnt != 0) \ -; \ -else\ -_Py_Dealloc((PyObject *)(op)); \ -} while (0) +#define Py_INCREF(ob) do { \ +if (!(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW)) \ +((PyObject *)(ob))->ob_refcnt++; \ +else \ +Py_IncRef((PyObject *)(ob)); \ +} while (0) +#define Py_DECREF(ob) do { \ +
[pypy-commit] pypy cpyext-gc-cycle: Removed unnecessary code
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95602:65ead3f78618 Date: 2018-04-12 10:21 +0200 http://bitbucket.org/pypy/pypy/changeset/65ead3f78618/ Log:Removed unnecessary code 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 @@ -14,8 +14,7 @@ #define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) #define PY_REFCNT_FROM_PYPY (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 2))) -#define PY_REFCNT_GREEN (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7))) -#define PY_REFCNT_OVERFLOW (1L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7) / 2L - 1L)) +#define PY_REFCNT_OVERFLOW (1L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 4) - 1L)) #define PY_REFCNT_MASK ((PY_REFCNT_OVERFLOW << 1L) - 1L) #define Py_RETURN_NONE return (PyObject *)(Py_None))->ob_refcnt & PY_REFCNT_OVERFLOW) == 0) ? \ ((PyObject *)(Py_None))->ob_refcnt++ : Py_IncRef((PyObject *)(Py_None))), Py_None @@ -48,12 +47,11 @@ Py_IncRef((PyObject *)(ob)); \ } while (0) #define Py_DECREF(ob) do { \ -if (!(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_GREEN) ||\ -(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW))\ -Py_DecRef((PyObject *)(ob)); \ +if PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW))\ +Py_DecRef((PyObject *)(ob)); \ else if (--((PyObject *)(ob))->ob_refcnt & PY_REFCNT_MASK) \ ; \ -else if ((!((PyObject *)(ob))->ob_refcnt) & PY_REFCNT_FROM_PYPY) \ +else \ _Py_Dealloc((PyObject *)(ob)); \ } while (0) 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 @@ -18,9 +18,8 @@ from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_base_ptr from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import ll_assert, fatalerror, debug_print -from rpython.rlib.rawrefcount import ( -REFCNT_MASK, REFCNT_FROM_PYPY, REFCNT_OVERFLOW, REFCNT_CYCLE_BUFFERED, -REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE) +from rpython.rlib.rawrefcount import (REFCNT_MASK, REFCNT_FROM_PYPY, + REFCNT_OVERFLOW) from pypy.module.cpyext.api import slot_function from pypy.module.cpyext.typeobjectdefs import visitproc @@ -401,31 +400,13 @@ rawrefcount.decref(pyobj) rc = pyobj.c_ob_refcnt if rc & REFCNT_MASK == 0: -if rc & REFCNT_FROM_PYPY == 0 and rc & REFCNT_CLR_MASK != REFCNT_CLR_PURPLE: -state = space.fromcache(State) -generic_cpy_call(space, state.C._Py_Dealloc, pyobj) -elif rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN: -possible_root(space, pyobj) +state = space.fromcache(State) +generic_cpy_call(space, state.C._Py_Dealloc, pyobj) #else: #w_obj = rawrefcount.to_obj(W_Root, ref) #if w_obj is not None: #assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY -@jit.dont_look_inside -def possible_root(space, obj): -#debug_print("possible root", obj) -rc = obj.c_ob_refcnt -if not obj.c_ob_type or not obj.c_ob_type.c_tp_traverse: -#debug_print("mark green", obj) -rc = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_GREEN -elif rc & REFCNT_CLR_MASK != REFCNT_CLR_PURPLE: -rc = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_PURPLE -if rc & REFCNT_CYCLE_BUFFERED == 0: -#debug_print("mark purple", obj) -rawrefcount.buffer_pyobj(obj) -rc = rc | REFCNT_CYCLE_BUFFERED -obj.c_ob_refcnt = rc - @cpython_api([PyObject], lltype.Void) def Py_IncRef(space, obj): incref(space, obj) 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 @@ -3067,9 +3067,6 @@ objint = llmemory.cast_adr_to_int(obj, "symbolic") self._pyobj(pyobject).c_ob_pypy_link = objint -def rawrefcount_buffer_pyobj(self, pyobject): -self.rrc_buffered.append(pyobject) - def rawrefcount_from_obj(self, gcobj): obj = llmemory.cast_ptr_to_adr(gcobj) if self.is_in_nursery(obj): @@ -3254,12
[pypy-commit] pypy cpyext-gc-cycle: Fixed cpyext test
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95611:fa88e83164e0 Date: 2018-08-03 13:11 +0200 http://bitbucket.org/pypy/pypy/changeset/fa88e83164e0/ Log:Fixed cpyext test 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 @@ -3,7 +3,7 @@ import pytest from pypy.tool.cpyext.extbuild import SystemCompilationInfo, HERE -from pypy.interpreter.gateway import unwrap_spec, interp2app +from pypy.interpreter.gateway import unwrap_spec, interp2app, ObjSpace from pypy.interpreter.error import OperationError from rpython.rtyper.lltypesystem import lltype from pypy.module.cpyext import api @@ -214,8 +214,8 @@ def debug_collect(space): rawrefcount._collect() -def print_pyobj_list(space): -rawrefcount._print_pyobj_list() +def in_pygclist(space, int_addr): +return space.wrap(rawrefcount._in_pygclist(int_addr)) class AppTestCpythonExtensionBase(LeakCheckingTest): @@ -227,7 +227,8 @@ 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.w_in_pygclist = space.wrap( +interp2app(in_pygclist, unwrap_spec=[ObjSpace, int])) cls.preload_builtins(space) else: def w_import_module(self, name, init=None, body='', filename=None, @@ -929,7 +930,7 @@ ), ]) -def test_gc_pyobj_list(self): +def test_gc_track(self): """ Test if Py_GC_Track and Py_GC_Untrack are adding and removing container objects from the list of all garbage-collected PyObjects. @@ -937,17 +938,16 @@ if self.runappdirect: skip('cannot import module with undefined functions') -# TODO: remove unnecessary stuff, add gc_(un)track asserts init = """ if (Py_IsInitialized()) { PyObject* m; -if (PyType_Ready() < 0) +if (PyType_Ready() < 0) return; -m = Py_InitModule("cycle", module_methods); +m = Py_InitModule("foo", module_methods); if (m == NULL) return; -Py_INCREF(); -PyModule_AddObject(m, "Cycle", (PyObject *)); +Py_INCREF(); +PyModule_AddObject(m, "Foo", (PyObject *)); } """ body = """ @@ -955,85 +955,22 @@ #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, +} Foo; +static PyTypeObject FooType; +static PyObject* Foo_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { -Cycle *self; -self = PyObject_GC_New(Cycle, type); -if (self != NULL) { -self->next = PyString_FromString(""); -if (self->next == NULL) { -Py_DECREF(self); -return NULL; -} -} +Foo *self; +self = PyObject_GC_New(Foo, type); 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, - )) -return -1; -if (next) { -tmp = self->next; -Py_INCREF(next); -self->next = next; -Py_XDECREF(tmp); -} -return 0; -} -
[pypy-commit] pypy cpyext-gc-cycle: Call tp_traverse from incminimark
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95599:fb1c6fe11349 Date: 2018-03-20 16:38 +0100 http://bitbucket.org/pypy/pypy/changeset/fb1c6fe11349/ Log:Call tp_traverse from incminimark Mark cpython objects reachable by pypy objects 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 @@ -1293,7 +1293,10 @@ # if do tuple_attach of the prebuilt empty tuple, we need to call # _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='') @@ -1716,6 +1719,11 @@ return make_generic_cpy_call(FT, False)(space, func, *args) @specialize.ll() +def generic_cpy_call_gc(func, *args): +FT = lltype.typeOf(func).TO +return make_generic_cpy_call_gc(FT, False)(func, *args) + +@specialize.ll() def generic_cpy_call_expect_null(space, func, *args): FT = lltype.typeOf(func).TO return make_generic_cpy_call(FT, True)(space, func, *args) @@ -1815,3 +1823,75 @@ return result return generic_cpy_call + +@specialize.memo() +def make_generic_cpy_call_gc(FT, expect_null): +from pypy.module.cpyext.pyobject import is_pyobj, make_ref, decref +from pypy.module.cpyext.pyobject import get_w_obj_and_decref +from pypy.module.cpyext.pyerrors import PyErr_Occurred +unrolling_arg_types = unrolling_iterable(enumerate(FT.ARGS)) +RESULT_TYPE = FT.RESULT + +# copied and modified from rffi.py +# We need tons of care to ensure that no GC operation and no +# exception checking occurs in call_external_function. +argnames = ', '.join(['a%d' % i for i in range(len(FT.ARGS))]) +source = py.code.Source(""" +def cpy_call_external(funcptr, %(argnames)s): +# NB. it is essential that no exception checking occurs here! +res = funcptr(%(argnames)s) +return res +""" % locals()) +miniglobals = {'__name__':__name__, # for module name propagation + } +exec source.compile() in miniglobals +call_external_function = specialize.ll()(miniglobals['cpy_call_external']) +call_external_function._dont_inline_ = True +call_external_function._gctransformer_hint_close_stack_ = True +# don't inline, as a hack to guarantee that no GC pointer is alive +# anywhere in call_external_function + +@specialize.ll() +def generic_cpy_call(func, *args): +boxed_args = () +to_decref = () +assert len(args) == len(FT.ARGS) +for i, ARG in unrolling_arg_types: +arg = args[i] +_pyobj = None +if is_PyObject(ARG): +assert is_pyobj(arg) + +boxed_args += (arg,) +to_decref += (_pyobj,) + +# see "Handling of the GIL" above +tid = rthread.get_ident() +tid_before = cpyext_glob_tid_ptr[0] +assert tid_before == 0 or tid_before == tid +cpyext_glob_tid_ptr[0] = tid + +try: +# Call the function +result = call_external_function(func, *boxed_args) +finally: +assert cpyext_glob_tid_ptr[0] == tid +cpyext_glob_tid_ptr[0] = tid_before +for i, ARG in unrolling_arg_types: +# note that this loop is nicely unrolled statically by RPython +_pyobj = to_decref[i] +if _pyobj is not None: +pyobj = rffi.cast(PyObject, _pyobj) +rawrefcount.decref(pyobj) + +if is_PyObject(RESULT_TYPE): +ret = None + +# Check for exception consistency +# XXX best attempt, will miss preexisting error that is +# overwritten with a new error of the same type + +return ret +return result + +return generic_cpy_call \ No newline at end of file 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 @@ -321,6 +321,8 @@ #define _PyGC_FINALIZED(o) 1 #define PyType_IS_GC(tp) 1 +/* TODO: implement like in cpython + (see https://github.com/python/cpython/blob/517da1e58f4c489d4b31579852cde5f7113da08e/Include/objimpl.h#L295) */ #define PyObject_GC_Track(o) do { } while(0) #define PyObject_GC_UnTrack(o)do { } while(0) #define _PyObject_GC_TRACK(o) do { } while(0) 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 @@ -193,13 +193,27 @@ VISIT_FUNCTYPE = rffi.CCallback([PyObject, rffi.VOIDP], rffi.INT_real) -def traverse(obj, func_ptr): -from pypy.module.cpyext.api import generic_cpy_call -from pypy.module.cpyext.typeobjectdefs import
[pypy-commit] pypy cpyext-gc-cycle: Implemented cpython-like GC list for cpyext
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95604:e5ba3fd47f96 Date: 2018-07-04 17:56 +0200 http://bitbucket.org/pypy/pypy/changeset/e5ba3fd47f96/ Log:Implemented cpython-like GC list for cpyext Added some code for cpyext-only boehm GC support 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 @@ -751,6 +751,8 @@ PyVarObjectFields = PyObjectFields + (("ob_size", Py_ssize_t), ) PyVarObjectStruct = cts.gettype('PyVarObject') PyVarObject = cts.gettype('PyVarObject *') +PyGC_Head = cts.gettype('PyGC_Head') +PyGC_HeadPtr = cts.gettype('PyGC_Head *') Py_buffer = cts.gettype('Py_buffer') Py_bufferP = cts.gettype('Py_buffer *') @@ -1173,6 +1175,9 @@ 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, +compilation_info=eci, _nowrapper=True) def init_function(func): @@ -1294,6 +1299,9 @@ ll2ctypes.lltype2ctypes(func.get_llhelper(space)), ctypes.c_void_p) +# initialize the pyobj_list for the gc +space.fromcache(State).C._PyPy_InitPyObjList() + # we need to call this *after* the init code above, because it might # indirectly call some functions which are attached to pypyAPI (e.g., we # if do tuple_attach of the prebuilt empty tuple, we need to call @@ -1826,75 +1834,3 @@ return result return generic_cpy_call - -@specialize.memo() -def make_generic_cpy_call_gc(FT, expect_null): -from pypy.module.cpyext.pyobject import is_pyobj, make_ref, decref -from pypy.module.cpyext.pyobject import get_w_obj_and_decref -from pypy.module.cpyext.pyerrors import PyErr_Occurred -unrolling_arg_types = unrolling_iterable(enumerate(FT.ARGS)) -RESULT_TYPE = FT.RESULT - -# copied and modified from rffi.py -# We need tons of care to ensure that no GC operation and no -# exception checking occurs in call_external_function. -argnames = ', '.join(['a%d' % i for i in range(len(FT.ARGS))]) -source = py.code.Source(""" -def cpy_call_external(funcptr, %(argnames)s): -# NB. it is essential that no exception checking occurs here! -res = funcptr(%(argnames)s) -return res -""" % locals()) -miniglobals = {'__name__':__name__, # for module name propagation - } -exec source.compile() in miniglobals -call_external_function = specialize.ll()(miniglobals['cpy_call_external']) -call_external_function._dont_inline_ = True -call_external_function._gctransformer_hint_close_stack_ = True -# don't inline, as a hack to guarantee that no GC pointer is alive -# anywhere in call_external_function - -@specialize.ll() -def generic_cpy_call(func, *args): -boxed_args = () -to_decref = () -assert len(args) == len(FT.ARGS) -for i, ARG in unrolling_arg_types: -arg = args[i] -_pyobj = None -if is_PyObject(ARG): -assert is_pyobj(arg) - -boxed_args += (arg,) -to_decref += (_pyobj,) - -# see "Handling of the GIL" above -tid = rthread.get_ident() -tid_before = cpyext_glob_tid_ptr[0] -assert tid_before == 0 or tid_before == tid -cpyext_glob_tid_ptr[0] = tid - -try: -# Call the function -result = call_external_function(func, *boxed_args) -finally: -assert cpyext_glob_tid_ptr[0] == tid -cpyext_glob_tid_ptr[0] = tid_before -for i, ARG in unrolling_arg_types: -# note that this loop is nicely unrolled statically by RPython -_pyobj = to_decref[i] -if _pyobj is not None: -pyobj = rffi.cast(PyObject, _pyobj) -rawrefcount.decref(pyobj) - -if is_PyObject(RESULT_TYPE): -ret = None - -# Check for exception consistency -# XXX best attempt, will miss preexisting error that is -# overwritten with a new error of the same type - -return ret -return result - -return generic_cpy_call \ No newline at end of file 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 @@ -279,7 +279,7 @@ ) & ~(SIZEOF_VOID_P - 1) \ ) - + #define PyObject_INIT(op, typeobj) \ ( Py_TYPE(op) = (typeobj), ((PyObject *)(op))->ob_refcnt = 1,\ ((PyObject *)(op))->ob_pypy_link = 0, (op) ) @@ -309,22 +309,65 @@ #define PyObject_GC_NewVar(type, typeobj, n) \ ( (type *) _PyObject_GC_NewVar((typeobj), (n)) ) -
[pypy-commit] pypy cpyext-gc-cycle: Fixed some formatting issues
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95613:80824650968a Date: 2018-08-24 09:30 +0200 http://bitbucket.org/pypy/pypy/changeset/80824650968a/ Log:Fixed some formatting issues 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 @@ -332,4 +332,4 @@ typedef struct _gchdr_pyobject { Py_ssize_t ob_refcnt; Py_ssize_t ob_pypy_link; -} GCHdr_PyObject; \ No newline at end of file +} GCHdr_PyObject; 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 @@ -406,6 +406,7 @@ #if w_obj is not None: #assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY + @init_function def write_w_marker_deallocating(space): if we_are_translated(): 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 @@ -221,4 +221,4 @@ { obj->ob_size = size; return (PyVarObject*)PyObject_Init((PyObject*)obj, type); -} \ No newline at end of file +} ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Implemented pyobj as gc and vice-versa
Author: Stefan Beyer 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;
[pypy-commit] pypy cpyext-gc-cycle: Added some dot tests
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95617:c46c894a7c06 Date: 2019-01-11 10:40 +0100 http://bitbucket.org/pypy/pypy/changeset/c46c894a7c06/ Log:Added some dot tests diff --git a/rpython/memory/gc/test/dot/free_cpython_self.dot b/rpython/memory/gc/test/dot/free_cpython_self.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cpython_self.dot @@ -0,0 +1,4 @@ +digraph G { +"a" [type=C, alive=n]; +"a" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_1a.dot b/rpython/memory/gc/test/dot/free_cross_multi_1a.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_1a.dot @@ -0,0 +1,9 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"c" [type=C, alive=n]; +"a" -> "b"; +"b" -> "a"; +"a" -> "c"; +"c" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_1b.dot b/rpython/memory/gc/test/dot/free_cross_multi_1b.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_1b.dot @@ -0,0 +1,10 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"c" [type=C, alive=n]; +"a" -> "b"; +"b" -> "a"; +"a" -> "c"; +"c" -> "a"; +"b" -> "c"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_2a.dot b/rpython/memory/gc/test/dot/free_cross_multi_2a.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_2a.dot @@ -0,0 +1,9 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"c" [type=C, alive=n]; +"a" -> "b"; +"b" -> "c"; +"c" -> "a"; +"b" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_2b.dot b/rpython/memory/gc/test/dot/free_cross_multi_2b.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_2b.dot @@ -0,0 +1,11 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"c" [type=C, alive=n]; +"d" [type=B, alive=n]; +"a" -> "b"; +"b" -> "c"; +"c" -> "d"; +"b" -> "a"; +"d" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_2c.dot b/rpython/memory/gc/test/dot/free_cross_multi_2c.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_2c.dot @@ -0,0 +1,13 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"c" [type=C, alive=n]; +"d" [type=B, alive=n]; +"e" [type=P, alive=n]; +"a" -> "b"; +"b" -> "c"; +"c" -> "d"; +"b" -> "a"; +"d" -> "e"; +"e" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_3a.dot b/rpython/memory/gc/test/dot/free_cross_multi_3a.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_3a.dot @@ -0,0 +1,9 @@ +digraph G { +"a" [type=P, alive=n]; +"b" [type=B, alive=n]; +"c" [type=C, alive=n]; +"a" -> "b"; +"b" -> "a"; +"b" -> "c"; +"c" -> "b"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_3b.dot b/rpython/memory/gc/test/dot/free_cross_multi_3b.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_3b.dot @@ -0,0 +1,11 @@ +digraph G { +"a" [type=P, alive=n]; +"b" [type=B, alive=n]; +"c" [type=C, alive=n]; +"d" [type=C, alive=n]; +"a" -> "b"; +"b" -> "a"; +"b" -> "c"; +"c" -> "d"; +"d" -> "b"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_multi_3c.dot b/rpython/memory/gc/test/dot/free_cross_multi_3c.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_multi_3c.dot @@ -0,0 +1,11 @@ +digraph G { +"a" [type=P, alive=n]; +"b" [type=P, alive=n]; +"c" [type=B, alive=n]; +"d" [type=C, alive=n]; +"a" -> "b"; +"b" -> "c"; +"c" -> "d"; +"d" -> "c"; +"c" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_simple_1.dot b/rpython/memory/gc/test/dot/free_cross_simple_1.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_simple_1.dot @@ -0,0 +1,6 @@ +digraph G { +"a" [type=B, alive=n]; +"b" [type=C, alive=n]; +"a" -> "b"; +"b" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_simple_2.dot b/rpython/memory/gc/test/dot/free_cross_simple_2.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_simple_2.dot @@ -0,0 +1,10 @@ +digraph G { +"a" [type=P, alive=n]; +"b" [type=B, alive=n]; +"c" [type=C, alive=n]; +"d" [type=B, alive=n]; +"a" -> "b"; +"b" -> "c"; +"c" -> "d"; +"d" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/free_cross_simple_3.dot b/rpython/memory/gc/test/dot/free_cross_simple_3.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_cross_simple_3.dot @@ -0,0 +1,12 @@ +digraph G { +"a" [type=P, alive=n]; +"b" [type=P, alive=n]; +"c" [type=B, alive=n]; +"d" [type=C, alive=n]; +"e" [type=B,
[pypy-commit] pypy cpyext-gc-cycle: Cleaned up code in incminimark
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95612:5b3e2b3a25bf Date: 2018-08-24 09:17 +0200 http://bitbucket.org/pypy/pypy/changeset/5b3e2b3a25bf/ Log:Cleaned up code in incminimark 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 @@ -3030,9 +3030,6 @@ self.rrc_dealloc_trigger_callback = dealloc_trigger_callback self.rrc_dealloc_pending = self.AddressStack() self.rrc_tp_traverse = tp_traverse -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 @@ -3205,16 +3202,8 @@ self._pyobj(pyobject).c_ob_refcnt = rc _rrc_free._always_inline_ = True -NO_CYCLE_DETECTION = False - def rrc_major_collection_trace(self): -debug_start("gc-rrc-trace") -if self.NO_CYCLE_DETECTION: -self.rrc_p_list_old.foreach(self._rrc_major_trace, None) -else: -self.rrc_major_collection_trace_cycle() -self.rrc_p_list_old.foreach(self._rrc_major_trace, None) # for now, remove later -debug_stop("gc-rrc-trace") +self.rrc_p_list_old.foreach(self._rrc_major_trace, None) def _rrc_major_trace(self, pyobject, ignore): from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY @@ -3230,77 +3219,6 @@ self.objects_to_trace.append(obj) self.visit_all_objects() -def rrc_major_collection_trace_cycle(self): -assert not self.objects_to_trace.non_empty() -assert not self.rrc_pyobjects_to_scan.non_empty() -assert not self.rrc_more_pyobjects_to_scan.non_empty() -assert not self.rrc_pyobjects_to_trace.non_empty() - -self._rrc_gc_print_list() - -# initially, scan all real pyobjects (not proxies) which are linked to objects -#self.rrc_p_list_old.foreach(self._rrc_major_scan_non_rc_roots, None) -self.rrc_o_list_old.foreach(self._rrc_major_scan_non_rc_roots, None) - -# as long as we find new pyobjects which should be marked, recursively -# mark them -while self.rrc_pyobjects_to_trace.non_empty(): -while self.rrc_pyobjects_to_trace.non_empty(): -pyobject = self.rrc_pyobjects_to_trace.pop() -self._rrc_traverse(pyobject) - -# see if we found new pypy objects to trace -if self.objects_to_trace.non_empty(): -self.visit_all_objects() -self.objects_to_trace.delete() -self.objects_to_trace = self.AddressStack() - -# look if there are some pyobjects with linked objects which were -# not marked previously, but are marked now -swap = self.rrc_pyobjects_to_scan -self.rrc_pyobjects_to_scan = self.rrc_more_pyobjects_to_scan -self.rrc_more_pyobjects_to_scan = swap -self.rrc_pyobjects_to_scan.foreach( -self._rrc_major_scan_non_rc_roots, None) -self.rrc_pyobjects_to_scan.delete() -self.rrc_pyobjects_to_scan = self.AddressStack() - -self.rrc_more_pyobjects_to_scan.delete() -self.rrc_more_pyobjects_to_scan = self.AddressStack() - -def _rrc_mark_cpyobj(self, pyobj): -# if the pyobj is not marked, remember it and if there is a linked pypy -# object also remember it -visited = True # TODO: check if visited (via 'cast' to PyGC_Head) -if not visited: -# TODO: mark visited -pyobject = llmemory.cast_ptr_to_adr(pyobj) -self.rrc_more_pyobjects_to_scan.append(pyobject) -intobj = pyobj.c_ob_pypy_link -if intobj != 0: -obj = llmemory.cast_int_to_adr(intobj) -hdr = self.header(obj) -if not (hdr.tid & GCFLAG_VISITED): -self.objects_to_trace.append(obj) - -def _rrc_major_scan_non_rc_roots(self, pyobject, ignore): -# check in the object header of the linked pypy object, if it is marked -# or not -pyobj = self._pyobj(pyobject) -intobj = pyobj.c_ob_pypy_link -obj = llmemory.cast_int_to_adr(intobj) -hdr = self.header(obj) -if hdr.tid & GCFLAG_VISITED: -visited = True # TODO: check if visited -if not visited: -# process the pyobject now -# TODO: mark visited -self.rrc_pyobjects_to_trace.append(pyobject) -else: -# save the pyobject for later, in case its linked object becomes -# marked -self.rrc_more_pyobjects_to_scan.append(pyobject) - def
[pypy-commit] pypy cpyext-gc-cycle: Fixed rawrefcount tests
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95610:c1219f8ea34b Date: 2018-08-03 09:43 +0200 http://bitbucket.org/pypy/pypy/changeset/c1219f8ea34b/ Log:Fixed rawrefcount tests 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 @@ -334,146 +334,66 @@ self._collect(major=True) check_alive(0) -def test_cycle_self_reference_free(self): +def test_linked_cycle_self_reference_dies_without_external_reference(self): +p1, p1ref, r1, r1addr, check_alive = ( +self._rawrefcount_pair(42)) +r1.c_ob_refcnt += 1 +p1.next = p1 +check_alive(+1) +self._collect(major=True, expected_trigger=1) +py.test.raises(RuntimeError, "p1.x") # dead +assert r1.c_ob_refcnt == 1 # in the pending list +assert r1.c_ob_pypy_link == 0 +assert self.gc.rawrefcount_next_dead() == r1addr +assert self.gc.rawrefcount_next_dead() == llmemory.NULL +assert self.gc.rawrefcount_next_dead() == llmemory.NULL +self.gc.check_no_more_rawrefcount_state() +lltype.free(r1, flavor='raw') + +def test_linked_cycle_self_reference_survives_with_pyobj_reference(self): p1, p1ref, r1, r1addr, check_alive = ( self._rawrefcount_pair(42, create_immortal=True)) +r1.c_ob_refcnt += 2 # the pyobject is kept alive p1.next = p1 -check_alive(0) +check_alive(+2) self._collect(major=True) -py.test.raises(RuntimeError, "r1.c_ob_refcnt") # dead +check_alive(+2) +r1.c_ob_refcnt -= 1 # the external reference from pyobj is removed +check_alive(+1) +self._collect(major=True, expected_trigger=1) py.test.raises(RuntimeError, "p1.x") # dead +assert r1.c_ob_refcnt == 1 # in the pending list +assert r1.c_ob_pypy_link == 0 +assert self.gc.rawrefcount_next_dead() == r1addr +assert self.gc.rawrefcount_next_dead() == llmemory.NULL +assert self.gc.rawrefcount_next_dead() == llmemory.NULL +self.gc.check_no_more_rawrefcount_state() +lltype.free(r1, flavor='raw') -def test_cycle_self_reference_not_free(self): +def test_linked_cycle_self_reference_survives_with_pypy_reference(self): p1, p1ref, r1, r1addr, check_alive = ( self._rawrefcount_pair(42, create_immortal=True)) -r1.c_ob_refcnt += 1 # the pyobject is kept alive +r1.c_ob_refcnt += 1 p1.next = p1 +self.stackroots.append(p1) check_alive(+1) self._collect(major=True) +assert p1.x == 42 +assert self.trigger == [] check_alive(+1) +p1 = self.stackroots.pop() +check_alive(+1) +self._collect(major=True, expected_trigger=1) +py.test.raises(RuntimeError, "p1.x") # dead +assert r1.c_ob_refcnt == 1 +assert r1.c_ob_pypy_link == 0 +assert self.gc.rawrefcount_next_dead() == r1addr +self.gc.check_no_more_rawrefcount_state() +lltype.free(r1, flavor='raw') -# def test_simple_cycle_free(self): -# self.gc.rawrefcount_init(lambda: self.trigger.append(1)) -# r1 = self._rawrefcount_cycle_obj() -# r2 = self._rawrefcount_cycle_obj() -# r1.next = r2 -# r2.next = r1 -# self._rawrefcount_buffer_obj(r1) -# self.gc.rrc_collect_cycles() -# assert r1.base.c_ob_refcnt & REFCNT_MASK == 0 -# assert r2.base.c_ob_refcnt & REFCNT_MASK == 0 -# -# def test_simple_cycle_not_free(self): -# self.gc.rawrefcount_init(lambda: self.trigger.append(1)) -# r1 = self._rawrefcount_cycle_obj() -# r2 = self._rawrefcount_cycle_obj() -# r1.next = r2 -# r2.next = r1 -# r2.base.c_ob_refcnt += 1 -# self._rawrefcount_buffer_obj(r1) -# self.gc.rrc_collect_cycles() -# assert r1.base.c_ob_refcnt & REFCNT_MASK == 1 -# assert r2.base.c_ob_refcnt & REFCNT_MASK == 2 -# -# def test_complex_cycle_free(self): -# self.gc.rawrefcount_init(lambda: self.trigger.append(1)) -# r1 = self._rawrefcount_cycle_obj() -# r2 = self._rawrefcount_cycle_obj() -# r3 = self._rawrefcount_cycle_obj() -# r1.next = r2 -# r1.prev = r2 -# r2.base.c_ob_refcnt += 1 -# r2.next = r3 -# r3.prev = r1 -# self._rawrefcount_buffer_obj(r1) -# self.gc.rrc_collect_cycles() -# assert r1.base.c_ob_refcnt & REFCNT_MASK == 0 -# assert r2.base.c_ob_refcnt & REFCNT_MASK == 0 -# assert r3.base.c_ob_refcnt & REFCNT_MASK == 0 -# -# def test_complex_cycle_not_free(self): -# self.gc.rawrefcount_init(lambda: self.trigger.append(1)) -# r1 = self._rawrefcount_cycle_obj() -# r2 =
[pypy-commit] pypy cpyext-gc-cycle: Refactored call to tp_traverse from incminimark so there are no dependencies to pypy
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95600:94b062729ca4 Date: 2018-03-23 11:46 +0100 http://bitbucket.org/pypy/pypy/changeset/94b062729ca4/ Log:Refactored call to tp_traverse from incminimark so there are no dependencies to pypy 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 @@ -1719,11 +1719,6 @@ return make_generic_cpy_call(FT, False)(space, func, *args) @specialize.ll() -def generic_cpy_call_gc(func, *args): -FT = lltype.typeOf(func).TO -return make_generic_cpy_call_gc(FT, False)(func, *args) - -@specialize.ll() def generic_cpy_call_expect_null(space, func, *args): FT = lltype.typeOf(func).TO return make_generic_cpy_call(FT, True)(space, func, *args) 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 @@ -20,8 +20,7 @@ from rpython.rlib.debug import ll_assert, fatalerror, debug_print from rpython.rlib.rawrefcount import ( REFCNT_MASK, REFCNT_FROM_PYPY, REFCNT_OVERFLOW, REFCNT_CYCLE_BUFFERED, -REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE, -W_MARKER_DEALLOCATING) +REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE) from pypy.module.cpyext.api import slot_function from pypy.module.cpyext.typeobjectdefs import visitproc @@ -254,6 +253,8 @@ w_obj._cpyext_attach_pyobj(space, py_obj) +w_marker_deallocating = W_Root() + @jit.dont_look_inside def from_ref(space, ref): """ @@ -265,7 +266,7 @@ return None w_obj = rawrefcount.to_obj(W_Root, ref) if w_obj is not None: -if w_obj is not W_MARKER_DEALLOCATING: +if w_obj is not w_marker_deallocating: return w_obj fatalerror( "*** Invalid usage of a dying CPython object ***\n" @@ -318,7 +319,7 @@ def pyobj_has_w_obj(pyobj): w_obj = rawrefcount.to_obj(W_Root, pyobj) -return w_obj is not None and w_obj is not W_MARKER_DEALLOCATING +return w_obj is not None and w_obj is not w_marker_deallocating def w_obj_has_pyobj(w_obj): return bool(rawrefcount.from_obj(PyObject, w_obj)) @@ -454,7 +455,7 @@ @init_function def write_w_marker_deallocating(space): if we_are_translated(): -llptr = cast_instance_to_base_ptr(W_MARKER_DEALLOCATING) +llptr = cast_instance_to_base_ptr(w_marker_deallocating) state = space.fromcache(State) state.C.set_marker(rffi.cast(Py_ssize_t, llptr)) 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 @@ -1,8 +1,8 @@ from rpython.rlib.objectmodel import we_are_translated, specialize -from rpython.rtyper.lltypesystem import rffi, lltype +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 +from rpython.rtyper.annlowlevel import llhelper, llhelper_args from rpython.rlib.rdynload import DLLHANDLE from rpython.rlib import rawrefcount import sys @@ -70,7 +70,10 @@ decref(space, ob) print 'dealloc_trigger DONE' return "RETRY" -rawrefcount.init(dealloc_trigger) +def tp_traverse(obj_addr, callback, args): +# TODO: implement +pass +rawrefcount.init(dealloc_trigger, tp_traverse) else: if space.config.translation.gc == "boehm": action = BoehmPyObjDeallocAction(space) @@ -80,6 +83,25 @@ pyobj_dealloc_action = PyObjDeallocAction(space) self.dealloc_trigger = lambda: pyobj_dealloc_action.fire() +def _rawrefcount_tp_traverse(space, pyobj_ptr, callback, args): +from pypy.module.cpyext.api import (generic_cpy_call, +PyObject) +from pypy.module.cpyext.typeobjectdefs import visitproc +# convert to pointers with correct types (PyObject) +callback_addr = llmemory.cast_ptr_to_adr(callback) +callback_ptr = llmemory.cast_adr_to_ptr(callback_addr, +visitproc) +pyobj_addr = llmemory.cast_ptr_to_adr(pyobj_ptr) +pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject) +# now call tp_traverse (if possible) +if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse: +generic_cpy_call(space, pyobj.c_ob_type.c_tp_traverse, + pyobj, + callback_ptr, args) +self.tp_traverse = (lambda o, v, a: +
[pypy-commit] pypy cpyext-gc-cycle: Finished implementation of dot file tests for rawrefcount
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95615:48c91f03eaa6 Date: 2018-12-27 16:22 +0100 http://bitbucket.org/pypy/pypy/changeset/48c91f03eaa6/ Log:Finished implementation of dot file tests for rawrefcount Removed obsolete tests, that will be replaced by dot tests 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 @@ -387,66 +387,11 @@ self._collect(major=True) check_alive(0) -def test_linked_cycle_self_reference_dies_without_external_reference(self): -p1, p1ref, r1, r1addr, check_alive = ( -self._rawrefcount_pair(42)) -r1.c_ob_refcnt += 1 -p1.next = p1 -check_alive(+1) -self._collect(major=True, expected_trigger=1) -py.test.raises(RuntimeError, "p1.x") # dead -assert r1.c_ob_refcnt == 1 # in the pending list -assert r1.c_ob_pypy_link == 0 -assert self.gc.rawrefcount_next_dead() == r1addr -assert self.gc.rawrefcount_next_dead() == llmemory.NULL -assert self.gc.rawrefcount_next_dead() == llmemory.NULL -self.gc.check_no_more_rawrefcount_state() -lltype.free(r1, flavor='raw') - -def test_linked_cycle_self_reference_survives_with_pyobj_reference(self): -p1, p1ref, r1, r1addr, check_alive = ( -self._rawrefcount_pair(42, create_immortal=True)) -r1.c_ob_refcnt += 2 # the pyobject is kept alive -p1.next = p1 -check_alive(+2) -self._collect(major=True) -check_alive(+2) -r1.c_ob_refcnt -= 1 # the external reference from pyobj is removed -check_alive(+1) -self._collect(major=True, expected_trigger=1) -py.test.raises(RuntimeError, "p1.x") # dead -assert r1.c_ob_refcnt == 1 # in the pending list -assert r1.c_ob_pypy_link == 0 -assert self.gc.rawrefcount_next_dead() == r1addr -assert self.gc.rawrefcount_next_dead() == llmemory.NULL -assert self.gc.rawrefcount_next_dead() == llmemory.NULL -self.gc.check_no_more_rawrefcount_state() -lltype.free(r1, flavor='raw') - -def test_linked_cycle_self_reference_survives_with_pypy_reference(self): -p1, p1ref, r1, r1addr, check_alive = ( -self._rawrefcount_pair(42, create_immortal=True)) -r1.c_ob_refcnt += 1 -p1.next = p1 -self.stackroots.append(p1) -check_alive(+1) -self._collect(major=True) -assert p1.x == 42 -assert self.trigger == [] -check_alive(+1) -p1 = self.stackroots.pop() -check_alive(+1) -self._collect(major=True, expected_trigger=1) -py.test.raises(RuntimeError, "p1.x") # dead -assert r1.c_ob_refcnt == 1 -assert r1.c_ob_pypy_link == 0 -assert self.gc.rawrefcount_next_dead() == r1addr -self.gc.check_no_more_rawrefcount_state() -lltype.free(r1, flavor='raw') - dot_dir = os.path.join(os.path.realpath(os.path.dirname(__file__)), "dot") dot_files = [file for file in os.listdir(dot_dir) if file.endswith(".dot")] +@py.test.mark.dont_track_allocations('intentionally keep objects alive, ' + 'because we do the checks ourselves') @py.test.mark.parametrize("file", dot_files) def test_dots(self, file): from rpython.memory.gc.test.dot import pydot @@ -488,6 +433,7 @@ g = pydot.graph_from_dot_file(path)[0] nodes = {} +# create objects from graph for n in g.get_nodes(): name = n.get_name() attr = n.obj_dict['attributes'] @@ -512,6 +458,8 @@ r.c_ob_refcnt = ext_refcnt nodes[name] = BorderNode(p, pref, r, raddr, check_alive, info) pass + +# add references between objects from graph for e in g.get_edges(): source = nodes[e.get_source()] dest = nodes[e.get_destination()] @@ -526,33 +474,36 @@ else: assert False # only 2 refs supported from pypy obj +# quick self check, if traverse works properly +dests_by_source = {} +for e in g.get_edges(): +source = nodes[e.get_source()] +dest = nodes[e.get_destination()] +if source.info.type == "C" or dest.info.type == "C": +if not dests_by_source.has_key(source): +dests_by_source[source] = [] +dests_by_source[source].append(dest.r) +for source in dests_by_source: +dests_target = dests_by_source[source] +def append(self, pyobj): +dests_target.remove(pyobj) +self.gc._rrc_visit_pyobj = append +self.gc._rrc_traverse(source.raddr) +assert len(dests_target) == 0 + +
[pypy-commit] pypy cpyext-gc-cycle: Fixed cpyext test
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95609:860d9f8d29b6 Date: 2018-08-02 14:53 +0200 http://bitbucket.org/pypy/pypy/changeset/860d9f8d29b6/ Log:Fixed cpyext test 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 @@ -937,7 +937,7 @@ if self.runappdirect: skip('cannot import module with undefined functions') -# TODO: remove unnecessary stuff, add tests for gc_untrack, add asserts +# TODO: remove unnecessary stuff, add gc_(un)track asserts init = """ if (Py_IsInitialized()) { PyObject* m; @@ -994,7 +994,7 @@ PyObject *kwds) { Cycle *self; -self = (Cycle *)type->tp_alloc(type, 0); +self = PyObject_GC_New(Cycle, type); if (self != NULL) { self->next = PyString_FromString(""); if (self->next == NULL) { @@ -1074,23 +1074,23 @@ extern PyGC_Head *_pypy_rawrefcount_pyobj_list; - static PyObject * Cycle_Create(Cycle *self, PyObject *val) - { - Cycle *c = PyObject_GC_New(Cycle, ); - if (c == NULL) - return NULL; - - Py_INCREF(val); - c->next = val; +static PyObject * Cycle_Create(Cycle *self, PyObject *val) +{ +Cycle *c = (Cycle *)Cycle_new(, NULL, NULL); +if (c == NULL) + return NULL; + +Py_INCREF(val); +c->next = val; - // TODO: check if _pypy_rawrefcount_pyobj_list contains c +// TODO: check if _pypy_rawrefcount_pyobj_list contains c - return (PyObject *)c; - } - static PyMethodDef module_methods[] = { - {"create", (PyCFunction)Cycle_Create, METH_OLDARGS, ""}, - {NULL} /* Sentinel */ - }; +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) ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Directly call tp_traverse instead of via generic_cpy_call
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95601:fd6699184d11 Date: 2018-03-23 12:53 +0100 http://bitbucket.org/pypy/pypy/changeset/fd6699184d11/ Log:Directly call tp_traverse instead of via generic_cpy_call 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 @@ -83,9 +83,8 @@ pyobj_dealloc_action = PyObjDeallocAction(space) self.dealloc_trigger = lambda: pyobj_dealloc_action.fire() -def _rawrefcount_tp_traverse(space, pyobj_ptr, callback, args): -from pypy.module.cpyext.api import (generic_cpy_call, -PyObject) +def _rawrefcount_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) callback_addr = llmemory.cast_ptr_to_adr(callback) @@ -95,12 +94,10 @@ pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject) # now call tp_traverse (if possible) if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse: -generic_cpy_call(space, pyobj.c_ob_type.c_tp_traverse, - pyobj, - callback_ptr, args) +pyobj.c_ob_type.c_tp_traverse(pyobj, callback_ptr, + args) self.tp_traverse = (lambda o, v, a: -_rawrefcount_tp_traverse(self.space, - o, v, a)) +_rawrefcount_tp_traverse(o, v, a)) def build_api(self): """NOT_RPYTHON ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy unicode-utf8-py3: vastly speed up own tests
Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95618:cc276d4cd166 Date: 2019-01-11 14:05 +0200 http://bitbucket.org/pypy/pypy/changeset/cc276d4cd166/ Log:vastly speed up own tests diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -42,9 +42,10 @@ self._index_storage = rutf8.null_storage() # XXX checking, remove before any performance measurments # ifdef not_running_in_benchmark -if not we_are_translated(): -lgt = rutf8.codepoints_in_utf8(utf8str) -assert lgt == length +# if not we_are_translated(): +#print 'UnicodeObject.__init__' +#lgt = rutf8.codepoints_in_utf8(utf8str) +#assert lgt == length @staticmethod def from_utf8builder(builder): ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy unicode-utf8: remove untranslated check for unicode object creation
Author: Matti Picus Branch: unicode-utf8 Changeset: r95619:baef7e3e3ac0 Date: 2019-01-11 15:56 +0200 http://bitbucket.org/pypy/pypy/changeset/baef7e3e3ac0/ Log:remove untranslated check for unicode object creation diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -41,15 +41,6 @@ self._utf8 = utf8str self._length = length self._index_storage = rutf8.null_storage() -# XXX checking, remove before any performance measurments -# ifdef not_running_in_benchmark -if not we_are_translated(): -try: -lgt = rutf8.check_utf8(utf8str, True) -assert lgt == length -except: -# array.array can return invalid unicode -pass @staticmethod def from_utf8builder(builder): ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Removed unnecessary code
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95603:b74906a7ac4b Date: 2018-05-10 11:12 +0200 http://bitbucket.org/pypy/pypy/changeset/b74906a7ac4b/ Log:Removed unnecessary code 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,6 +43,7 @@ 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 @@ -974,6 +975,8 @@ # we hope that malloc removal removes the newtuple() that is # inserted exactly here by the varargs specializer +print "start to pypy" + # see "Handling of the GIL" above (careful, we don't have the GIL here) tid = rthread.get_or_make_ident() _gil_auto = False @@ -1085,6 +1088,9 @@ rffi.stackcounter.stacks_counter -= 1 _restore_gil_state(pygilstate_release, gilstate, gil_release, _gil_auto, tid) + +print "end to pypy" + return retval wrapper_second_level._dont_inline_ = True @@ -1773,8 +1779,10 @@ preexist_error = PyErr_Occurred(space) try: +print "start cpyext_call" # Call the function result = call_external_function(func, *boxed_args) +print "end cpyext_call" finally: assert cpyext_glob_tid_ptr[0] == tid cpyext_glob_tid_ptr[0] = tid_before diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -105,6 +105,14 @@ "asmgcc": [("translation.gctransformer", "framework"), ("translation.backend", "c")], }), +ChoiceOption("cpyextgc", "Garbage Collection Strategy for cpyext", + ["boehm", "ref", "ref_trialdel", "none"], + default="ref", + requires={ +"boehm": [("translation.gc", "incminimark")], +"ref_trialdel": [("translation.gc", "incminimark")], + }, + cmdline="--cpyextgc"), # other noticeable options BoolOption("thread", "enable use of threading primitives", 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 @@ -177,6 +177,8 @@ defines = defines.copy() if self.config.translation.countmallocs: defines['COUNT_OP_MALLOCS'] = 1 +if self.config.translation.cpyextgc == "boehm": +defines['CPYEXT_BOEHM'] = 1 if self.config.translation.sandbox: defines['RPY_SANDBOXED'] = 1 if CBuilder.have___thread is None: ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Implemented pyobj_list for rawrefcount (to be used in cpyext tests)
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95606:9e5001a6604b Date: 2018-07-05 15:54 +0200 http://bitbucket.org/pypy/pypy/changeset/9e5001a6604b/ Log:Implemented pyobj_list for rawrefcount (to be used in cpyext tests) Added own cpyext test file for GC-related tests 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 @@ -1300,7 +1300,8 @@ ctypes.c_void_p) # initialize the pyobj_list for the gc -space.fromcache(State).C._PyPy_InitPyObjList() +pyobj_list = space.fromcache(State).C._PyPy_InitPyObjList() +rawrefcount._init_pyobj_list(pyobj_list) # we need to call this *after* the init code above, because it might # indirectly call some functions which are attached to pypyAPI (e.g., we 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 @@ -59,28 +59,23 @@ def setup_rawrefcount(self): space = self.space if not self.space.config.translating: -from pypy.module.cpyext.api import PyGC_HeadPtr def dealloc_trigger(): -from pypy.module.cpyext.pyobject import PyObject, decref +from pypy.module.cpyext.pyobject import PyObject, decref, cts print 'dealloc_trigger...' while True: ob = rawrefcount.next_dead(PyObject) if not ob: break -print 'deallocating PyObject', ob +pto = ob.c_ob_type +name = rffi.charp2str(cts.cast('char*', pto.c_tp_name)) +print 'deallocating PyObject', ob, 'of type', name decref(space, ob) print 'dealloc_trigger DONE' return "RETRY" def tp_traverse(obj_addr, callback, args): # TODO: implement pass -# Warning: This list ist different than the list actually used -# by the extension modules (see _PyPy_InitPyObjList). -pyobj_list = lltype.malloc(PyGC_HeadPtr.TO, - flavor='raw', immortal=True, zero=True) -pyobj_list.c_gc_next = rffi.cast(rffi.VOIDP, pyobj_list); -pyobj_list.c_gc_next = rffi.cast(rffi.VOIDP, pyobj_list); -rawrefcount.init(dealloc_trigger, tp_traverse, pyobj_list) +rawrefcount.init(dealloc_trigger, tp_traverse) else: if space.config.translation.gc == "boehm": action = BoehmPyObjDeallocAction(space) diff --git a/pypy/module/cpyext/test/test_cpyext_gc.py b/pypy/module/cpyext/test/test_cpyext_gc.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_cpyext_gc.py @@ -0,0 +1,801 @@ +import sys +import weakref + +import pytest + +from pypy.tool.cpyext.extbuild import ( +SystemCompilationInfo, HERE, get_sys_info_app) +from pypy.interpreter.gateway import unwrap_spec, interp2app +from rpython.rtyper.lltypesystem import lltype, ll2ctypes +from pypy.module.cpyext import api +from pypy.module.cpyext.state import State +from rpython.tool.identity_dict import identity_dict +from rpython.tool import leakfinder +from rpython.rlib import rawrefcount +from rpython.tool.udir import udir + +only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" + +@api.cpython_api([], api.PyObject) +def PyPy_Crash1(space): +1/0 + +@api.cpython_api([], lltype.Signed, error=-1) +def PyPy_Crash2(space): +1/0 + +class SpaceCompiler(SystemCompilationInfo): +"""Extension compiler for regular (untranslated PyPy) mode""" +def __init__(self, space, *args, **kwargs): +self.space = space +SystemCompilationInfo.__init__(self, *args, **kwargs) + +def load_module(self, mod, name): +space = self.space +api.load_extension_module(space, mod, name) +return space.getitem( +space.sys.get('modules'), space.wrap(name)) + + +def get_cpyext_info(space): +from pypy.module.imp.importing import get_so_extension +state = space.fromcache(State) +api_library = state.api_lib +if sys.platform == 'win32': +libraries = [api_library] +# '%s' undefined; assuming extern returning int +compile_extra = ["/we4013"] +# prevent linking with PythonXX.lib +w_maj, w_min = space.fixedview(space.sys.get('version_info'), 5)[:2] +link_extra = ["/NODEFAULTLIB:Python%d%d.lib" % +(space.int_w(w_maj), space.int_w(w_min))] +else: +libraries = [] +if sys.platform.startswith('linux'): +compile_extra = [ +"-Werror", "-g", "-O0", "-Wp,-U_FORTIFY_SOURCE", "-fPIC"] +link_extra = ["-g"] +else: +compile_extra = link_extra = None +return
[pypy-commit] pypy cpyext-gc-cycle: Removed extra flags in cpython refcount
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95605:878ff32d88a1 Date: 2018-07-05 13:01 +0200 http://bitbucket.org/pypy/pypy/changeset/878ff32d88a1/ Log:Removed extra flags in cpython refcount Fixed tests in test_rawrefcount and test_cpyext Removed references from rawrefcount to cpyext Added some comments 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 @@ -1787,10 +1787,10 @@ preexist_error = PyErr_Occurred(space) try: -print "start cpyext_call" +#print "start cpyext_call" # Call the function result = call_external_function(func, *boxed_args) -print "end cpyext_call" +#print "end cpyext_call" finally: assert cpyext_glob_tid_ptr[0] == tid cpyext_glob_tid_ptr[0] = tid_before diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h --- a/pypy/module/cpyext/include/boolobject.h +++ b/pypy/module/cpyext/include/boolobject.h @@ -13,8 +13,8 @@ #define Py_True ((PyObject *) &_Py_TrueStruct) /* Macros for returning Py_True or Py_False, respectively */ -#define Py_RETURN_TRUE do { Py_INCREF(Py_True); return Py_True; } while(0) -#define Py_RETURN_FALSE do { Py_INCREF(Py_False); return Py_False; } while(0) +#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True +#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False #ifdef __cplusplus } 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 @@ -2,7 +2,6 @@ #define Py_OBJECT_H #include -#include #ifdef __cplusplus extern "C" { @@ -13,12 +12,7 @@ #define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1)) #define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1) -#define PY_REFCNT_FROM_PYPY (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 2))) -#define PY_REFCNT_OVERFLOW (1L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 4) - 1L)) -#define PY_REFCNT_MASK ((PY_REFCNT_OVERFLOW << 1L) - 1L) -#define Py_RETURN_NONE return (PyObject *)(Py_None))->ob_refcnt & PY_REFCNT_OVERFLOW) == 0) ? \ - ((PyObject *)(Py_None))->ob_refcnt++ : Py_IncRef((PyObject *)(Py_None))), Py_None - +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None /* CPython has this for backwards compatibility with really old extensions, and now @@ -40,20 +34,14 @@ #define Py_XDECREF(ob) (Py_DecRef((PyObject *)(ob))) #else /* Fast version */ -#define Py_INCREF(ob) do { \ -if (!(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW)) \ -((PyObject *)(ob))->ob_refcnt++; \ -else \ -Py_IncRef((PyObject *)(ob)); \ -} while (0) -#define Py_DECREF(ob) do { \ -if PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW))\ -Py_DecRef((PyObject *)(ob)); \ -else if (--((PyObject *)(ob))->ob_refcnt & PY_REFCNT_MASK) \ -; \ -else \ -_Py_Dealloc((PyObject *)(ob)); \ -} while (0) +#define Py_INCREF(ob) (((PyObject *)(ob))->ob_refcnt++) +#define Py_DECREF(op) \ +do {\ +if (--((PyObject *)(op))->ob_refcnt != 0) \ +; \ +else\ +_Py_Dealloc((PyObject *)(op)); \ +} while (0) #define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0) #define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0) @@ -73,8 +61,7 @@ } \ } while (0) -#define Py_REFCNT(ob) PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW == 0) ? \ - (((PyObject*)(ob))->ob_refcnt & PY_REFCNT_MASK) : _Py_RefCnt_Overflow(ob)) +#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob)(((PyObject*)(ob))->ob_type) #define Py_SIZE(ob)(((PyVarObject*)(ob))->ob_size) diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py ---
[pypy-commit] pypy cpyext-gc-cycle: Fixed cpyext test
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95608:ada4b64c0816 Date: 2018-08-02 10:59 +0200 http://bitbucket.org/pypy/pypy/changeset/ada4b64c0816/ Log:Fixed cpyext test 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 @@ -1069,6 +1069,7 @@ (initproc)Cycle_init, /* tp_init */ 0, /* tp_alloc */ Cycle_new, /* tp_new */ +PyObject_GC_Del, /* tp_free */ }; extern PyGC_Head *_pypy_rawrefcount_pyobj_list; @@ -1078,6 +1079,8 @@ Cycle *c = PyObject_GC_New(Cycle, ); if (c == NULL) return NULL; + + Py_INCREF(val); c->next = val; // TODO: check if _pypy_rawrefcount_pyobj_list contains c @@ -1100,7 +1103,3 @@ 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 ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Fixed dot tests by allocating all PyPy objects old
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95616:ea95f4bc807f Date: 2019-01-10 11:43 +0100 http://bitbucket.org/pypy/pypy/changeset/ea95f4bc807f/ Log:Fixed dot tests by allocating all PyPy objects old Add to stackroots instead of immortal PyPy objects for dot tests Sorted dot tests 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 @@ -64,25 +64,16 @@ refs.append(pyobj_to) pyobj_to.c_ob_refcnt += 1 -def _rawrefcount_pypyobj(self, intval, create_old=False, - create_immortal=False, force_external=False): -if create_immortal: -p1 = lltype.malloc(S, immortal=True) -else: -saved = self.gc.nonlarge_max -try: -if force_external: -self.gc.nonlarge_max = 1 -p1 = self.malloc(S) -finally: -self.gc.nonlarge_max = saved +def _rawrefcount_pypyobj(self, intval, rooted=False, create_old=True): +p1 = self.malloc(S) p1.x = intval -if create_immortal: -self.consider_constant(p1) -elif create_old: + +if create_old: self.stackroots.append(p1) self._collect(major=False) p1 = self.stackroots.pop() +if rooted: +self.stackroots.append(p1) p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1) def check_alive(): @@ -116,7 +107,7 @@ def _rawrefcount_pair(self, intval, is_light=False, is_pyobj=False, create_old=False, create_immortal=False, - force_external=False): + rooted=False, force_external=False): if is_light: rc = REFCNT_FROM_PYPY_LIGHT else: @@ -139,6 +130,8 @@ self.stackroots.append(p1) self._collect(major=False) p1 = self.stackroots.pop() +if rooted: +self.stackroots.append(p1) p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1) r1 = lltype.malloc(PYOBJ_HDR, flavor='raw', immortal=create_immortal) @@ -389,6 +382,7 @@ dot_dir = os.path.join(os.path.realpath(os.path.dirname(__file__)), "dot") dot_files = [file for file in os.listdir(dot_dir) if file.endswith(".dot")] +dot_files.sort() @py.test.mark.dont_track_allocations('intentionally keep objects alive, ' 'because we do the checks ourselves') @@ -433,7 +427,8 @@ g = pydot.graph_from_dot_file(path)[0] nodes = {} -# create objects from graph +# create objects from graph (always create old to prevent moving) +i = 0 for n in g.get_nodes(): name = n.get_name() attr = n.obj_dict['attributes'] @@ -449,15 +444,18 @@ nodes[name] = CPythonNode(r, raddr, check_alive, info) elif type == "P": p, pref, check_alive = \ -self._rawrefcount_pypyobj(42, create_immortal=rooted) +self._rawrefcount_pypyobj(42 + i, rooted=rooted, + create_old=True) nodes[name] = PyPyNode(p, pref, check_alive, info) +i += 1 elif type == "B": p, pref, r, raddr, check_alive =\ -self._rawrefcount_pair(42, create_immortal=rooted) +self._rawrefcount_pair(42 + i, rooted=rooted, + create_old=True) if ext_refcnt > 0: r.c_ob_refcnt = ext_refcnt nodes[name] = BorderNode(p, pref, r, raddr, check_alive, info) -pass +i += 1 # add references between objects from graph for e in g.get_edges(): ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit
[pypy-commit] pypy cpyext-gc-cycle: Added complex rawrefcount tests using dot files
Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95614:2e7b85611e30 Date: 2018-12-21 11:55 +0100 http://bitbucket.org/pypy/pypy/changeset/2e7b85611e30/ Log:Added complex rawrefcount tests using dot files Adapted traverse support in incminimark to support tests diff too long, truncating to 2000 out of 9197 lines 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 @@ -3253,9 +3253,6 @@ else: self._rrc_free(pyobject) -def _rrc_visit_pyobj(self, pyobj): -pass - def _rrc_visit(pyobj, self_ptr): from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance # @@ -3265,13 +3262,18 @@ return rffi.cast(rffi.INT_real, 0) def _rrc_traverse(self, pyobject): +from rpython.rlib.objectmodel import we_are_translated from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr, llhelper) # pyobj = self._pyobj(pyobject) -callback_ptr = llhelper(self.RAWREFCOUNT_VISIT, -IncrementalMiniMarkGC._rrc_visit) -self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self)) +if we_are_translated(): +callback_ptr = llhelper(self.RAWREFCOUNT_VISIT, +IncrementalMiniMarkGC._rrc_visit) +self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self)) +else: +callback_ptr = self._rrc_visit_pyobj +self_ptr = None self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr) def _rrc_gc_list_init(self, pygclist): diff --git a/rpython/memory/gc/test/__init__.py b/rpython/memory/gc/test/dot/__init__.py copy from rpython/memory/gc/test/__init__.py copy to rpython/memory/gc/test/dot/__init__.py diff --git a/rpython/memory/gc/test/dot/dot_parser.py b/rpython/memory/gc/test/dot/dot_parser.py new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/dot_parser.py @@ -0,0 +1,555 @@ +"""Graphviz's dot language parser. + +The dotparser parses GraphViz files in +dot and dot files and transforms them +into a class representation defined by `pydot`. + +Author: Michael Krause +Fixes by: Ero Carrera +""" +from __future__ import division +from __future__ import print_function +import sys + +from pyparsing import ( +nestedExpr, Literal, CaselessLiteral, +Word, OneOrMore, +Forward, +Group, Optional, Combine, +restOfLine, cStyleComment, nums, alphanums, +printables, +ParseException, ParseResults, CharsNotIn, +QuotedString) + +import pydot + +__author__ = ['Michael Krause', 'Ero Carrera'] +__license__ = 'MIT' + + +PY3 = sys.version_info >= (3, 0, 0) +if PY3: +str_type = str +else: +str_type = basestring + + +class P_AttrList(object): + +def __init__(self, toks): + +self.attrs = {} +i = 0 + +while i < len(toks): +attrname = toks[i] +if i+2 < len(toks) and toks[i+1] == '=': +attrvalue = toks[i+2] +i += 3 +else: +attrvalue = None +i += 1 + +self.attrs[attrname] = attrvalue + + +def __repr__(self): + +return "%s(%r)" % (self.__class__.__name__, self.attrs) + + + +class DefaultStatement(P_AttrList): + +def __init__(self, default_type, attrs): + +self.default_type = default_type +self.attrs = attrs + +def __repr__(self): + +return "%s(%s, %r)" % (self.__class__.__name__, +self.default_type, self.attrs) + + +top_graphs = list() + +def push_top_graph_stmt(str, loc, toks): + +attrs = {} +g = None + +for element in toks: + +if (isinstance(element, (ParseResults, tuple, list)) and +len(element) == 1 and +isinstance(element[0], str_type)): + +element = element[0] + +if element == 'strict': +attrs['strict'] = True + +elif element in ['graph', 'digraph']: + +attrs = {} + +g = pydot.Dot(graph_type=element, **attrs) +attrs['type'] = element + +top_graphs.append( g ) + +elif isinstance( element, str_type): +g.set_name( element ) + +elif isinstance(element, pydot.Subgraph): + +g.obj_dict['attributes'].update( element.obj_dict['attributes'] ) +g.obj_dict['edges'].update( element.obj_dict['edges'] ) +g.obj_dict['nodes'].update( element.obj_dict['nodes'] ) +g.obj_dict['subgraphs'].update( element.obj_dict['subgraphs'] ) + +g.set_parent_graph(g) + +elif isinstance(element, P_AttrList): +attrs.update(element.attrs) + +elif isinstance(element, (ParseResults, list)): +add_elements(g, element) + +else: +
[pypy-commit] pypy unicode-utf8-py3: three XXX, will try to fix this weekend
Author: Carl Friedrich Bolz-Tereick Branch: unicode-utf8-py3 Changeset: r95620:1da3240effbd Date: 2019-01-11 22:48 +0100 http://bitbucket.org/pypy/pypy/changeset/1da3240effbd/ Log:three XXX, will try to fix this weekend diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -1171,6 +1171,8 @@ return self.space.utf8_w(wrapped) def is_correct_type(self, w_obj): +# XXX the ascii restriction needs to be lifted, otherwise the +# assumptions about get/setitem_str are just broken space = self.space return type(w_obj) is space.UnicodeObjectCls and w_obj.is_ascii() @@ -1189,11 +1191,14 @@ def setitem_str(self, w_dict, key, w_value): assert key is not None +# XXX this is not valid! UnicodeDictStrategy can right now only store ascii, but +# this path can lead to non-ascii utf8 strings ending up as keys self.unerase(w_dict.dstorage)[self.decodekey_str(key)] = w_value def getitem(self, w_dict, w_key): space = self.space # -- This is called extremely often. Hack for performance -- +# XXX this shortcut looks wrong to me if type(w_key) is space.StringObjectCls: return self.getitem_str(w_dict, w_key.unwrap(space)) # -- End of performance hack -- @@ -1201,6 +1206,7 @@ def getitem_str(self, w_dict, key): assert key is not None +# XXX why can't we just key here? return self.unerase(w_dict.dstorage).get(self.decodekey_str(key), None) def listview_utf8(self, w_dict): ___ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit