[pypy-commit] pypy default: we cannot immediately free the temp buffers that we allocate to convert strings, because the caller needs to have a chance to play with them in case they have been returned; instead, the temp buffers are owned by the function itself, and needs to be explicitly freed
antocuni
noreply at buildbot.pypy.org
Thu Jul 28 14:05:50 CEST 2011
Author: Antonio Cuni <anto.cuni at gmail.com>
Branch:
Changeset: r46037:ddc4544bf357
Date: 2011-07-28 14:03 +0200
http://bitbucket.org/pypy/pypy/changeset/ddc4544bf357/
Log: we cannot immediately free the temp buffers that we allocate to
convert strings, because the caller needs to have a chance to play
with them in case they have been returned; instead, the temp buffers
are owned by the function itself, and needs to be explicitly freed
diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_ffi.py
--- a/pypy/module/_ffi/interp_ffi.py
+++ b/pypy/module/_ffi/interp_ffi.py
@@ -11,6 +11,7 @@
from pypy.rlib import libffi
from pypy.rlib.rdynload import DLOpenError
from pypy.rlib.rarithmetic import intmask, r_uint
+from pypy.rlib.objectmodel import we_are_translated
class W_FFIType(Wrappable):
@@ -181,6 +182,7 @@
self.func = func
self.argtypes_w = argtypes_w
self.w_restype = w_restype
+ self.to_free = []
@jit.unroll_safe
def build_argchain(self, space, args_w):
@@ -195,8 +197,6 @@
self.func.name, expected, arg, given)
#
argchain = libffi.ArgChain()
- to_free = [] # list of automatically malloc()ed buffers that needs to
- # be freed after the call
for i in range(expected):
w_argtype = self.argtypes_w[i]
w_arg = args_w[i]
@@ -207,7 +207,7 @@
self.arg_longlong(space, argchain, w_arg)
elif w_argtype.is_signed():
argchain.arg(unwrap_truncate_int(rffi.LONG, space, w_arg))
- elif self.add_char_p_maybe(space, argchain, to_free, w_arg, w_argtype):
+ elif self.add_char_p_maybe(space, argchain, w_arg, w_argtype):
# the argument is added to the argchain direcly by the method above
pass
elif w_argtype.is_pointer():
@@ -232,9 +232,9 @@
argchain.arg_raw(ptrval)
else:
assert False, "Argument shape '%s' not supported" % w_argtype
- return argchain, to_free
+ return argchain
- def add_char_p_maybe(self, space, argchain, to_free, w_arg, w_argtype):
+ def add_char_p_maybe(self, space, argchain, w_arg, w_argtype):
"""
Automatic conversion from string to char_p. The allocated buffer will
be automatically freed after the call.
@@ -243,7 +243,7 @@
if w_argtype.is_char_p() and w_type is space.w_str:
strval = space.str_w(w_arg)
buf = rffi.str2charp(strval)
- to_free.append(rffi.cast(rffi.VOIDP, buf))
+ self.to_free.append(rffi.cast(rffi.VOIDP, buf))
addr = rffi.cast(rffi.ULONG, buf)
argchain.arg(addr)
return True
@@ -251,7 +251,7 @@
w_type is space.w_unicode):
unicodeval = space.unicode_w(w_arg)
buf = rffi.unicode2wcharp(unicodeval)
- to_free.append(rffi.cast(rffi.VOIDP, buf))
+ self.to_free.append(rffi.cast(rffi.VOIDP, buf))
addr = rffi.cast(rffi.ULONG, buf)
argchain.arg(addr)
return True
@@ -279,12 +279,16 @@
def call(self, space, args_w):
self = jit.promote(self)
- argchain, to_free = self.build_argchain(space, args_w)
- try:
- return self._do_call(space, argchain)
- finally:
- for buf in to_free:
- lltype.free(buf, flavor='raw')
+ argchain = self.build_argchain(space, args_w)
+ return self._do_call(space, argchain)
+
+ def free_temp_buffers(self, space):
+ for buf in self.to_free:
+ if not we_are_translated():
+ buf[0] = '\00' # invalidate the buffer, so that
+ # test_keepalive_temp_buffer can fail
+ lltype.free(buf, flavor='raw')
+ self.to_free = []
def _do_call(self, space, argchain):
w_restype = self.w_restype
@@ -424,6 +428,7 @@
'_ffi.FuncPtr',
__call__ = interp2app(W_FuncPtr.call),
getaddr = interp2app(W_FuncPtr.getaddr),
+ free_temp_buffers = interp2app(W_FuncPtr.free_temp_buffers),
fromaddr = interp2app(descr_fromaddr, as_classmethod=True)
)
diff --git a/pypy/module/_ffi/test/test__ffi.py b/pypy/module/_ffi/test/test__ffi.py
--- a/pypy/module/_ffi/test/test__ffi.py
+++ b/pypy/module/_ffi/test/test__ffi.py
@@ -210,6 +210,7 @@
mystr = CharArray(7, 'foobar')
assert mystrlen(mystr.buffer) == 6
mystr.free()
+ mystrlen.free_temp_buffers()
def test_convert_unicode_to_unichar_p(self):
"""
@@ -235,6 +236,25 @@
mystr = UniCharArray(7, u'foobar')
assert mystrlen(mystr.buffer) == 6
mystr.free()
+ mystrlen.free_temp_buffers()
+
+ def test_keepalive_temp_buffer(self):
+ """
+ char* do_nothing(char* s)
+ {
+ return s;
+ }
+ """
+ from _ffi import CDLL, types
+ import _rawffi
+ libfoo = CDLL(self.libfoo_name)
+ do_nothing = libfoo.getfunc('do_nothing', [types.char_p], types.char_p)
+ CharArray = _rawffi.Array('c')
+ #
+ ptr = do_nothing('foobar')
+ array = CharArray.fromaddress(ptr, 7)
+ assert list(array) == list('foobar\00')
+ do_nothing.free_temp_buffers()
def test_typed_pointer(self):
from _ffi import types
More information about the pypy-commit
mailing list