So I’ve been trying to get FSBV working on ABCL and I’ve run into a problem with returning structs. The problem is that we allocate memory on the stack for the return struct, successfully call the foreign function, writing the return value into the appropriate place, then we free the memory and return a pointer to the now defunct memory location.
I’m curious as to how this is supposed to work in other implementations. It obviously does work in, at least, SBCL, so I must be doing something wrong. But looking at the ffcall-body-libffi function it looks like we setup the foreign memory for RESULT in with-foreign-objects and then if we have a translatable-foreign-type, we return RESULT, which of course gets freed by with-foreign-objects. What am I missing here?
thanks,
Cyrus
(defun ffcall-body-libffi (function symbols return-type argument-types &optional pointerp (abi :default-abi)) "A body of foreign-funcall calling the libffi function #'call (ffi_call)." (let ((number-of-arguments (length argument-types))) `(with-foreign-objects ((argvalues :pointer ,number-of-arguments) ,@(unless (eql return-type :void) `((result ',return-type)))) (loop :for arg :in (list ,@symbols) :for count :from 0 :do (setf (mem-aref argvalues :pointer count) arg)) (call (prepare-function ,function ',return-type ',argument-types ',abi) ,(if pointerp function `(foreign-symbol-pointer ,function)) ,(if (eql return-type :void) '(null-pointer) 'result) argvalues) ,(if (eql return-type :void) '(values) (if (typep (parse-type return-type) 'translatable-foreign-type) ;; just return the pointer so that expand-from-foreign ;; can apply translate-from-foreign 'result ;; built-in types won't be translated by ;; expand-from-foreign, we have to do it here `(mem-aref result ',return-type))))))
My first cut at this makes me think that the translate-from-foreign and w-f-o should trade places. Here's a selected macroexpansion of GSLL's cx-add:
(LET () (DEFUN CX-ADD (C1 C2) (DECLARE) (LET ((#:CRETURN (WITH-FOREIGN-OBJECT (#:G1705 '(:STRUCT GRID:COMPLEX-DOUBLE-C)) (TRANSLATE-INTO-FOREIGN-MEMORY C1 #<GRID::COMPLEX-DOUBLE-TYPE GRID:COMPLEX-DOUBLE-C> #:G1705) (WITH-FOREIGN-OBJECT (#:G1706 '(:STRUCT GRID:COMPLEX-DOUBLE-C)) (TRANSLATE-INTO-FOREIGN-MEMORY C2 #<GRID::COMPLEX-DOUBLE-TYPE GRID:COMPLEX-DOUBLE-C> #:G1706) (TRANSLATE-FROM-FOREIGN ;;; <=========== this should be after the next two lines (WITH-FOREIGN-OBJECTS ((CFFI::ARGVALUES :POINTER 2) (CFFI::RESULT '(:STRUCT GRID:COMPLEX-DOUBLE-C))) (LOOP :FOR CFFI::ARG :IN (LIST #:G1705 #:G1706) :FOR COUNT :FROM 0 :DO (SETF (MEM-AREF CFFI::ARGVALUES :POINTER COUNT) CFFI::ARG)) (CFFI::CALL (CFFI::PREPARE-FUNCTION "gsl_complex_add" '(:STRUCT GRID:COMPLEX-DOUBLE-C) '((:STRUCT GRID:COMPLEX-DOUBLE-C) (:STRUCT GRID:COMPLEX-DOUBLE-C)) ':DEFAULT-ABI) (FOREIGN-SYMBOL-POINTER "gsl_complex_add") CFFI::RESULT CFFI::ARGVALUES) CFFI::RESULT) #<GRID::COMPLEX-DOUBLE-TYPE GRID:COMPLEX-DOUBLE-C>))))) #:CRETURN)) (MAP-NAME 'CX-ADD "gsl_complex_add") (EXPORT 'CX-ADD))
So that means some mashing around the other definitions, as the translate-from-foreign comes in outside the ffcall-body-libffi. As to why it works in SBCL (and maybe CCL): um, luck?
Liam
On Fri, Apr 18, 2014 at 2:01 PM, Cyrus Harmon ch-lisp@bobobeach.com wrote:
So I've been trying to get FSBV working on ABCL and I've run into a problem with returning structs. The problem is that we allocate memory on the stack for the return struct, successfully call the foreign function, writing the return value into the appropriate place, then we free the memory and return a pointer to the now defunct memory location.
I'm curious as to how this is supposed to work in other implementations. It obviously does work in, at least, SBCL, so I must be doing something wrong. But looking at the ffcall-body-libffi function it looks like we setup the foreign memory for RESULT in with-foreign-objects and then if we have a translatable-foreign-type, we return RESULT, which of course gets freed by with-foreign-objects. What am I missing here?
thanks,
Cyrus
(defun ffcall-body-libffi (function symbols return-type argument-types &optional pointerp (abi :default-abi)) "A body of foreign-funcall calling the libffi function #'call (ffi_call)." (let ((number-of-arguments (length argument-types))) `(with-foreign-objects ((argvalues :pointer ,number-of-arguments) ,@(unless (eql return-type :void) `((result ',return-type)))) (loop :for arg :in (list ,@symbols) :for count :from 0 :do (setf (mem-aref argvalues :pointer count) arg)) (call (prepare-function ,function ',return-type ',argument-types ',abi) ,(if pointerp function `(foreign-symbol-pointer ,function)) ,(if (eql return-type :void) '(null-pointer) 'result) argvalues) ,(if (eql return-type :void) '(values) (if (typep (parse-type return-type) 'translatable-foreign-type) ;; just return the pointer so that expand-from-foreign ;; can apply translate-from-foreign 'result ;; built-in types won't be translated by ;; expand-from-foreign, we have to do it here `(mem-aref result ',return-type)))))) _______________________________________________ Cffi-devel mailing list Cffi-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel
On second thought, that won't work in the case of it not being translatable-foreign-type, because then the mem-aref in the body has already kicked in. I'll have to look at this later.
Liam
And why is that a mem-aref instead of just a mem-ref anyway?
On Apr 18, 2014, at 12:38 PM, Liam Healy lnp@healy.washington.dc.us wrote:
On second thought, that won't work in the case of it not being translatable-foreign-type, because then the mem-aref in the body has already kicked in. I'll have to look at this later.
Liam
I have rewritten ffcall-body-libffi and related definitions so that the expanded forms allocate, translate, and deallocate in the correct order, so this problem should now be fixed. Please checkout the libffi branch and try; if it fixes your problem I will merge it into master.
Liam
On Fri, Apr 18, 2014 at 2:01 PM, Cyrus Harmon ch-lisp@bobobeach.com wrote:
So I’ve been trying to get FSBV working on ABCL and I’ve run into a problem with returning structs. The problem is that we allocate memory on the stack for the return struct, successfully call the foreign function, writing the return value into the appropriate place, then we free the memory and return a pointer to the now defunct memory location.
I’m curious as to how this is supposed to work in other implementations. It obviously does work in, at least, SBCL, so I must be doing something wrong. But looking at the ffcall-body-libffi function it looks like we setup the foreign memory for RESULT in with-foreign-objects and then if we have a translatable-foreign-type, we return RESULT, which of course gets freed by with-foreign-objects. What am I missing here?
thanks,
Cyrus
(defun ffcall-body-libffi (function symbols return-type argument-types &optional pointerp (abi :default-abi)) "A body of foreign-funcall calling the libffi function #'call (ffi_call)." (let ((number-of-arguments (length argument-types))) `(with-foreign-objects ((argvalues :pointer ,number-of-arguments) ,@(unless (eql return-type :void) `((result ',return-type)))) (loop :for arg :in (list ,@symbols) :for count :from 0 :do (setf (mem-aref argvalues :pointer count) arg)) (call (prepare-function ,function ',return-type ',argument-types ',abi) ,(if pointerp function `(foreign-symbol-pointer ,function)) ,(if (eql return-type :void) '(null-pointer) 'result) argvalues) ,(if (eql return-type :void) '(values) (if (typep (parse-type return-type) 'translatable-foreign-type) ;; just return the pointer so that expand-from-foreign ;; can apply translate-from-foreign 'result ;; built-in types won't be translated by ;; expand-from-foreign, we have to do it here `(mem-aref result ',return-type)))))) _______________________________________________ Cffi-devel mailing list Cffi-devel@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/cffi-devel