[pypy-commit] pypy cpyext-gc-support: Deallocators

arigo noreply at buildbot.pypy.org
Wed Oct 21 04:50:55 EDT 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cpyext-gc-support
Changeset: r80371:59c2d992bc0e
Date: 2015-10-21 10:48 +0200
http://bitbucket.org/pypy/pypy/changeset/59c2d992bc0e/

Log:	Deallocators

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
@@ -262,6 +262,7 @@
                       rffi.cast(restype, 0) == 0)
 
     def decorate(func):
+        func._always_inline_ = 'try'
         func_name = func.func_name
         if external:
             c_name = None
@@ -355,7 +356,6 @@
                 return res
             unwrapper.func = func
             unwrapper.api_func = api_function
-            unwrapper._always_inline_ = 'try'
             return unwrapper
 
         unwrapper_catch = make_unwrapper(True)
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
@@ -38,7 +38,7 @@
     fill_pyobj: called to fill the PyObject after attaching is done
     alloc_pypy: function called to create a PyPy object from a PyObject
     fill_pypy: called to fill the PyPy object after attaching is done
-    dealloc   : a cpython_api(external=False), similar to PyObject_dealloc
+    dealloc: function called with the pyobj when the refcount drops to zero
     """
 
     tp_basestruct = kw.pop('basestruct', PyObject.TO)
@@ -46,11 +46,11 @@
     tp_fill_pyobj   = kw.pop('fill_pyobj', None)
     tp_alloc_pypy   = kw.pop('alloc_pypy', None)
     tp_fill_pypy    = kw.pop('fill_pypy', None)
+    tp_dealloc      = kw.pop('dealloc', None)
     force_create_pyobj  = kw.pop('force_create_pyobj', False)
     realize_subclass_of = kw.pop('realize_subclass_of', None)
     alloc_pypy_light_if = kw.pop('alloc_pypy_light_if', None)
-    #tp_dealloc    = kw.pop('dealloc', None)
-    assert not kw, "Extra arguments to make_typedescr: %s" % kw.keys()
+    assert not kw, "Extra arguments to setup_class_for_cpyext: %s" % kw.keys()
 
     assert 'cpyext_basestruct' not in W_Class.__dict__    # double set
 
@@ -86,7 +86,7 @@
             keepalive_until_here(self)
         W_Class.cpyext_fill_prebuilt_pyobj = cpyext_fill_prebuilt_pyobj
 
-    if tp_alloc_pyobj or tp_fill_pyobj or realize_subclass_of:
+    if tp_alloc_pyobj or tp_fill_pyobj or realize_subclass_of or tp_dealloc:
         if realize_subclass_of is None:
             realize_subclass_of = W_Class
         #
@@ -116,6 +116,19 @@
         assert 'cpyext_create_pypy' not in typedef.__dict__
         typedef.cpyext_create_pypy = cpyext_create_pypy
 
+        if tp_dealloc:
+            @cpython_api([PyObject], lltype.Void,
+                         external=False, error=CANNOT_FAIL)
+            def dealloc(space, py_obj):
+                tp_dealloc(space, rffi.cast(lltype.Ptr(tp_basestruct), py_obj))
+            #
+            def cpyext_get_dealloc(space):
+                return llhelper(dealloc.api_func.functype,
+                                dealloc.api_func.get_wrapper(space))
+            #
+            assert 'cpyext_get_dealloc' not in typedef.__dict__
+            typedef.cpyext_get_dealloc = cpyext_get_dealloc
+
     W_Class.cpyext_basestruct = tp_basestruct
 
 
@@ -157,10 +170,17 @@
 @bootstrap_function
 def init_pyobject(space):
     setup_class_for_cpyext(W_Root, force_create_pyobj=True,
-                           realize_subclass_of=W_ObjectObject)
+                           realize_subclass_of=W_ObjectObject,
+                           dealloc=_default_dealloc)
     # use this cpyext_create_pypy as the default for all other TypeDefs
     from pypy.interpreter.typedef import TypeDef
-    TypeDef.cpyext_create_pypy = W_ObjectObject.typedef.cpyext_create_pypy
+    TypeDef.cpyext_create_pypy = staticmethod(
+        W_ObjectObject.typedef.cpyext_create_pypy)
+    TypeDef.cpyext_get_dealloc = staticmethod(
+        W_ObjectObject.typedef.cpyext_get_dealloc)
+
+def _default_dealloc(space, py_obj):
+    lltype.free(py_obj, flavor='raw', track_allocation=False)
 
 
 #________________________________________________________
@@ -479,24 +499,6 @@
     #      "'s type which is", rffi.charp2str(pto.c_tp_name)
     generic_cpy_call(space, pto.c_tp_dealloc, obj)
 
-#___________________________________________________________
-# Support for "lifelines"
-#
-# Object structure must stay alive even when not referenced
-# by any C code.
-
-class PyOLifeline(object):
-    def __init__(self, space, pyo):
-        ZZZ
-        self.pyo = pyo
-        self.space = space
-
-    def __del__(self):
-        if self.pyo:
-            assert self.pyo.c_ob_refcnt == 0
-            _Py_Dealloc(self.space, self.pyo)
-            self.pyo = lltype.nullptr(PyObject.TO)
-        # XXX handle borrowed objects here
 
 #___________________________________________________________
 # Support for borrowed references
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -1,7 +1,7 @@
 import py
 
 from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP
+from pypy.module.cpyext.pyobject import PyObject, PyObjectP, debug_collect
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from rpython.rtyper.lltypesystem import rffi, lltype
 
@@ -26,6 +26,14 @@
         w_obj2 = api.from_pyobj(api.PyTuple_GetItem(atuple, 1))
         assert space.eq_w(w_obj1, space.wrap(10))
         assert space.eq_w(w_obj2, space.wrap(11))
+        #
+        # one reference from the PyTupleObject
+        assert api.as_pyobj(w_obj1).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+        assert api.as_pyobj(w_obj2).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT + 1
+        del atuple
+        debug_collect()
+        assert api.as_pyobj(w_obj1).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
+        assert api.as_pyobj(w_obj2).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
 
     def test_tupleobject_spec_oo(self, space, api):
         w_obj1 = space.newlist([])
@@ -34,6 +42,14 @@
         assert api.PyTuple_Size(atuple) == 2
         assert api.from_pyobj(api.PyTuple_GetItem(atuple, 0)) is w_obj1
         assert api.from_pyobj(api.PyTuple_GetItem(atuple, 1)) is w_obj2
+        #
+        # no reference from the PyTupleObject: it is borrowed
+        assert api.as_pyobj(w_obj1).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
+        assert api.as_pyobj(w_obj2).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
+        del atuple
+        debug_collect()
+        assert api.as_pyobj(w_obj1).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
+        assert api.as_pyobj(w_obj2).c_ob_refcnt == REFCNT_FROM_PYPY_LIGHT
 
     def test_new_setitem(self, space, api):
         w_obj1 = space.newlist([])
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,7 +4,7 @@
     cpython_struct, PyVarObjectFields, build_type_checkers3, bootstrap_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
     setup_class_for_cpyext, as_pyobj, get_pyobj_and_incref, from_pyobj,
-    pyobj_has_w_obj, RRC_PERMANENT, RRC_PERMANENT_LIGHT, new_pyobj)
+    pyobj_has_w_obj, RRC_PERMANENT, RRC_PERMANENT_LIGHT, new_pyobj, xdecref)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject, W_AbstractTupleObject
 
@@ -34,6 +34,10 @@
 
         # --and then we call this function to initialize the W_TupleObject--
         fill_pypy=tuple_fill_pypy,
+
+        # --deallocator, *not* called if tuple_alloc_pyobj() made a
+        #   PyTupleObject of borrowed items--
+        dealloc=tuple_dealloc,
         )
 
 def tuple_alloc_pyobj(space, w_obj):
@@ -63,6 +67,11 @@
                  for i in range(py_tuple.c_ob_size)]
     W_TupleObject.__init__(w_obj, objects_w)
 
+def tuple_dealloc(space, py_tup):
+    for i in range(py_tup.c_ob_size):
+        xdecref(space, py_tup.c_ob_item[i])
+    lltype.free(py_tup, flavor='raw', track_allocation=False)
+
 
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
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
@@ -428,7 +428,8 @@
     w_type.cpyext_c_type_object = pto
 
     # dealloc
-    #pto.c_tp_dealloc = typedescr.get_dealloc(space)   ZZZ
+    pto.c_tp_dealloc = w_type.instancetypedef.cpyext_get_dealloc(space)
+
     # buffer protocol
     if space.is_w(w_type, space.w_str):
         setup_string_buffer_procs(space, pto)


More information about the pypy-commit mailing list