savvy/
unwind_protect.rs

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
12/// # Safety
13///
14/// This function wraps around `R_UnwindProtect()` API, which is very unsafe in
15/// its nature. So, please use this with care.
16pub 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}