use setback::{protect, recover, AssertUnwindSafe, RecoveryError, RecoveryFailure};
#[test]
fn recovery_into_inner_leaves_the_outer_scope_intact() {
const TID: usize = 41;
let c = 7;
let outer: Result<&str, RecoveryError> = unsafe {
protect(TID, || {
let inner: Result<(), RecoveryError> = protect(TID, || {
let _ = recover(TID, c);
});
assert_eq!(inner, Err(RecoveryError { cause: c }));
"outer ran to completion"
})
};
assert_eq!(outer, Ok("outer ran to completion"));
}
#[test]
fn recovery_peels_the_nest_innermost_first() {
const TID: usize = 52;
let (c_inner, c_middle, c_outer) = (1, 2, 3);
let outer: Result<(), RecoveryError> = unsafe {
protect(TID, || {
let middle: Result<(), RecoveryError> = protect(TID, || {
let inner: Result<(), RecoveryError> = protect(TID, || {
let _ = recover(TID, c_inner);
});
assert_eq!(inner, Err(RecoveryError { cause: c_inner }));
let _ = recover(TID, c_middle);
});
assert_eq!(middle, Err(RecoveryError { cause: c_middle }));
let _ = recover(TID, c_outer);
})
};
assert_eq!(outer, Err(RecoveryError { cause: c_outer }));
}
#[test]
fn find_reports_a_miss_while_nested_marks_are_live() {
const OUTER_TID: usize = 63;
const INNER_TID: usize = 64;
const ABSENT_TID: usize = 6399;
let outer: Result<&str, RecoveryError> = unsafe {
protect(OUTER_TID, || {
let inner: Result<&str, RecoveryError> = protect(INNER_TID, || {
let miss = recover(ABSENT_TID, 1);
assert_eq!(miss, Err(RecoveryFailure));
"inner ran to completion"
});
assert_eq!(inner, Ok("inner ran to completion"));
"outer ran to completion"
})
};
assert_eq!(outer, Ok("outer ran to completion"));
}
#[test]
fn find_skips_another_threads_mark_in_the_global_list() {
use std::sync::mpsc;
use std::thread;
const MAIN_TID: usize = 100;
const FOREIGN_TID: usize = 200;
let (go_tx, go_rx) = mpsc::channel::<()>();
let (ready_tx, ready_rx) = mpsc::channel::<()>();
let (wake_tx, wake_rx) = mpsc::channel::<()>();
let foreign = thread::spawn(move || unsafe {
go_rx.recv().unwrap();
protect(
FOREIGN_TID,
AssertUnwindSafe(move || {
ready_tx.send(()).unwrap(); wake_rx.recv().unwrap(); "foreign ran to completion"
}),
)
});
let c = 42;
let main: Result<(), RecoveryError> = unsafe {
protect(
MAIN_TID,
AssertUnwindSafe(|| {
go_tx.send(()).unwrap(); ready_rx.recv().unwrap(); let _ = recover(MAIN_TID, c);
}),
)
};
assert_eq!(main, Err(RecoveryError { cause: c }));
wake_tx.send(()).unwrap();
assert_eq!(foreign.join().unwrap(), Ok("foreign ran to completion"));
}