summaryrefslogtreecommitdiffstats
path: root/ffi.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2017-05-01 06:52:53 -0700
committerKaz Kylheku <kaz@kylheku.com>2017-05-01 06:52:53 -0700
commit15888fc36f23ff33ec7b780670ef374b5f517c54 (patch)
tree61b3fc666e310ff1be635163cc9116927e452514 /ffi.c
parent1b8f2a5dcbe6211163ebf3495eeb0cd448c838e9 (diff)
downloadtxr-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.c117
1 files changed, 108 insertions, 9 deletions
diff --git a/ffi.c b/ffi.c
index 43ae753c..077751aa 100644
--- a/ffi.c
+++ b/ffi.c
@@ -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);
}