use crate::*;
use std::sync::atomic::{AtomicU32, Ordering};
static OWNER_THREAD: AtomicU32 = AtomicU32::new(0);
static NEXT_THREAD_ID: AtomicU32 = AtomicU32::new(1);
thread_local! {
static THREAD_ID: u32 = NEXT_THREAD_ID.fetch_add(1, Ordering::SeqCst);
}
pub fn this_thread_id() -> u32 {
THREAD_ID.with(|&v| v)
}
pub fn single_threaded<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let id = this_thread_id();
let old_id = OWNER_THREAD.load(Ordering::Acquire);
if old_id != id {
while OWNER_THREAD
.compare_exchange(0, id, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
std::thread::sleep(std::time::Duration::from_millis(1));
}
}
let res = f();
if old_id != id {
OWNER_THREAD.store(0, Ordering::Release);
}
res
}
#[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(_) => {
unsafe {
libR_sys::Rf_error(err_str.as_ptr() as *const std::os::raw::c_char);
}
unreachable!("handle_panic unreachable")
}
}
}
pub fn throw_r_error<S: AsRef<str>>(s: S) {
let s = s.as_ref();
unsafe {
let cstring = std::ffi::CString::new(s).unwrap();
libR_sys::Rf_error(cstring.as_ptr());
unreachable!("Rf_error does not return");
};
}
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 = std::mem::transmute(data);
f()
}
unsafe extern "C" fn do_cleanup(_: *mut raw::c_void, jump: Rboolean) {
if jump != 0 {
panic!("R has thrown an error.");
}
}
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 = std::mem::transmute(&f);
let cleandata = std::mem::transmute(&x);
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
}
}