I am attempting to retrieve a Vec of backend name strings from a C function. The function signature is as follows:
// Invokes a callback with each registered backend.
void EnumerateBackends(
void (*callback)(const char *backend, void *userData),
session_t session,
void *userData
);
Upon investigation, it appears that the callback function is only called while the EnumerateBackends function is still executing. I have tried defining
However, this solution can lead to race conditions if multiple sessions are spawned concurrently. I also attempted to use the libffi-rs to pass FnMut closures into the FFI function, but encountered some unusual errors. Do you have any suggestions for a robust way to solve this problem?
Closures in Rust may have additional context data, which makes them incompatible with context-free C function pointers.
You will need to use user_data to pass the closure, and the function pointer needs to be a C function that will cast user_data to the closure type and call it.
You can't use Box<dyn Fn> or &mut dyn Fn as the user_data pointer, because this is a fat pointer, and C only supports thin pointers (you could pass a pointer to a fat pointer (double indirection)).
Here's the third version of the code I wrote for it. It was cumbersome to make it safe and pass Miri:
use std::os::raw::c_void;
use std::marker::PhantomData;
pub struct CCallback<'closure, Arg, Ret> {
pub function: unsafe extern "C-unwind" fn(*mut c_void, Arg) -> Ret,
pub user_data: *mut c_void,
// without this it's too easy to accidentally drop the closure too soon
_lifetime: PhantomData<&'closure mut c_void>,
}
impl<'closure, Arg, Ret> CCallback<'closure, Arg, Ret> {
pub fn new<F>(closure: &'closure mut F) -> Self where F: FnMut(Arg) -> Ret {
let function: unsafe extern "C-unwind" fn(*mut c_void, Arg) -> Ret = Self::call_closure::<F>;
debug_assert_eq!(std::mem::size_of::<&'closure mut F>(), std::mem::size_of::<*const c_void>());
debug_assert_eq!(std::mem::size_of_val(&function), std::mem::size_of::<*const c_void>());
Self {
function,
user_data: closure as *mut F as *mut c_void,
_lifetime: PhantomData,
}
}
unsafe extern "C-unwind" fn call_closure<F>(user_data: *mut c_void, arg: Arg) -> Ret where F: FnMut(Arg) -> Ret {
let cb: &mut F = user_data.cast::<F>().as_mut().unwrap();
(*cb)(arg)
}
}
fn main() {
let mut v = Vec::new();
// must assign to a variable to ensure it lives until the end of scope
let closure = &mut |x: i32| { v.push(x) };
let c = CCallback::new(closure);
unsafe { (c.function)(c.user_data, 123) };
assert_eq!(v, [123]);
}