[pypy-commit] pypy cpyext-gc-cycle: Fixed some (de)allocation bugs in existing and derived gc cpyext-classes

stevie_92 pypy.commits at gmail.com
Fri May 10 02:13:31 EDT 2019


Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96591:0dff3316692e
Date: 2019-05-09 23:32 +0200
http://bitbucket.org/pypy/pypy/changeset/0dff3316692e/

Log:	Fixed some (de)allocation bugs in existing and derived gc cpyext-
	classes

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
@@ -129,7 +129,7 @@
 Py_TPFLAGS_READY Py_TPFLAGS_READYING
 METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE Py_MAX_FMT
 METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O
-Py_TPFLAGS_HEAPTYPE
+Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_GC
 Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_MAX_NDIMS
 Py_CLEANUP_SUPPORTED
 PyBUF_FORMAT PyBUF_ND PyBUF_STRIDES PyBUF_WRITABLE PyBUF_SIMPLE PyBUF_WRITE
@@ -1182,6 +1182,9 @@
         [Py_ssize_t], PyObject,
         compilation_info=eci,
         _nowrapper=True)
+    state.C._PyPy_tuple_free = rffi.llexternal(
+        '_PyPy_tuple_free', [rffi.VOIDP], lltype.Void,
+        compilation_info=eci, _nowrapper=True)
     state.C._PyPy_tuple_dealloc = rffi.llexternal(
         '_PyPy_tuple_dealloc', [PyObject], lltype.Void,
         compilation_info=eci, _nowrapper=True)
diff --git a/pypy/module/cpyext/include/tupleobject.h b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -18,6 +18,7 @@
 
 PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
 PyAPI_FUNC(void) _PyPy_tuple_dealloc(PyObject *);
+PyAPI_FUNC(void) _PyPy_tuple_free(void *);
 
 /* defined in varargswrapper.c */
 PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
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
@@ -7,7 +7,7 @@
 from pypy.module.cpyext.api import (
     cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR,
     CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject,
-    PyVarObject, Py_ssize_t, init_function, cts)
+    PyVarObject, Py_ssize_t, init_function, cts, Py_TPFLAGS_HAVE_GC)
 from pypy.module.cpyext.state import State
 from pypy.objspace.std.typeobject import W_TypeObject
 from pypy.objspace.std.noneobject import W_NoneObject
@@ -77,37 +77,17 @@
         state = space.fromcache(State)
         return state.C._PyPy_subtype_dealloc
 
-    # CCC port to C
+    def get_free(self, space):
+        state = space.fromcache(State)
+        return state.C.PyObject_Free
+
     def allocate(self, space, w_type, itemcount=0, immortal=False):
-        # typically called from PyType_GenericAlloc via typedescr.allocate
-        # this returns a PyObject with ob_refcnt == 1.
-
-        pytype = as_pyobj(space, w_type)
-        pytype = rffi.cast(PyTypeObjectPtr, pytype)
-        assert pytype
-        # Don't increase refcount for non-heaptypes
-        flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
-        if flags & Py_TPFLAGS_HEAPTYPE:
-            incref(space, pytype)
-
-        if pytype:
-            size = pytype.c_tp_basicsize
-        else:
-            size = rffi.sizeof(self.basestruct)
-        if pytype.c_tp_itemsize:
-            size += itemcount * pytype.c_tp_itemsize
-        assert size >= rffi.sizeof(PyObject.TO)
-        buf = lltype.malloc(rffi.VOIDP.TO, size,
-                            flavor='raw', zero=True,
-                            add_memory_pressure=True, immortal=immortal)
-        pyobj = rffi.cast(PyObject, buf)
-        if pytype.c_tp_itemsize:
-            pyvarobj = rffi.cast(PyVarObject, pyobj)
-            pyvarobj.c_ob_size = itemcount
-        pyobj.c_ob_refcnt = 1
-        #pyobj.c_ob_pypy_link should get assigned very quickly
-        pyobj.c_ob_type = pytype
-        return pyobj
+        state = space.fromcache(State)
+        ob_type = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_type))
+        ptup = state.ccall("PyType_GenericAlloc", ob_type, itemcount)
+        if not ptup:
+            state.check_and_raise_exception(always=True)
+        return ptup
 
     def attach(self, space, pyobj, w_obj, w_userdata=None):
         pass
@@ -140,6 +120,7 @@
     attach    : Function called to tie a raw structure to a pypy object
     realize   : Function called to create a pypy object from a raw struct
     dealloc   : a @slot_function(), similar to PyObject_dealloc
