extendr_api/
thread_safety.rs1use crate::*;
3use extendr_ffi::{
4 R_MakeUnwindCont, R_UnwindProtect, Rboolean, Rf_error, Rf_protect, Rf_unprotect,
5};
6use std::cell::Cell;
7use std::sync::Mutex;
8
9static R_API_LOCK: Mutex<()> = Mutex::new(());
12
13thread_local! {
14 static THREAD_HAS_LOCK: Cell<bool> = Cell::new(false);
15}
16
17pub fn single_threaded<F, R>(f: F) -> R
24where
25 F: FnOnce() -> R,
26{
27 let has_lock = THREAD_HAS_LOCK.with(|x| x.get());
28
29 let _guard = if !has_lock {
31 Some(R_API_LOCK.lock().unwrap())
32 } else {
33 None
34 };
35
36 THREAD_HAS_LOCK.with(|x| x.set(true));
38
39 let result = f();
40
41 if _guard.is_some() {
43 THREAD_HAS_LOCK.with(|x| x.set(false));
44 }
45
46 result
47}
48
49#[doc(hidden)]
53pub fn handle_panic<F, R>(err_str: &str, f: F) -> R
54where
55 F: FnOnce() -> R,
56 F: std::panic::UnwindSafe,
57{
58 match std::panic::catch_unwind(f) {
59 Ok(res) => res,
60 Err(_) => {
61 let err_str = CString::new(err_str).unwrap();
62 unsafe { Rf_error(err_str.as_ptr()) }
63 }
64 }
65}
66
67static mut R_ERROR_BUF: Option<std::ffi::CString> = None;
68
69pub fn throw_r_error<S: AsRef<str>>(s: S) -> ! {
70 let s = s.as_ref();
71 unsafe {
72 R_ERROR_BUF = Some(std::ffi::CString::new(s).unwrap());
73 Rf_error(R_ERROR_BUF.as_ref().unwrap().as_ptr());
74 };
75}
76
77pub fn catch_r_error<F>(f: F) -> Result<SEXP>
89where
90 F: FnOnce() -> SEXP + Copy,
91 F: std::panic::UnwindSafe,
92{
93 use std::os::raw;
94
95 unsafe extern "C" fn do_call<F>(data: *mut raw::c_void) -> SEXP
96 where
97 F: FnOnce() -> SEXP + Copy,
98 {
99 let data = data as *const ();
100 let f: &F = &*(data as *const F);
101 f()
102 }
103
104 unsafe extern "C" fn do_cleanup(_: *mut raw::c_void, jump: Rboolean) {
105 if jump != Rboolean::FALSE {
106 panic!("R has thrown an error.");
107 }
108 }
109
110 single_threaded(|| unsafe {
111 let fun_ptr = do_call::<F> as *const ();
112 let clean_ptr = do_cleanup as *const ();
113 let x = false;
114 let fun = std::mem::transmute(fun_ptr);
115 let cleanfun = std::mem::transmute(clean_ptr);
116 let data = &f as *const _ as _;
117 let cleandata = &x as *const _ as _;
118 let cont = R_MakeUnwindCont();
119 Rf_protect(cont);
120
121 let res = match std::panic::catch_unwind(|| {
123 R_UnwindProtect(fun, data, cleanfun, cleandata, cont)
124 }) {
125 Ok(res) => Ok(res),
126 Err(_) => Err("Error in protected R code".into()),
127 };
128 Rf_unprotect(1);
129 res
130 })
131}