extendr_api/
thread_safety.rsuse crate::*;
use std::cell::Cell;
use std::sync::Mutex;
static R_API_LOCK: Mutex<()> = Mutex::new(());
thread_local! {
static THREAD_HAS_LOCK: Cell<bool> = Cell::new(false);
}
pub fn single_threaded<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let has_lock = THREAD_HAS_LOCK.with(|x| x.get());
let _guard = if !has_lock {
Some(R_API_LOCK.lock().unwrap())
} else {
None
};
THREAD_HAS_LOCK.with(|x| x.set(true));
let result = f();
if _guard.is_some() {
THREAD_HAS_LOCK.with(|x| x.set(false));
}
result
}
#[doc(hidden)]
pub fn handle_panic<F, R>(err_str: &str, f: F) -> R
where
F: FnOnce() -> R,
F: std::panic::UnwindSafe,
{
match std::panic::catch_unwind(f) {
Ok(res) => res,
Err(_) => {
let err_str = CString::new(err_str).unwrap();
unsafe { libR_sys::Rf_error(err_str.as_ptr()) }
}
}
}
static mut R_ERROR_BUF: Option<std::ffi::CString> = None;
pub fn throw_r_error<S: AsRef<str>>(s: S) -> ! {
let s = s.as_ref();
unsafe {
R_ERROR_BUF = Some(std::ffi::CString::new(s).unwrap());
libR_sys::Rf_error(R_ERROR_BUF.as_ref().unwrap().as_ptr());
};
}
pub fn catch_r_error<F>(f: F) -> Result<SEXP>
where
F: FnOnce() -> SEXP + Copy,
F: std::panic::UnwindSafe,
{
use std::os::raw;
unsafe extern "C" fn do_call<F>(data: *mut raw::c_void) -> SEXP
where
F: FnOnce() -> SEXP + Copy,
{
let data = data as *const ();
let f: &F = &*(data as *const F);
f()
}
unsafe extern "C" fn do_cleanup(_: *mut raw::c_void, jump: Rboolean) {
if jump != Rboolean::FALSE {
panic!("R has thrown an error.");
}
}
single_threaded(|| unsafe {
let fun_ptr = do_call::<F> as *const ();
let clean_ptr = do_cleanup as *const ();
let x = false;
let fun = std::mem::transmute(fun_ptr);
let cleanfun = std::mem::transmute(clean_ptr);
let data = &f as *const _ as _;
let cleandata = &x as *const _ as _;
let cont = R_MakeUnwindCont();
Rf_protect(cont);
let res = match std::panic::catch_unwind(|| {
R_UnwindProtect(fun, data, cleanfun, cleandata, cont)
}) {
Ok(res) => Ok(res),
Err(_) => Err("Error in protected R code".into()),
};
Rf_unprotect(1);
res
})
}