[pypy-commit] pypy cffi-new-allocator: in-progress

arigo noreply at buildbot.pypy.org
Mon Jul 6 09:36:36 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-new-allocator
Changeset: r78444:0514cea3666b
Date: 2015-07-06 09:36 +0200
http://bitbucket.org/pypy/pypy/changeset/0514cea3666b/

Log:	in-progress

diff --git a/pypy/module/_cffi_backend/allocator.py b/pypy/module/_cffi_backend/allocator.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/allocator.py
@@ -0,0 +1,48 @@
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
+
+from rpython.rtyper.lltypesystem import lltype, rffi
+
+
+class W_Allocator(W_Root):
+    _immutable_ = True
+
+    def __init__(self, ffi, should_clear_after_alloc):
+        self.ffi = ffi
+        self.should_clear_after_alloc = should_clear_after_alloc
+
+    def allocate(self, space, datasize, ctype, length=-1):
+        from pypy.module._cffi_backend.cdataobj import W_CDataNewStd
+        if self.should_clear_after_alloc:
+            ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
+                                flavor='raw', zero=True)
+        else:
+            ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
+                                flavor='raw', zero=False)
+        return W_CDataNewStd(space, ptr, ctype, length)
+
+    @unwrap_spec(w_init=WrappedDefault(None))
+    def descr_call(self, space, w_arg, w_init):
+        from pypy.module._cffi_backend.ctypeobj import W_CType
+        if isinstance(w_arg, W_CType):
+            w_ctype = w_arg
+        else:
+            ffi = self.ffi
+            if ffi is None:
+                raise oefmt(space.w_TypeError,
+                            "expected a ctype object, got '%T'", w_arg)
+            w_ctype = ffi.ffi_type(w_arg, ffi.ACCEPT_STRING)
+        return w_ctype.newp(w_init, self)
+
+
+W_Allocator.typedef = TypeDef(
+        'FFIAllocator',
+        __call__ = interp2app(W_Allocator.descr_call),
+        )
+W_Allocator.typedef.acceptable_as_base_class = False
+
+
+default_allocator = W_Allocator(ffi=None, should_clear_after_alloc=True)
+nonzero_allocator = W_Allocator(ffi=None, should_clear_after_alloc=False)
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -365,14 +365,13 @@
 
 
 class W_CDataMem(W_CData):
-    """This is the base class used for cdata objects that own and free
-    their memory.  Used directly by the results of cffi.cast('int', x)
-    or other primitive explicitly-casted types.  It is further subclassed
-    by W_CDataNewOwning."""
+    """This is used only by the results of cffi.cast('int', x)
+    or other primitive explicitly-casted types."""
     _attrs_ = []
 
-    def __init__(self, space, size, ctype):
-        cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
+    def __init__(self, space, ctype):
+        cdata = lltype.malloc(rffi.CCHARP.TO, ctype.size, flavor='raw',
+                              zero=False)
         W_CData.__init__(self, space, cdata, ctype)
 
     @rgc.must_be_light_finalizer
@@ -380,34 +379,51 @@
         lltype.free(self._ptr, flavor='raw')
 
 
-class W_CDataNewOwning(W_CDataMem):
-    """This is the class used for the cata objects created by newp()."""
-    _attrs_ = []
+class W_CDataNewOwning(W_CData):
+    """This is the abstract base class used for cdata objects created
+    by newp().  They create and free their own memory according to an
+    allocator."""
+
+    # the 'length' is either >= 0 for arrays, or -1 for pointers.
+    _attrs_ = ['length']
+    _immutable_fields_ = ['length']
+
+    def __init__(self, space, cdata, ctype, length=-1):
+        W_CData.__init__(self, space, cdata, ctype)
+        self.length = length
 
     def _repr_extra(self):
         return self._repr_extra_owning()
 
-
-class W_CDataNewOwningLength(W_CDataNewOwning):
-    """Subclass with an explicit length, for allocated instances of
-    the C type 'foo[]'."""
-    _attrs_ = ['length']
-    _immutable_fields_ = ['length']
-
-    def __init__(self, space, size, ctype, length):
-        W_CDataNewOwning.__init__(self, space, size, ctype)
-        self.length = length
-
     def _sizeof(self):
-        from pypy.module._cffi_backend import ctypearray
         ctype = self.ctype
-        assert isinstance(ctype, ctypearray.W_CTypeArray)
-        return self.length * ctype.ctitem.size
+        if self.length >= 0:
+            from pypy.module._cffi_backend import ctypearray
+            assert isinstance(ctype, ctypearray.W_CTypeArray)
+            return self.length * ctype.ctitem.size
+        else:
+            return ctype.size
 
     def get_array_length(self):
         return self.length
 
 
