1use std::os::raw::c_void;
2
3use savvy_ffi::SEXP;
4
5extern "C" {
6 fn unwind_protect_impl(
7 fun: Option<unsafe extern "C" fn(data: *mut c_void) -> SEXP>,
8 data: *mut c_void,
9 ) -> SEXP;
10}
11
12pub unsafe fn unwind_protect<F>(f: F) -> crate::error::Result<SEXP>
17where
18 F: FnOnce() -> SEXP + Copy,
19{
20 unsafe {
21 unsafe extern "C" fn do_call<F>(data: *mut c_void) -> SEXP
22 where
23 F: FnOnce() -> SEXP + Copy,
24 {
25 unsafe {
26 let data = data as *const ();
27 let f: &F = &*(data as *const F);
28 f()
29 }
30 }
31
32 let do_call_ptr = std::mem::transmute::<
33 *const (),
34 Option<unsafe extern "C" fn(*mut c_void) -> SEXP>,
35 >(do_call::<F> as *const ());
36 let actual_fn_ptr = std::mem::transmute::<*const F, *mut c_void>(&f as *const F);
37 let res: SEXP = unwind_protect_impl(do_call_ptr, actual_fn_ptr);
38
39 if (res as usize & 1) == 1 {
40 return Err(crate::error::Error::Aborted(res));
41 }
42
43 Ok(res)
44 }
45}