1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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")
}
}
}
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());
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 = &*(data as *const F);
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
}
}