Skip to content

Instantly share code, notes, and snippets.

@pacmancoder
Last active December 11, 2019 13:15
Show Gist options
  • Save pacmancoder/3c4b39dd966a122106fd051213d9ef8a to your computer and use it in GitHub Desktop.
Save pacmancoder/3c4b39dd966a122106fd051213d9ef8a to your computer and use it in GitHub Desktop.

This example shows how to pass rust closure as C callback without need of Box type (nostd; no liballoc); Created for the the implementation of thread::spawn for FreeRTOS (rust-idf)

Only requirement - malloc and free functions

extern crate libc; // 0.2.65
use core::ffi::c_void as CVoid;
use core::mem::MaybeUninit;
use core::ptr::drop_in_place;
struct CallbackHolder<F: FnOnce()> {
pub callback: Option<F>,
}
// wrapper funtion
fn test_callback_rust<F: FnOnce() + Send + 'static>(callback: F) { unsafe {
let holder = libc::malloc(
std::mem::size_of::<MaybeUninit<CallbackHolder<F>>>()
) as *mut MaybeUninit<CallbackHolder<F>>;
(*holder) = MaybeUninit::new(CallbackHolder{callback: Some(callback)});
test_callback_c(c_fn::<F>, holder as *mut CVoid);
}}
// Proxy function for closure calls
extern "C" fn c_fn<F : FnOnce()>(data: *mut CVoid) { unsafe {
let holder = data as *mut MaybeUninit<CallbackHolder<F>>;
(*(&mut *holder).as_mut_ptr()).callback.take().unwrap()();
drop_in_place((&mut *holder).as_mut_ptr());
libc::free(holder as *mut CVoid);
}}
// Defined in C code
extern "C" fn test_callback_c(callback: extern "C" fn(*mut CVoid), data: *mut CVoid) {
callback(data);
}
struct DropStruct {
val: u32,
}
impl DropStruct {
pub fn new() -> DropStruct {
DropStruct { val: 42 }
}
pub fn print_self(&self) {
println!("I am alive {}!", self.val)
}
}
impl Drop for DropStruct {
fn drop(&mut self) {
println!("I have been dropped {}!", self.val)
}
}
fn main() {
let captured = DropStruct::new();
test_callback_rust(move || {
captured.print_self();
println!("Hello world!");
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment