pen_ffi/future/
call_function.rs

1#[macro_export]
2macro_rules! call_function {
3    (fn($($argument_type:ty),* $(,)?) -> $result_type:ty, $function:expr) => {
4        call_function!(fn($($argument_type),*) -> $result_type, $function,)
5    };
6    (fn($($argument_type:ty),* $(,)?) -> $result_type:ty, $function:expr, $($argument:expr),* $(,)?) => {
7        async {
8            use core::{future::poll_fn, task::Poll};
9            use $crate::{cps, future::__private::INITIAL_STACK_CAPACITY};
10
11            type AsyncStack = cps::AsyncStack<$result_type>;
12
13            type Trampoline = cps::Trampoline<$result_type, $result_type>;
14
15            extern "C" fn resolve(stack: &mut AsyncStack, value: $result_type) {
16                stack.resolve(value);
17            }
18
19            // Move arguments into an initializer function.
20            let mut initialize = Some(|stack: &mut AsyncStack| {
21                let function = $function;
22
23                unsafe { function(stack, resolve, $($argument),*) };
24            });
25
26            let mut trampoline: Option<Trampoline> = None;
27            let mut stack = AsyncStack::new(INITIAL_STACK_CAPACITY);
28
29            poll_fn(move |context| {
30                if let Some(initialize) = initialize.take() {
31                    stack.run_with_context(context, initialize);
32                } else if let Some((step, continue_)) = trampoline.take() {
33                    stack.run_with_context(context, |stack| step(stack, continue_));
34                } else {
35                    unreachable!("suspension must return trampoline functions")
36                }
37
38                if let Some(value) = stack.resolved_value() {
39                    value.into()
40                } else {
41                    trampoline = Some(stack.resume().unwrap());
42                    Poll::Pending
43                }
44            })
45            .await
46        }
47    };
48}
49
50#[cfg(test)]
51mod tests {
52    use crate::{
53        cps::{AsyncStack, ContinuationFunction},
54        ByteString, Number,
55    };
56    use core::future::ready;
57
58    unsafe extern "C" fn get_number(
59        stack: &mut AsyncStack<Number>,
60        continue_: ContinuationFunction<Number, Number>,
61    ) {
62        continue_(stack, 42.0.into())
63    }
64
65    #[tokio::test]
66    async fn call_with_no_argument() {
67        assert_eq!(
68            call_function!(fn() -> Number, get_number,).await,
69            42.0.into()
70        );
71    }
72
73    unsafe extern "C" fn pass_through_number(
74        stack: &mut AsyncStack<Number>,
75        continue_: ContinuationFunction<Number, Number>,
76        x: Number,
77    ) {
78        continue_(stack, x)
79    }
80
81    #[tokio::test]
82    async fn call_one_argument_closure() {
83        let value = 42.0;
84
85        assert_eq!(
86            call_function!(fn(Number) -> Number, pass_through_number, value.into()).await,
87            value.into()
88        );
89    }
90
91    unsafe extern "C" fn add_numbers(
92        stack: &mut AsyncStack<Number>,
93        continue_: ContinuationFunction<Number, Number>,
94        x: Number,
95        y: Number,
96    ) {
97        continue_(stack, (f64::from(x) + f64::from(y)).into())
98    }
99
100    #[tokio::test]
101    async fn call_two_argument_closure() {
102        assert_eq!(
103            call_function!(
104                fn(Number, Number) -> Number,
105                add_numbers,
106                40.0.into(),
107                2.0.into(),
108            )
109            .await,
110            42.0.into()
111        );
112    }
113
114    unsafe extern "C" fn get_number_with_suspension(
115        stack: &mut AsyncStack<Number>,
116        continue_: ContinuationFunction<Number, Number>,
117    ) {
118        fn step(stack: &mut AsyncStack<Number>, continue_: ContinuationFunction<Number, Number>) {
119            continue_(stack, 42.0.into())
120        }
121
122        stack.suspend(step, continue_, ready(())).unwrap();
123
124        // Wake immediately as we are waiting for nothing!
125        stack.context().unwrap().waker().wake_by_ref();
126    }
127
128    #[tokio::test]
129    async fn call_closure_with_suspension() {
130        assert_eq!(
131            call_function!(fn() -> Number, get_number_with_suspension,).await,
132            42.0.into()
133        );
134    }
135
136    unsafe extern "C" fn closure_entry_function_with_string(
137        stack: &mut AsyncStack<ByteString>,
138        continue_: ContinuationFunction<ByteString, ByteString>,
139        x: ByteString,
140    ) {
141        continue_(stack, x)
142    }
143
144    #[tokio::test]
145    async fn move_argument() {
146        let value = "foo";
147
148        assert_eq!(
149            call_function!(
150                fn(ByteString) -> ByteString,
151                closure_entry_function_with_string,
152                value.into(),
153            )
154            .await,
155            value.into()
156        );
157    }
158
159    #[tokio::test]
160    async fn move_argument_in_closure() {
161        let value = ByteString::from("foo");
162
163        assert_eq!(
164            call_function!(
165                fn(ByteString) -> ByteString,
166                closure_entry_function_with_string,
167                value.clone(),
168            )
169            .await,
170            value
171        );
172    }
173}