+    free      : a @slot_function(), similar to PyObject_free
     """
 
     tp_basestruct = kw.pop('basestruct', PyObject.TO)
@@ -147,6 +128,7 @@
     tp_attach     = kw.pop('attach', None)
     tp_realize    = kw.pop('realize', None)
     tp_dealloc    = kw.pop('dealloc', None)
+    tp_free       = kw.pop('free', None)
     assert not kw, "Extra arguments to make_typedescr"
 
     null_dealloc = lltype.nullptr(lltype.FuncType([PyObject], lltype.Void))
@@ -166,6 +148,10 @@
             def get_dealloc(self, space):
                 return tp_dealloc
 
+        if tp_free:
+            def get_free(self, space):
+                return tp_free
+
         if tp_attach:
             def attach(self, space, pyobj, w_obj, w_userdata=None):
                 tp_attach(space, pyobj, w_obj, w_userdata)
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
@@ -132,7 +132,10 @@
 PyObject *
 PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
 {
-    return (PyObject*)_PyObject_NewVar(type, nitems);
+    if (PyType_IS_GC(type))
+        return (PyObject*)_PyObject_GC_NewVar(type, nitems);
+    else
+        return (PyObject*)_PyObject_NewVar(type, nitems);
 }
 
 PyObject *
diff --git a/pypy/module/cpyext/src/tupleobject.c b/pypy/module/cpyext/src/tupleobject.c
--- a/pypy/module/cpyext/src/tupleobject.c
+++ b/pypy/module/cpyext/src/tupleobject.c
@@ -89,3 +89,9 @@
 done:
     Py_TRASHCAN_SAFE_END(op)
 }
+
+void
+_PyPy_tuple_free(void *obj)
+{
+    PyObject_GC_Del(obj);
+}
\ No newline at end of file
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -822,7 +822,7 @@
     if (PyType_Ready(&UnicodeSubtype3) < 0)
         INITERROR;
 
-    TupleLike.tp_flags = Py_TPFLAGS_DEFAULT;
+    TupleLike.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
     TupleLike.tp_base = &PyTuple_Type;
     if (PyType_Ready(&TupleLike) < 0)
         INITERROR;
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -4,10 +4,11 @@
 from rpython.rlib.debug import fatalerror_notb
 from pypy.module.cpyext.api import (
     cpython_api, Py_ssize_t, build_type_checkers_flags,
-    PyVarObjectFields, cpython_struct, bootstrap_function, slot_function)
+    PyVarObjectFields, cpython_struct, bootstrap_function, slot_function,
+    PyTypeObjectPtr)
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, make_ref, from_ref, decref, incref, BaseCpyTypedescr,
-    track_reference, make_typedescr, get_typedescr, pyobj_has_w_obj)
+    track_reference, make_typedescr, get_typedescr, pyobj_has_w_obj, as_pyobj)
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject
@@ -44,7 +45,8 @@
                    attach=tuple_attach,
                    alloc=tuple_alloc,
                    dealloc=state.C._PyPy_tuple_dealloc,
-                   realize=tuple_realize)
+                   realize=tuple_realize,
+                   free=state.C._PyPy_tuple_free)
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers_flags("Tuple")
 
@@ -65,7 +67,13 @@
             state.check_and_raise_exception(always=True)
         return ptup
     else:
-        return BaseCpyTypedescr.allocate(typedescr, space, w_type, itemcount)
+        if not we_are_translated() and itemcount == _BAD_ITEMCOUNT:
+            itemcount = -42
+        ob_type = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_type))
+        ptup = state.ccall("PyType_GenericAlloc", ob_type, itemcount)
+        if not ptup:
+            state.check_and_raise_exception(always=True)
+        return ptup
 
 def tuple_attach(space, py_obj, w_obj, w_userdata=None):
     """
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -20,7 +20,7 @@
     Py_TPFLAGS_DICT_SUBCLASS, Py_TPFLAGS_BASE_EXC_SUBCLASS,
     Py_TPFLAGS_TYPE_SUBCLASS,
     Py_TPFLAGS_BYTES_SUBCLASS,
-    Py_TPPYPYFLAGS_FLOAT_SUBCLASS,
+    Py_TPPYPYFLAGS_FLOAT_SUBCLASS, Py_TPFLAGS_HAVE_GC
     )
 
 from pypy.module.cpyext.cparser import CTypeSpace
@@ -419,6 +419,8 @@
     if pto.c_tp_itemsize < base_pto.c_tp_itemsize:
         pto.c_tp_itemsize = base_pto.c_tp_itemsize
 
+    pto.c_tp_flags |= (base_pto.c_tp_flags & Py_TPFLAGS_HAVE_GC)
+
     #/* Setup fast subclass flags */
     if space.issubtype_w(w_obj, space.w_BaseException):
         pto.c_tp_flags |= Py_TPFLAGS_BASE_EXC_SUBCLASS
@@ -432,6 +434,7 @@
         pto.c_tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS
     elif space.issubtype_w(w_obj, space.w_tuple):
         pto.c_tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS
+        pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC
     elif space.issubtype_w(w_obj, space.w_list):
         pto.c_tp_flags |= Py_TPFLAGS_LIST_SUBCLASS
     elif space.issubtype_w(w_obj, space.w_dict):
@@ -606,9 +609,9 @@
         pto.c_tp_itemsize = 1
     elif space.is_w(w_type, space.w_tuple):
         pto.c_tp_itemsize = rffi.sizeof(PyObject)
+        pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC
 
     state = space.fromcache(State)
-    pto.c_tp_free = state.C.PyObject_Free
     pto.c_tp_alloc = state.C.PyType_GenericAlloc
     builder = state.builder
     if ((pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) != 0
@@ -643,6 +646,15 @@
             # strange, but happens (ABCMeta)
             pto.c_tp_dealloc = state.C._PyPy_subtype_dealloc
 
+    # free
+    if space.gettypeobject(w_type.layout.typedef) is w_type:
+        # only for the exact type, like 'space.w_tuple' or 'space.w_list'
+        pto.c_tp_free = typedescr.get_free(space)
+    else:
+        pto.c_tp_free = pto.c_tp_base.c_tp_free
+
+    # TODO: traverse (for tuple)
+
     if builder.cpyext_type_init is not None:
         builder.cpyext_type_init.append((pto, w_type))
     else:


More information about the pypy-commit mailing list