+class W_CDataNewStd(W_CDataNewOwning):
+    """Subclass using the standard allocator, lltype.malloc()/lltype.free()"""
+    _attrs_ = []
+
+    @rgc.must_be_light_finalizer
+    def __del__(self):
+        lltype.free(self._ptr, flavor='raw')
+
+
+class W_CDataMemNonStd(W_CDataNewOwning):
+    """Subclass using a non-standard allocator"""
+    _attrs_ = []
+
+    # XXXXXXXXX
+
+
 class W_CDataPtrToStructOrUnion(W_CData):
     """This subclass is used for the pointer returned by new('struct foo').
     It has a strong reference to a W_CDataNewOwning that really owns the
diff --git a/pypy/module/_cffi_backend/ctypearray.py b/pypy/module/_cffi_backend/ctypearray.py
--- a/pypy/module/_cffi_backend/ctypearray.py
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -28,7 +28,7 @@
     def _alignof(self):
         return self.ctitem.alignof()
 
-    def newp(self, w_init):
+    def newp(self, w_init, allocator):
         space = self.space
         datasize = self.size
         #
@@ -40,12 +40,10 @@
             except OverflowError:
                 raise OperationError(space.w_OverflowError,
                     space.wrap("array size would overflow a ssize_t"))
-            #
-            cdata = cdataobj.W_CDataNewOwningLength(space, datasize,
-                                                    self, length)
+        else:
+            length = self.length
         #
-        else:
-            cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+        cdata = allocator.allocate(space, datasize, self, length)
         #
         if not space.is_w(w_init, space.w_None):
             with cdata as ptr:
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -55,7 +55,7 @@
     def pack_list_of_items(self, cdata, w_ob):
         return False
 
-    def newp(self, w_init):
+    def newp(self, w_init, allocator):
         space = self.space
         raise oefmt(space.w_TypeError,
                     "expected a pointer or array ctype, got '%s'", self.name)
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
--- a/pypy/module/_cffi_backend/ctypeprim.py
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -63,7 +63,7 @@
             value = self._cast_result(value)
         else:
             value = self._cast_generic(w_ob)
-        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(space, self)
         self.write_raw_integer_data(w_cdata, value)
         return w_cdata
 
@@ -353,7 +353,7 @@
             value = self.cast_unicode(w_ob)
         else:
             value = space.float_w(w_ob)
-        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(space, self)
         if not isinstance(self, W_CTypePrimitiveLongDouble):
             w_cdata.write_raw_float_data(value)
         else:
@@ -446,7 +446,7 @@
         return self.space.wrap(value)
 
     def convert_to_object(self, cdata):
-        w_cdata = cdataobj.W_CDataMem(self.space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(self.space, self)
         with w_cdata as ptr:
             self._copy_longdouble(cdata, ptr)
         return w_cdata
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -185,7 +185,7 @@
         self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid)
         W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem)
 
-    def newp(self, w_init):
+    def newp(self, w_init, allocator):
         from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
         space = self.space
         ctitem = self.ctitem
@@ -205,14 +205,14 @@
                     datasize = ctitem.convert_struct_from_object(
                         lltype.nullptr(rffi.CCHARP.TO), w_init, datasize)
             #
-            cdatastruct = cdataobj.W_CDataNewOwning(space, datasize, ctitem)
+            cdatastruct = allocator.allocate(space, datasize, ctitem)
             ptr = cdatastruct.unsafe_escaping_ptr()
             cdata = cdataobj.W_CDataPtrToStructOrUnion(space, ptr,
                                                        self, cdatastruct)
         else:
             if self.is_char_or_unichar_ptr_or_array():
                 datasize *= 2       # forcefully add a null character
-            cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+            cdata = allocator.allocate(space, datasize, self)
         #
         if not space.is_w(w_init, space.w_None):
             with cdata as ptr:
diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py
--- a/pypy/module/_cffi_backend/ctypestruct.py
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -81,10 +81,9 @@
     def copy_and_convert_to_object(self, source):
         space = self.space
         self.check_complete()
-        ob = cdataobj.W_CDataNewOwning(space, self.size, self)
-        with ob as target:
-            misc._raw_memcopy(source, target, self.size)
-        return ob
+        ptr = lltype.malloc(rffi.CCHARP.TO, self.size, flavor='raw', zero=False)
+        misc._raw_memcopy(source, ptr, self.size)
+        return cdataobj.W_CDataNewStd(space, ptr, self)
 
     def typeoffsetof_field(self, fieldname, following):
         self.force_lazy_struct()
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -14,6 +14,7 @@
 from pypy.module._cffi_backend import cffi_opcode
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.allocator import W_Allocator, default_allocator
 
 
 ACCEPT_STRING   = 1
@@ -44,6 +45,10 @@
 
 
 class W_FFIObject(W_Root):
+    ACCEPT_STRING = ACCEPT_STRING
+    ACCEPT_CTYPE  = ACCEPT_CTYPE
+    ACCEPT_CDATA  = ACCEPT_CDATA
+
     w_gc_wref_remove = None
 
     @jit.dont_look_inside
@@ -414,7 +419,17 @@
 pointer to the memory somewhere else, e.g. into another structure."""
         #
         w_ctype = self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CTYPE)
