diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2017-05-01 06:52:53 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2017-05-01 06:52:53 -0700 |
commit | 15888fc36f23ff33ec7b780670ef374b5f517c54 (patch) | |
tree | 61b3fc666e310ff1be635163cc9116927e452514 /ffi.c | |
parent | 1b8f2a5dcbe6211163ebf3495eeb0cd448c838e9 (diff) | |
download | txr-15888fc36f23ff33ec7b780670ef374b5f517c54.tar.gz txr-15888fc36f23ff33ec7b780670ef374b5f517c54.tar.bz2 txr-15888fc36f23ff33ec7b780670ef374b5f517c54.zip |
ffi: memory handling in callback interface.
We need to extend the existing framework to handle the
requirement that the callback can update objects passed by
pointer, such that the changes are propagated to the original
C objects. For instance, a callback receives a pointer to a
struct with some strings. The Lisp code updates the strings
and we would like the strings to appear back in the C code.
The functions which support this sort of thing in the
call direction are not quite suitable for this purpose,
because that system relies on being able to nail temporary
buffers into a stack-allocated rtvec[] which is cleaned
up after the FFI call. Anything we write back to a C
structure in a FFI callback is is gone out the door; we
don't get to clean up any temporary malloced storage.
* ffi.c (struct txr_ffi_type): New member out: a virtual
fucnction which is like in, but in the opposite direction,
and not relying in rtvec. The copy flag argument
tells the function whether to just recurse, or whether
perform copying, or just recursion.
(ffi_ptr_in_out, ffi_ptr_out_out, ffi_struct_out,
ffi_array_out): New static functions.
(make_ffi_type_pointer): Take an a new parameter for
specifying out functions, and store it.
(make_ffi_type_struct): Set up an out handler for a struct
type or array which needs one. It is needed if any of the
element types have an out handler. Basic types don't have out
handlers; ptr types do. What if a struct is full of basic
types and has no out handler, and is the object of an out
pointer? In that case, the logic in ffi_ptr_out_out handles
it; it notices that its target type has no out handler, and
uses the struct's put function instead to update the
underlying C buffer.
(ffi_type_compile): Add in functions to ptr types.
(ffi_closure_dispatch): After calling the Lisp function,
perform a pass of out calls over the parameters, if
necessary.
Diffstat (limited to 'ffi.c')
-rw-r--r-- | ffi.c | 117 |
1 files changed, 108 insertions, 9 deletions
@@ -97,6 +97,7 @@ struct txr_ffi_type { val (*get)(struct txr_ffi_type *, mem_t *src, val self); void (*in)(struct txr_ffi_type *, mem_t *src, val obj, mem_t *rtvec[], val self); + void (*out)(struct txr_ffi_type *, int copy, val obj, mem_t *dest, val self); mem_t *(*alloc)(struct txr_ffi_type *, val obj, val self); void (*free)(void *); }; @@ -852,6 +853,17 @@ static void ffi_ptr_in_put(struct txr_ffi_type *tft, val s, mem_t *dst, } } +static void ffi_ptr_in_out(struct txr_ffi_type *tft, int copy, val s, + mem_t *dst, val self) +{ + val tgttype = tft->mtypes; + struct txr_ffi_type *tgtft = ffi_type_struct(tgttype); + if (tgtft->out != 0) { + mem_t *buf = *coerce(mem_t **, dst); + tgtft->out(tgtft, 0, s, buf, self); + } +} + static void ffi_ptr_in_d_put(struct txr_ffi_type *tft, val s, mem_t *dst, mem_t *rtvec[], val self) { @@ -894,6 +906,18 @@ static void ffi_ptr_out_put(struct txr_ffi_type *tft, val s, mem_t *dst, } } +static void ffi_ptr_out_out(struct txr_ffi_type *tft, int copy, val s, + mem_t *dst, val self) +{ + val tgttype = tft->mtypes; + struct txr_ffi_type *tgtft = ffi_type_struct(tgttype); + mem_t *buf = *coerce(mem_t **, dst); + if (tgtft->out != 0) + tgtft->out(tgtft, 1, s, buf, self); + else + tgtft->put(tgtft, s, buf, 0, self); +} + static val ffi_ptr_get(struct txr_ffi_type *tft, mem_t *src, val self) { val tgttype = tft->mtypes; @@ -990,6 +1014,31 @@ static void ffi_struct_put(struct txr_ffi_type *tft, val strct, mem_t *dst, } } +static void ffi_struct_out(struct txr_ffi_type *tft, int copy, val strct, + mem_t *dst, val self) +{ + val slots = tft->mnames; + val types = tft->mtypes; + ucnum offs = 0; + + while (types) { + val slsym = pop(&slots); + val type = pop(&types); + struct txr_ffi_type *mtft = ffi_type_struct(type); + ucnum almask = mtft->align - 1; + offs = (offs + almask) & ~almask; + if (mtft->out != 0) { + if (slsym) { + val slval = slot(strct, slsym); + mtft->out(mtft, copy, slval, dst + offs, self); + } else { + memset(dst + offs, 0, mtft->size); + } + } + offs += mtft->size; + } +} + static val ffi_struct_get(struct txr_ffi_type *tft, mem_t *src, val self) { val slots = tft->mnames; @@ -1058,6 +1107,30 @@ static void ffi_array_put(struct txr_ffi_type *tft, val vec, mem_t *dst, } } +static void ffi_array_out(struct txr_ffi_type *tft, int copy, val vec, + mem_t *dst, val self) +{ + val eltypes = tft->mtypes; + cnum nelem = tft->nelem, i; + int nt = tft->null_term; + ucnum offs = 0; + + for (i = 0; i < nelem; i++) { + val eltype = pop(&eltypes); + struct txr_ffi_type *etft = ffi_type_struct(eltype); + cnum elsize = etft->size; + if (nt && i == nelem - 1) { + memset(dst + offs, 0, elsize); + break; + } + if (etft->out != 0) { + val elval = ref(vec, num_fast(i)); + etft->out(etft, copy, elval, dst + offs, self); + } + offs += elsize; + } +} + static val ffi_array_get(struct txr_ffi_type *tft, mem_t *src, val self) { val eltypes = tft->mtypes; @@ -1141,6 +1214,8 @@ static val make_ffi_type_pointer(val syntax, val lisp_type, mem_t *src, val self), void (*in)(struct txr_ffi_type *, mem_t *src, val obj, mem_t *rtvec[], val self), + void (*out)(struct txr_ffi_type *, int copy, + val obj, mem_t *dst, val self), val tgtype) { struct txr_ffi_type *tft = coerce(struct txr_ffi_type *, @@ -1159,6 +1234,7 @@ static val make_ffi_type_pointer(val syntax, val lisp_type, tft->get = get; tft->mtypes = tgtype; tft->in = in; + tft->out = out; tft->alloc = ffi_fixed_alloc; tft->free = free; @@ -1178,6 +1254,7 @@ static val make_ffi_type_struct(val syntax, val lisp_type, val obj = cobj(coerce(mem_t *, tft), ffi_type_s, &ffi_type_struct_ops); cnum total_size = 0; cnum most_align = 0; + int need_out_handler = 0; ft->type = FFI_TYPE_STRUCT; ft->size = 0; @@ -1206,10 +1283,14 @@ static val make_ffi_type_struct(val syntax, val lisp_type, most_align = align; total_size = (total_size + align - 1) / align * align + size; + need_out_handler = need_out_handler || mtft->out != 0; } elements[i] = 0; + if (need_out_handler) + tft->out = ffi_struct_out; + ft->elements = elements; total_size = (total_size + most_align - 1) / most_align * most_align; @@ -1254,6 +1335,8 @@ static val make_ffi_type_array(val syntax, val lisp_type, if (i == 0) { tft->size = etft->size * nelem; tft->align = etft->align; + if (etft->out != 0) + tft->out = ffi_array_out; } } @@ -1339,31 +1422,36 @@ val ffi_type_compile(val syntax) return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *), &ffi_type_pointer, ffi_ptr_in_put, ffi_ptr_get, - ffi_ptr_in_in, target_type); + ffi_ptr_in_in, ffi_ptr_in_out, + target_type); } else if (sym == ptr_in_d_s) { val target_type = ffi_type_compile(cadr(syntax)); return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *), &ffi_type_pointer, ffi_ptr_in_d_put, ffi_ptr_d_get, - 0, target_type); + 0, ffi_ptr_in_out, + target_type); } else if (sym == ptr_out_s) { val target_type = ffi_type_compile(cadr(syntax)); return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *), &ffi_type_pointer, ffi_ptr_out_put, ffi_ptr_get, - ffi_ptr_out_in, target_type); + ffi_ptr_out_in, ffi_ptr_out_out, + target_type); } else if (sym == ptr_out_d_s) { val target_type = ffi_type_compile(cadr(syntax)); return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *), &ffi_type_pointer, ffi_ptr_out_put, ffi_ptr_d_get, - ffi_ptr_out_in, target_type); + ffi_ptr_out_in, ffi_ptr_out_out, + target_type); } else if (sym == ptr_s) { val target_type = ffi_type_compile(cadr(syntax)); return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *), &ffi_type_pointer, ffi_ptr_put, ffi_ptr_get, - ffi_ptr_out_in, target_type); + ffi_ptr_out_in, ffi_ptr_out_out, + target_type); } else if (sym == buf_s || sym == buf_d_s) { cnum nelem = c_num(cadr(syntax)); val type = make_ffi_type_builtin(syntax, cptr_s, sizeof (mem_t *), @@ -1684,21 +1772,32 @@ static void ffi_closure_dispatch(ffi_cif *cif, void *cret, val rtype = tfcd->rettype; struct txr_ffi_type *rtft = ffi_type_struct(rtype); val retval = nil; + int out_pass_needed = 0; args_decl(args, tfcl->nparam); + args_decl(args_cp, tfcl->nparam); for (i = 0; i < nargs; i++) { val type = pop(&types); struct txr_ffi_type *mtft = ffi_type_struct(type); val arg = mtft->get(mtft, convert(mem_t *, cargs[i]), self); args_add(args, arg); + if (mtft->out != 0) + out_pass_needed = 1; } + args_copy(args_cp, args); + retval = generic_funcall(tfcl->fun, args); - /* rtvec is purposely null in the following call. - * if the put placed any pointers into rtvec, it would - * be wrong. - */ + if (out_pass_needed) { + for (types = tfcd->argtypes, i = 0; i < nargs; i++) { + val type = pop(&types); + val arg = args_at(args_cp, i); + struct txr_ffi_type *mtft = ffi_type_struct(type); + mtft->out(mtft, 0, arg, convert(mem_t *, cargs[i]), self); + } + } + rtft->put(rtft, retval, convert(mem_t *, cret), 0, self); } |