1mod abort;
5
6use std::hint::unreachable_unchecked;
7use std::panic;
8use std::sync::Once;
9
10use self::abort::{
11 abort_execution, abort_execution_panic_middleware, extract_return_value,
12 is_abort_execution_exception,
13};
14
15pub type ExecutableState<Ctx, Ret> = fn(&mut Ctx) -> State<Ctx, Ret>;
17
18#[repr(transparent)]
20pub struct State<Ctx, Ret> {
21 inner: for<'ctx> fn(&'ctx mut Ctx) -> Ret,
22}
23
24impl<Ctx, Ret> State<Ctx, Ret> {
25 fn executable(&self) -> ExecutableState<Ctx, Ret> {
26 unsafe { std::mem::transmute(self.inner) }
27 }
28
29 pub fn next(self, context: &mut Ctx) -> Self {
31 self.executable()(context)
32 }
33
34 pub fn bail(return_value: Ret) -> Self
36 where
37 Ret: Send + 'static,
38 {
39 abort_execution(return_value)
40 }
41
42 pub fn from_fn(state: ExecutableState<Ctx, Ret>) -> Self {
44 Self {
45 inner: unsafe {
46 std::mem::transmute::<
47 for<'ctx> fn(&'ctx mut Ctx) -> State<Ctx, Ret>,
48 for<'ctx> fn(&'ctx mut Ctx) -> Ret,
49 >(state)
50 },
51 }
52 }
53}
54
55pub fn run<Ctx, Ret>(context: Ctx, init_state: State<Ctx, Ret>) -> Ret
65where
66 Ret: 'static,
67 Ctx: panic::UnwindSafe,
68{
69 static INSTALL_HANDLER: Once = Once::new();
70
71 INSTALL_HANDLER.call_once(|| {
72 let previous_hook = panic::take_hook();
73 panic::set_hook(abort_execution_panic_middleware::<Ret>(previous_hook));
74 });
75
76 let result = panic::catch_unwind(|| run_inner(context, init_state));
78
79 match result {
81 Err(exception) if !is_abort_execution_exception::<Ret>(&*exception) => {
83 panic::resume_unwind(exception);
84 }
85 Err(mut abort) => {
88 unsafe { extract_return_value(&mut *abort) }
91 }
92 Ok(_) => unsafe { unreachable_unchecked() },
94 }
95}
96
97#[inline(always)]
98fn run_inner<Ctx, Ret>(mut context: Ctx, mut state: State<Ctx, Ret>) -> ! {
99 loop {
100 state = state.next(&mut context);
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn terminate() {
110 assert_eq!(run((), State::from_fn(basic_ret_i32::entry::<1234>)), 1234);
111 assert_eq!(run((), State::from_fn(basic_ret_i32::entry::<42>)), 42);
112 }
113
114 mod basic_ret_i32 {
115 use super::*;
116
117 pub fn entry<const RET: i32>(_: &mut ()) -> State<(), i32> {
118 State::from_fn(middle_state::<RET>)
119 }
120
121 fn middle_state<const RET: i32>(_: &mut ()) -> State<(), i32> {
122 State::from_fn(end_state::<RET>)
123 }
124
125 fn end_state<const RET: i32>(_: &mut ()) -> State<(), i32> {
126 State::bail(RET)
127 }
128 }
129}