Skip to main content

wasmedge_sys/async/
function.rs

1use crate::{
2    ffi, instance::module::InnerInstance, r#async::fiber::AsyncCx, CallingFrame, FuncType,
3    Function, Instance, WasmEdgeResult, WasmValue,
4};
5use std::{future::Future, os::raw::c_void};
6use wasmedge_types::error::CoreError;
7
8use super::module::AsyncInstance;
9
10pub type AsyncFn<'data, 'inst, 'frame, 'fut, Data>
11    // where
12    //     'data: 'fut,
13    //     'inst: 'fut,
14    //     'frame: 'fut,
15    = fn(
16    &'data mut Data,
17    &'inst mut AsyncInstance,
18    &'frame mut CallingFrame,
19    Vec<WasmValue>,
20) -> Box<dyn Future<Output = Result<Vec<WasmValue>, CoreError>> + Send + 'fut>;
21
22unsafe extern "C" fn wrap_async_fn<Data>(
23    key_ptr: *mut c_void,
24    data: *mut std::os::raw::c_void,
25    call_frame_ctx: *const ffi::WasmEdge_CallingFrameContext,
26    params: *const ffi::WasmEdge_Value,
27    param_len: u32,
28    returns: *mut ffi::WasmEdge_Value,
29    return_len: u32,
30) -> ffi::WasmEdge_Result {
31    let mut frame = CallingFrame::create(call_frame_ctx);
32    // let executor_ctx = ffi::WasmEdge_CallingFrameGetExecutor(call_frame_ctx);
33    let inst_ctx = ffi::WasmEdge_CallingFrameGetModuleInstance(call_frame_ctx);
34    let mut inst = std::mem::ManuallyDrop::new(AsyncInstance(Instance {
35        inner: InnerInstance(inst_ctx as _),
36    }));
37    let data = &mut *(data as *mut Data);
38
39    // arguments
40    let input = if params.is_null() || param_len == 0 {
41        vec![]
42    } else {
43        let raw_input = unsafe { std::slice::from_raw_parts(params, param_len as usize) };
44        raw_input.iter().map(|r| (*r).into()).collect::<Vec<_>>()
45    };
46
47    // returns
48    let return_len = return_len as usize;
49    let mut empty_return = [];
50    let raw_returns = if returns.is_null() || return_len == 0 {
51        &mut empty_return
52    } else {
53        unsafe { std::slice::from_raw_parts_mut(returns, return_len) }
54    };
55
56    // get and call host function
57    let real_fn: AsyncFn<'_, '_, '_, '_, Data> = std::mem::transmute(key_ptr);
58
59    let async_cx = AsyncCx::new();
60    let mut future = std::pin::Pin::from(real_fn(data, &mut inst, &mut frame, input));
61    // call host function
62    let result = match unsafe { async_cx.block_on(future.as_mut()) } {
63        Ok(Ok(ret)) => Ok(ret),
64        Ok(Err(err)) => Err(err),
65        Err(_err) => Err(CoreError::Common(
66            wasmedge_types::error::CoreCommonError::Interrupted,
67        )),
68    };
69
70    // parse result
71    match result {
72        Ok(returns) => {
73            assert!(returns.len() == return_len, "[wasmedge-sys] check the number of returns of async host function. Expected: {}, actual: {}", return_len, returns.len());
74            for (idx, wasm_value) in returns.into_iter().enumerate() {
75                raw_returns[idx] = wasm_value.as_raw();
76            }
77            ffi::WasmEdge_Result { Code: 0 }
78        }
79        Err(err) => err.into(),
80    }
81}
82
83pub struct AsyncFunction(pub(crate) Function);
84
85impl AsyncFunction {
86    /// Creates an async [host function](crate::Function) with the given function type.
87    ///
88    /// # Arguments
89    ///
90    /// * `ty` - The types of the arguments and returns of the target function.
91    ///
92    /// * `real_fn` - The pointer to the target function.
93    ///
94    /// * `data` - The host context data used in this function.
95    ///
96    /// * `cost` - The function cost in the [Statistics](crate::Statistics). Pass 0 if the calculation is not needed.
97    ///
98    /// # Error
99    ///
100    /// * If fail to create a [Function], then [WasmEdgeError::Func(FuncError::Create)](wasmedge_types::error::FuncError) is returned.
101    ///
102    pub fn create_async_func<'data, 'inst, 'frame, 'fut, T: Send>(
103        ty: &FuncType,
104        real_fn: AsyncFn<'data, 'inst, 'frame, 'fut, T>,
105        data: *mut T,
106        cost: u64,
107    ) -> WasmEdgeResult<Self>
108    where
109        'data: 'fut,
110        'inst: 'fut,
111        'frame: 'fut,
112    {
113        let f = unsafe {
114            Function::create_with_custom_wrapper(
115                ty,
116                wrap_async_fn::<T>,
117                real_fn as _,
118                data as _,
119                cost,
120            )
121        }?;
122        Ok(Self(f))
123    }
124}
125
126impl AsRef<Function> for AsyncFunction {
127    fn as_ref(&self) -> &Function {
128        &self.0
129    }
130}
131
132impl AsMut<Function> for AsyncFunction {
133    fn as_mut(&mut self) -> &mut Function {
134        &mut self.0
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::{
142        instance::function::AsFunc,
143        r#async::{fiber::AsyncState, module::AsyncImportObject},
144        types::WasmValue,
145        AsInstance, Executor,
146    };
147
148    use wasmedge_types::{error::CoreExecutionError, FuncType, ValType};
149
150    #[tokio::test]
151    async fn test_func_basic() {
152        #[derive(Debug)]
153        struct Data<T, S> {
154            _x: i32,
155            _y: String,
156            _v: Vec<T>,
157            _s: Vec<S>,
158        }
159
160        let data: Data<i32, &str> = Data {
161            _x: 12,
162            _y: "hello".to_string(),
163            _v: vec![1, 2, 3],
164            _s: vec!["macos", "linux", "windows"],
165        };
166
167        fn real_add<T: core::fmt::Debug>(
168            _host_data: &mut Data<i32, &str>,
169            _inst: &mut AsyncInstance,
170            _frame: &mut CallingFrame,
171            input: Vec<WasmValue>,
172        ) -> Box<dyn Future<Output = Result<Vec<WasmValue>, CoreError>> + Send> {
173            Box::new(async move {
174                println!("Rust: Entering Rust function real_add");
175
176                if input.len() != 2 {
177                    return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
178                }
179
180                let a = if input[0].ty() == ValType::I32 {
181                    input[0].to_i32()
182                } else {
183                    return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
184                };
185
186                let b = if input[1].ty() == ValType::I32 {
187                    input[1].to_i32()
188                } else {
189                    return Err(CoreError::Execution(CoreExecutionError::FuncSigMismatch));
190                };
191
192                tokio::time::sleep(std::time::Duration::from_millis(300)).await;
193
194                let c = a + b;
195                println!("Rust: calcuating in real_add c: {c:?}");
196
197                println!("Rust: Leaving Rust function real_add");
198                Ok(vec![WasmValue::from_i32(c)])
199            })
200        }
201
202        let mut import_module = AsyncImportObject::create("test_module", Box::new(data)).unwrap();
203
204        // create a FuncType
205        let func_ty = FuncType::new(vec![ValType::I32; 2], vec![ValType::I32]);
206        // create a host function
207        let result = AsyncFunction::create_async_func(
208            &func_ty,
209            real_add::<Data<i32, &str>>,
210            import_module.get_host_data_mut(),
211            0,
212        );
213        assert!(result.is_ok());
214        let host_func = result.unwrap();
215
216        // get func type
217        let result = host_func.ty();
218        assert!(result.is_some());
219        let ty = result.unwrap();
220
221        // check parameters
222        assert_eq!(ty.args_len(), 2);
223        assert_eq!(ty.args(), &[ValType::I32; 2]);
224
225        // check returns
226        assert_eq!(ty.returns_len(), 1);
227        assert_eq!(ty.returns(), &[ValType::I32]);
228
229        import_module.add_async_func("add", host_func);
230
231        // run this function
232        let result = Executor::create(None, None);
233        assert!(result.is_ok());
234        let mut executor = result.unwrap();
235
236        let mut add_func = import_module.get_func_mut("add").unwrap();
237
238        let async_state = AsyncState::new();
239
240        let result = executor
241            .call_func_async(
242                &async_state,
243                &mut add_func,
244                vec![WasmValue::from_i32(1), WasmValue::from_i32(2)],
245            )
246            .await;
247
248        assert!(result.is_ok());
249        let returns = result.unwrap();
250        assert_eq!(returns[0].to_i32(), 3);
251    }
252}