[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