Next: , Up: ffi call   [Index]


11.4.1 Overview of FFI function calls

Both callouts and callbacks are built on top of function signatures specifying the type of function arguments and returned value. Libffi allows us to specify such signatures through a Call InterFace (CIF) data structure; Vicare internally extends such a structure with additional data to speed up marshaling of values from Scheme to native representation and back.

At present, CIF data structures are allocated with malloc() and never released. Callouts and callbacks make use of CIF structures of the same format, so such structures are internally cached; a single CIF associated to a given function signature is used by all the callouts and callbacks having the same signature.

Calling out to foreign functions

Callouts require only a CIF structure and the address of the foreign C function; no additional structure needs to be allocated and then released.

Vicare organises the callout API by creating a callout maker function, closed upon a CIF structure, which can build any number of callout functions associating the CIF to foreign function pointers. Maker functions are cached internally, so only one maker function for a given signature is created in a running process.

Calling back to Scheme functions

Callbacks require an additional data structure, because they must generate at runtime callable machine code referenced by a pointer; such additional data structure cannot be automatically released by the garbage collector, but it can be explicitly freed by the program when the callback is no longer needed.

We can think of the code handling a callback as organised like the following pseudo–code:

cif_t  call_interface;

int
specific_callback (int a, long b, double c)
{
  void *  args[3] = { &a, &b, &c };
  int     result;
  generic_callback(&call_interface, &result, args);
  return result;
}

void
generic_callback (cif_t * call_interface,
                  void * result, void * args)
{
  scheme_value  s_a, s_b, s_c;
  scheme_value  s_result;
  s_a = native_to_scheme(call_interface, 0, args);
  s_b = native_to_scheme(call_interface, 1, args);
  s_c = native_to_scheme(call_interface, 2, args);
  s_result = scheme_callback(s_a, s_b, s_c);
  scheme_to_native(call_interface, result, s_result);
}

scheme_value
scheme_callback (scheme_value s_a,
                 scheme_value s_b,
                 scheme_value s_c)
{
  /* process arguments, return result */
}

where: specific_callback is generated at runtime by Libffi; generic_callback, scheme_to_native and native_to_scheme are implemented by Vicare’s runtime (using generic operations); scheme callback is implemented by the Scheme program. A pointer to specific_callback is the one we need to acquire when creating a callback from Scheme.

Vicare organises the callback API by creating a callback maker function, closed upon a CIF structure, which can build any number of callback functions associating the CIF to Scheme functions. Maker functions are cached internally, so only one maker function for each given signature is created in a running process.


Next: , Up: ffi call   [Index]