Skip to content

Commit b668a16

Browse files
committed
Implement r_safely! in harp and use it in session API
1 parent e1e2f49 commit b668a16

File tree

5 files changed

+61
-39
lines changed

5 files changed

+61
-39
lines changed

crates/ark/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ pub use r_task::r_task;
2929
use serde::Deserialize;
3030
use serde::Serialize;
3131

32+
#[macro_export]
33+
macro_rules! r_safely {
34+
($($expr:tt)*) => {{
35+
#[allow(unused_unsafe)]
36+
ark::r_task::safely(|| {
37+
unsafe { $($expr)* } }
38+
)
39+
}}
40+
}
41+
3242
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Deserialize, Serialize)]
3343
pub struct Position {
3444
row: usize,

crates/ark/src/r_task.rs

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{
1010
time::Duration,
1111
};
1212

13-
use libR_sys::R_interrupts_suspended;
13+
use harp::exec::safely;
1414

1515
extern "C" {
1616
pub static mut R_PolledEvents: Option<unsafe extern "C" fn()>;
@@ -119,38 +119,5 @@ fn acquire_r_main() -> &'static mut RMain {
119119
}
120120
}
121121

122-
// TODO: Should probably run in a toplevel-exec. Tasks also need a timeout.
123-
// This could be implemented with R interrupts but would require to
124-
// unsafely jump over the Rust stack, unless we wrapped all R API functions
125-
// to return an Option.
126-
fn safely<'env, F, T>(f: F) -> T
127-
where
128-
F: FnOnce() -> T,
129-
F: 'env + Send,
130-
T: 'env + Send,
131-
{
132-
let polled_events = unsafe { R_PolledEvents };
133-
let interrupts_suspended = unsafe { R_interrupts_suspended };
134-
unsafe {
135-
// Disable polled events in this scope.
136-
R_PolledEvents = Some(r_polled_events_disabled);
137-
138-
// Disable interrupts in this scope.
139-
R_interrupts_suspended = 1;
140-
}
141-
142-
// Execute the callback.
143-
let result = f();
144-
145-
// Restore state
146-
// TODO: Needs unwind protection
147-
unsafe {
148-
R_interrupts_suspended = interrupts_suspended;
149-
R_PolledEvents = polled_events;
150-
}
151-
152-
result
153-
}
154-
155122
// Tests are tricky because `harp::test::start_r()` is very bare bones and
156123
// doesn't have an `R_MAIN`.

crates/harp/src/exec.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use libR_sys::*;
1515

1616
use crate::error::Error;
1717
use crate::error::Result;
18+
use crate::lock::r_polled_events_disabled;
19+
use crate::lock::R_PolledEvents;
1820
use crate::object::RObject;
1921
use crate::protect::RProtect;
2022
use crate::r_string;
@@ -418,6 +420,39 @@ pub fn r_parse(code: &str) -> Result<RObject> {
418420
}
419421
}
420422

423+
// TODO: Should probably run in a toplevel-exec. Tasks also need a timeout.
424+
// This could be implemented with R interrupts but would require to
425+
// unsafely jump over the Rust stack, unless we wrapped all R API functions
426+
// to return an Option.
427+
pub fn safely<'env, F, T>(f: F) -> T
428+
where
429+
F: FnOnce() -> T,
430+
F: 'env,
431+
T: 'env,
432+
{
433+
let polled_events = unsafe { R_PolledEvents };
434+
let interrupts_suspended = unsafe { R_interrupts_suspended };
435+
unsafe {
436+
// Disable polled events in this scope.
437+
R_PolledEvents = Some(r_polled_events_disabled);
438+
439+
// Disable interrupts in this scope.
440+
R_interrupts_suspended = 1;
441+
}
442+
443+
// Execute the callback.
444+
let result = f();
445+
446+
// Restore state
447+
// TODO: Needs unwind protection
448+
unsafe {
449+
R_interrupts_suspended = interrupts_suspended;
450+
R_PolledEvents = polled_events;
451+
}
452+
453+
result
454+
}
455+
421456
#[cfg(test)]
422457
mod tests {
423458

crates/harp/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ macro_rules! r_lock {
4242

4343
}
4444

45+
#[macro_export]
46+
macro_rules! r_safely {
47+
($($expr:tt)*) => {{
48+
#[allow(unused_unsafe)]
49+
$crate::exec::safely(|| {
50+
unsafe { $($expr)* } }
51+
)
52+
}}
53+
}
54+
4555
#[macro_export]
4656
macro_rules! with_vector_impl {
4757
($x:expr, $class:ident, $variable:ident, $($code:tt)*) => {{

crates/harp/src/session.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::exec::r_parse;
1515
use crate::object::RObject;
1616
use crate::protect::RProtect;
1717
use crate::r_lang;
18-
use crate::r_lock;
18+
use crate::r_safely;
1919
use crate::r_symbol;
2020
use crate::utils::r_normalize_path;
2121
use crate::utils::r_try_eval_silent;
@@ -32,16 +32,16 @@ static mut OPTIONS_FN: Option<SEXP> = None;
3232
pub fn r_n_frame() -> crate::error::Result<i32> {
3333
SESSION_INIT.call_once(init_interface);
3434

35-
r_lock! {
35+
r_safely! {
3636
let ffi = r_try_eval_silent(NFRAME_CALL.unwrap_unchecked(), R_BaseEnv)?;
3737
let n_frame = IntegerVector::new(ffi)?;
3838
Ok(n_frame.get_unchecked_elt(0))
3939
}
4040
}
4141

4242
pub fn r_sys_frame(n: c_int) -> crate::error::Result<SEXP> {
43-
r_lock! {
44-
let mut protect = RProtect::new();
43+
let mut protect = unsafe { RProtect::new() };
44+
r_safely! {
4545
let n = protect.add(Rf_ScalarInteger(n));
4646
let call = protect.add(r_lang!(r_symbol!("sys.frame"), n));
4747
Ok(r_try_eval_silent(call, R_BaseEnv)?)
@@ -101,7 +101,7 @@ pub fn r_stack_info() -> anyhow::Result<Vec<FrameInfo>> {
101101

102102
// FIXME: It's better not to use `r_try_catch()` here because it adds
103103
// frames to the stack. Should wrap in a top-level-exec instead.
104-
let _ = r_lock!({
104+
let _ = r_safely!({
105105
(|| -> anyhow::Result<()> {
106106
let info = r_try_eval_silent(STACK_INFO_CALL.unwrap(), R_GlobalEnv)?;
107107
Rf_protect(info);

0 commit comments

Comments
 (0)