-        return w_ctype.newp(w_init)
+        return w_ctype.newp(w_init, default_allocator)
+
+
+    @unwrap_spec(should_clear_after_alloc=int)
+    def descr_new_allocator(self, should_clear_after_alloc=1):
+        """\
+Return a new allocator. Xxx
+        """
+        #
+        alloc = W_Allocator(self, bool(should_clear_after_alloc))
+        return self.space.wrap(alloc)
 
 
     def descr_new_handle(self, w_arg):
@@ -600,6 +615,7 @@
         getctype    = interp2app(W_FFIObject.descr_getctype),
         integer_const = interp2app(W_FFIObject.descr_integer_const),
         new         = interp2app(W_FFIObject.descr_new),
+        new_allocator = interp2app(W_FFIObject.descr_new_allocator),
         new_handle  = interp2app(W_FFIObject.descr_new_handle),
         offsetof    = interp2app(W_FFIObject.descr_offsetof),
         sizeof      = interp2app(W_FFIObject.descr_sizeof),
diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -1,13 +1,21 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
 from pypy.module._cffi_backend import ctypeobj, cdataobj
+from pypy.module._cffi_backend.allocator import W_Allocator, default_allocator
 
 
 # ____________________________________________________________
 
 @unwrap_spec(w_ctype=ctypeobj.W_CType, w_init=WrappedDefault(None))
 def newp(space, w_ctype, w_init):
-    return w_ctype.newp(w_init)
+    return w_ctype.newp(w_init, default_allocator)
+
+# ____________________________________________________________
+
+ at unwrap_spec(should_clear_after_alloc=int)
+def new_allocator(space, should_clear_after_alloc):
+    alloc = W_Allocator(None, bool(should_clear_after_alloc))
+    return space.wrap(alloc)
 
 # ____________________________________________________________
 
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -274,3 +274,25 @@
             import gc
             gc.collect()
         assert seen == [1, 1]
+
+    def test_ffi_new_allocator_1(self):
+        import _cffi_backend as _cffi1_backend
+        ffi = _cffi1_backend.FFI()
+        alloc1 = ffi.new_allocator()
+        alloc2 = ffi.new_allocator(should_clear_after_alloc=False)
+        for retry in range(100):
+            p1 = alloc1("int[10]")
+            p2 = alloc2("int[10]")
+            combination = 0
+            for i in range(10):
+                assert p1[i] == 0
+                combination |= p2[i]
+                p1[i] = -42
+                p2[i] = -43
+            if combination != 0:
+                break
+            del p1, p2
+            import gc; gc.collect()
+        else:
+            raise AssertionError("cannot seem to get an int[10] not "
+                                 "completely cleared")
diff --git a/pypy/module/_cffi_backend/wrapper.py b/pypy/module/_cffi_backend/wrapper.py
--- a/pypy/module/_cffi_backend/wrapper.py
+++ b/pypy/module/_cffi_backend/wrapper.py
@@ -9,6 +9,7 @@
 from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
 from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
 from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+from pypy.module._cffi_backend import allocator
 
 
 class W_FunctionWrapper(W_Root):
@@ -74,7 +75,8 @@
             # then internally allocate the struct and pass a pointer to it as
             # a first argument.
             if locs[0] == 'R':
-                w_result_cdata = ctype.fargs[0].newp(space.w_None)
+                w_result_cdata = ctype.fargs[0].newp(space.w_None,
+                                                    allocator.nonzero_allocator)
                 args_w = [w_result_cdata] + args_w
                 prepare_args(space, rawfunctype, args_w, 1)
                 #
@@ -116,7 +118,7 @@
             # the equivalent of ffi.new()
             if space.is_w(w_arg, space.w_None):
                 continue
-            w_arg = farg.newp(w_arg)
+            w_arg = farg.newp(w_arg, allocator.default_allocator)
         args_w[i] = w_arg
 
 


More information about the pypy-commit mailing list