pen_ffi/future/
call_function.rs1#[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 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 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}