Skip to main content

wasmedge_sys/
executor.rs

1//! Defines WasmEdge Executor.
2
3use super::ffi;
4#[cfg(all(feature = "async", target_os = "linux"))]
5use crate::r#async::fiber::{AsyncState, FiberFuture};
6
7#[cfg(all(feature = "async", target_os = "linux", not(target_env = "musl")))]
8use crate::r#async::fiber::TimeoutFiberFuture;
9
10use crate::{
11    instance::{function::AsFunc, module::InnerInstance},
12    store::Store,
13    types::WasmEdgeString,
14    utils::check,
15    AsInstance, Config, Function, Instance, Module, Statistics, WasmEdgeResult, WasmValue,
16};
17#[cfg(all(target_os = "linux", not(target_env = "musl")))]
18use std::os::raw::c_void;
19use wasmedge_types::error::WasmEdgeError;
20
21#[cfg(all(target_os = "linux", not(target_env = "musl")))]
22pub(crate) struct JmpState {
23    pub(crate) sigjmp_buf: *mut setjmp::sigjmp_buf,
24}
25
26#[cfg(all(target_os = "linux", not(target_env = "musl")))]
27scoped_tls::scoped_thread_local!(pub(crate) static JMP_BUF: JmpState);
28
29#[cfg(all(target_os = "linux", not(target_env = "musl")))]
30unsafe extern "C" fn sync_timeout(sig: i32, info: *mut libc::siginfo_t) {
31    if let Some(info) = info.as_mut() {
32        let si_value = info.si_value();
33        let value: *mut libc::pthread_t = si_value.sival_ptr.cast();
34        let dist_pthread = *value;
35        let self_pthread = libc::pthread_self();
36        if self_pthread == dist_pthread {
37            if JMP_BUF.is_set() {
38                let env = JMP_BUF.with(|s| s.sigjmp_buf);
39                setjmp::siglongjmp(env, 1);
40            }
41        } else {
42            libc::pthread_sigqueue(dist_pthread, sig, si_value);
43        }
44    }
45}
46#[cfg(all(target_os = "linux", not(target_env = "musl")))]
47unsafe extern "C" fn pre_host_func(_: *mut c_void) {
48    use libc::SIG_BLOCK;
49
50    let mut set = std::mem::zeroed();
51    libc::sigemptyset(&mut set);
52    libc::sigaddset(&mut set, timeout_signo());
53    libc::pthread_sigmask(SIG_BLOCK, &set, std::ptr::null_mut());
54}
55#[cfg(all(target_os = "linux", not(target_env = "musl")))]
56unsafe extern "C" fn post_host_func(_: *mut c_void) {
57    use libc::SIG_UNBLOCK;
58
59    let mut set = std::mem::zeroed();
60    libc::sigemptyset(&mut set);
61    libc::sigaddset(&mut set, timeout_signo());
62    libc::pthread_sigmask(SIG_UNBLOCK, &set, std::ptr::null_mut());
63}
64
65#[inline]
66#[cfg(all(target_os = "linux", not(target_env = "musl")))]
67pub(crate) fn timeout_signo() -> i32 {
68    option_env!("SIG_OFFSET")
69        .and_then(|s| s.parse().ok())
70        .unwrap_or(0)
71        + libc::SIGRTMIN()
72}
73
74#[cfg(all(target_os = "linux", not(target_env = "musl")))]
75static INIT_SIGNAL_LISTEN: std::sync::Once = std::sync::Once::new();
76
77#[inline(always)]
78#[cfg(all(target_os = "linux", not(target_env = "musl")))]
79pub(crate) unsafe fn init_signal_listen() {
80    INIT_SIGNAL_LISTEN.call_once(|| {
81        let mut new_act: libc::sigaction = std::mem::zeroed();
82        new_act.sa_sigaction = sync_timeout as *const () as usize;
83        new_act.sa_flags = libc::SA_RESTART | libc::SA_SIGINFO;
84        libc::sigaction(timeout_signo(), &new_act, std::ptr::null_mut());
85    });
86}
87
88/// Defines an execution environment for both pure WASM and compiled WASM.
89#[derive(Debug)]
90pub struct Executor {
91    pub(crate) inner: InnerExecutor,
92}
93
94impl Drop for Executor {
95    fn drop(&mut self) {
96        unsafe { ffi::WasmEdge_ExecutorDelete(self.inner.0) }
97    }
98}
99
100impl Executor {
101    /// Creates a new [executor](crate::Executor) to be associated with the given [config](crate::Config) and [statistics](crate::Statistics).
102    ///
103    /// # Arguments
104    ///
105    /// * `config` - The configuration of the new [executor](crate::Executor).
106    ///
107    /// * `stat` - The [statistics](crate::Statistics) needed by the new [executor](crate::Executor).
108    ///
109    /// # Error
110    ///
111    /// If fail to create a [executor](crate::Executor), then an error is returned.
112    pub fn create(config: Option<&Config>, stat: Option<Statistics>) -> WasmEdgeResult<Self> {
113        let conf_ctx = config
114            .map(|cfg| cfg.inner.0)
115            .unwrap_or(std::ptr::null_mut());
116        let stat_ctx = stat
117            .map(|stat| stat.inner.0)
118            .unwrap_or(std::ptr::null_mut());
119
120        let ctx = unsafe { ffi::WasmEdge_ExecutorCreate(conf_ctx, stat_ctx) };
121
122        if ctx.is_null() {
123            Err(Box::new(WasmEdgeError::ExecutorCreate))
124        } else {
125            #[cfg(all(target_os = "linux", not(target_env = "musl")))]
126            unsafe {
127                ffi::WasmEdge_ExecutorExperimentalRegisterPreHostFunction(
128                    ctx,
129                    std::ptr::null_mut(),
130                    Some(pre_host_func),
131                );
132                ffi::WasmEdge_ExecutorExperimentalRegisterPostHostFunction(
133                    ctx,
134                    std::ptr::null_mut(),
135                    Some(post_host_func),
136                );
137            }
138
139            Ok(Executor {
140                inner: InnerExecutor(ctx),
141            })
142        }
143    }
144}
145
146impl Executor {
147    /// Runs a host function instance and returns the results.
148    ///
149    /// # Arguments
150    ///
151    /// * `func` - The function instance to run.
152    ///
153    /// * `params` - The arguments to pass to the function.
154    ///
155    /// # Errors
156    ///
157    /// If fail to run the host function, then an error is returned.
158    pub fn call_func(
159        &mut self,
160        func: &mut Function,
161        params: impl IntoIterator<Item = WasmValue>,
162    ) -> WasmEdgeResult<Vec<WasmValue>> {
163        let raw_params = params.into_iter().map(|x| x.as_raw()).collect::<Vec<_>>();
164
165        // get the length of the function's returns
166        let func_ty = func
167            .ty()
168            .ok_or(WasmEdgeError::Func(wasmedge_types::error::FuncError::Type))?;
169        let returns_len = func_ty.returns_len();
170        let mut returns = Vec::with_capacity(returns_len);
171
172        unsafe {
173            check(ffi::WasmEdge_ExecutorInvoke(
174                self.inner.0,
175                func.get_func_raw(),
176                raw_params.as_ptr(),
177                raw_params.len() as u32,
178                returns.as_mut_ptr(),
179                returns_len as u32,
180            ))?;
181
182            returns.set_len(returns_len);
183        }
184
185        Ok(returns.into_iter().map(Into::into).collect::<Vec<_>>())
186    }
187
188    /// Run a host function instance and return the results or timeout.
189    ///
190    /// # Arguments
191    ///
192    /// * `func` - The function instance to run.
193    ///
194    /// * `params` - The arguments to pass to the function.
195    ///
196    /// * `timeout` - The maximum execution time of the function to be run.
197    ///
198    /// # Errors
199    ///
200    /// If fail to run the host function, then an error is returned.
201    #[cfg(all(target_os = "linux", not(target_env = "musl")))]
202    #[cfg_attr(docsrs, doc(cfg(all(target_os = "linux", not(target_env = "musl")))))]
203    pub fn call_func_with_timeout(
204        &self,
205        func: &mut Function,
206        params: impl IntoIterator<Item = WasmValue>,
207        timeout: std::time::Duration,
208    ) -> WasmEdgeResult<Vec<WasmValue>> {
209        use wasmedge_types::error;
210
211        let raw_params = params.into_iter().map(|x| x.as_raw()).collect::<Vec<_>>();
212        // get the length of the function's returns
213        let func_ty = func
214            .ty()
215            .ok_or(WasmEdgeError::Func(wasmedge_types::error::FuncError::Type))?;
216        let returns_len = func_ty.returns_len();
217        let mut returns = Vec::with_capacity(returns_len);
218
219        unsafe {
220            init_signal_listen();
221            let mut self_thread = libc::pthread_self();
222            let mut sigjmp_buf: setjmp::sigjmp_buf = std::mem::zeroed();
223            let env = &mut sigjmp_buf as *mut _;
224
225            let mut timerid: libc::timer_t = std::mem::zeroed();
226            let mut sev: libc::sigevent = std::mem::zeroed();
227            sev.sigev_notify = libc::SIGEV_SIGNAL;
228            sev.sigev_signo = timeout_signo();
229            sev.sigev_value.sival_ptr = &mut self_thread as *mut _ as *mut libc::c_void;
230
231            if libc::timer_create(libc::CLOCK_REALTIME, &mut sev, &mut timerid) < 0 {
232                return Err(Box::new(error::WasmEdgeError::Operation(
233                    "timer_create error".into(),
234                )));
235            }
236            let mut value: libc::itimerspec = std::mem::zeroed();
237            value.it_value.tv_sec = timeout.as_secs() as _;
238            value.it_value.tv_nsec = timeout.subsec_nanos() as _;
239            if libc::timer_settime(timerid, 0, &value, std::ptr::null_mut()) < 0 {
240                libc::timer_delete(timerid);
241                return Err(Box::new(error::WasmEdgeError::Operation(
242                    "timer_settime error".into(),
243                )));
244            }
245            let jmp_state = JmpState { sigjmp_buf: env };
246
247            JMP_BUF.set(&jmp_state, || {
248                if setjmp::sigsetjmp(env, 1) == 0 {
249                    let r = check(ffi::WasmEdge_ExecutorInvoke(
250                        self.inner.0,
251                        func.get_func_raw(),
252                        raw_params.as_ptr(),
253                        raw_params.len() as u32,
254                        returns.as_mut_ptr(),
255                        returns_len as u32,
256                    ));
257                    libc::timer_delete(timerid);
258                    r
259                } else {
260                    libc::timer_delete(timerid);
261                    Err(Box::new(error::WasmEdgeError::ExecuteTimeout))
262                }
263            })?;
264
265            returns.set_len(returns_len);
266            Ok(returns.into_iter().map(Into::into).collect::<Vec<_>>())
267        }
268    }
269
270    /// Asynchronously runs a host function instance and returns the results.
271    ///
272    /// # Arguments
273    ///
274    /// * `async_state` - Used to store asynchronous state at run time.
275    ///
276    /// * `func` - The function instance to run.
277    ///
278    /// * `params` - The arguments to pass to the function.
279    ///
280    /// # Errors
281    ///
282    /// If fail to run the host function, then an error is returned.
283    #[cfg(all(feature = "async", target_os = "linux"))]
284    #[cfg_attr(docsrs, doc(cfg(all(feature = "async", target_os = "linux"))))]
285    pub async fn call_func_async(
286        &mut self,
287        async_state: &AsyncState,
288        func: &mut Function,
289        params: impl IntoIterator<Item = WasmValue> + Send,
290    ) -> WasmEdgeResult<Vec<WasmValue>> {
291        FiberFuture::on_fiber(async_state, || self.call_func(func, params))
292            .await
293            .unwrap()
294    }
295
296    /// Asynchronously runs a host function instance with a timeout setting
297    ///
298    /// # Arguments
299    ///
300    /// * `async_state` - Used to store asynchronous state at run time.
301    ///
302    /// * `func` - The function instance to run.
303    ///
304    /// * `params` - The arguments to pass to the function.
305    ///
306    /// * `timeout` - The maximum execution time of the function to be run.
307    ///
308    /// # Errors
309    ///
310    /// If fail to run the host function, then an error is returned.
311    #[cfg(all(feature = "async", target_os = "linux", not(target_env = "musl")))]
312    #[cfg_attr(
313        docsrs,
314        doc(cfg(all(feature = "async", target_os = "linux", not(target_env = "musl"))))
315    )]
316    pub async fn call_func_async_with_timeout(
317        &mut self,
318        async_state: &AsyncState,
319        func: &mut Function,
320        params: impl IntoIterator<Item = WasmValue> + Send,
321        timeout: std::time::Duration,
322    ) -> WasmEdgeResult<Vec<WasmValue>> {
323        use wasmedge_types::error;
324        let ldd = std::time::SystemTime::now() + timeout;
325        TimeoutFiberFuture::on_fiber(async_state, || self.call_func(func, params), ldd)
326            .await
327            .map_err(|_| Box::new(error::WasmEdgeError::ExecuteTimeout))?
328    }
329
330    /// Runs a host function reference instance and returns the results.
331    ///
332    /// # Arguments
333    ///
334    /// * `func_ref` - The function reference instance to run.
335    ///
336    /// * `params` - The arguments to pass to the function.
337    ///
338    /// # Errors
339    ///
340    /// If fail to run the host function reference instance, then an error is returned.
341    pub fn call_func_ref<FuncRef: AsFunc>(
342        &mut self,
343        func_ref: &mut FuncRef,
344        params: impl IntoIterator<Item = WasmValue>,
345    ) -> WasmEdgeResult<Vec<WasmValue>> {
346        let raw_params = params.into_iter().map(|x| x.as_raw()).collect::<Vec<_>>();
347
348        // get the length of the function's returns
349        let func_ty = func_ref.ty().unwrap();
350        let returns_len = func_ty.returns_len();
351        let mut returns = Vec::with_capacity(returns_len);
352
353        unsafe {
354            check(ffi::WasmEdge_ExecutorInvoke(
355                self.inner.0,
356                func_ref.get_func_raw(),
357                raw_params.as_ptr(),
358                raw_params.len() as u32,
359                returns.as_mut_ptr(),
360                returns_len as u32,
361            ))?;
362            returns.set_len(returns_len);
363        }
364
365        Ok(returns.into_iter().map(Into::into).collect::<Vec<_>>())
366    }
367
368    /// Asynchronously runs a host function reference instance and returns the results.
369    ///
370    /// # Arguments
371    ///
372    /// * `async_state` - Used to store asynchronous state at run time.
373    ///
374    /// * `func_ref` - The function reference instance to run.
375    ///
376    /// * `params` - The arguments to pass to the function.
377    ///
378    /// # Errors
379    ///
380    /// If fail to run the host function reference instance, then an error is returned.
381    #[cfg(all(feature = "async", target_os = "linux"))]
382    #[cfg_attr(docsrs, doc(cfg(all(feature = "async", target_os = "linux"))))]
383    pub async fn call_func_ref_async<FuncRef: AsFunc + Send>(
384        &mut self,
385        async_state: &AsyncState,
386        func_ref: &mut FuncRef,
387        params: impl IntoIterator<Item = WasmValue> + Send,
388    ) -> WasmEdgeResult<Vec<WasmValue>> {
389        FiberFuture::on_fiber(async_state, || self.call_func_ref(func_ref, params))
390            .await
391            .unwrap()
392    }
393}
394
395impl Executor {
396    /// Registers and instantiates a [import module](crate::ImportModule) into a [store](crate::Store).
397    ///
398    /// # Arguments
399    ///
400    /// * `store` - The target [store](crate::Store), into which the given [import module](crate::ImportModule) is registered.
401    ///
402    /// * `import` - The WasmEdge [import module](crate::ImportModule) to be registered.
403    ///
404    /// # Error
405    ///
406    /// If fail to register the given [import module](crate::ImportModule), then an error is returned.
407    pub fn register_import_module<T: AsInstance + ?Sized>(
408        &mut self,
409        store: &mut Store,
410        import: &T,
411    ) -> WasmEdgeResult<()> {
412        unsafe {
413            check(ffi::WasmEdge_ExecutorRegisterImport(
414                self.inner.0,
415                store.inner.0,
416                import.as_ptr(),
417            ))?;
418        }
419
420        Ok(())
421    }
422
423    /// Registers and instantiates a WasmEdge [module](crate::Module) into a store.
424    ///
425    /// Instantiates the given WasmEdge [module](crate::Module), including the [functions](crate::Function), [memories](crate::Memory), [tables](crate::Table), and [globals](crate::Global) it hosts; and then, registers the module [instance](crate::Instance) into the [store](crate::Store) with the given name.
426    ///
427    /// # Arguments
428    ///
429    /// * `store` - The target [store](crate::Store), into which the given [module](crate::Module) is registered.
430    ///
431    /// * `module` - A validated [module](crate::Module) to be registered.
432    ///
433    /// * `name` - The exported name of the registered [module](crate::Module).
434    ///
435    /// # Error
436    ///
437    /// If fail to register the given [module](crate::Module), then an error is returned.
438    pub fn register_named_module(
439        &mut self,
440        store: &mut Store,
441        module: &Module,
442        name: impl AsRef<str>,
443    ) -> WasmEdgeResult<Instance> {
444        let mut instance_ctx = std::ptr::null_mut();
445        let mod_name: WasmEdgeString = name.as_ref().into();
446        unsafe {
447            check(ffi::WasmEdge_ExecutorRegister(
448                self.inner.0,
449                &mut instance_ctx,
450                store.inner.0,
451                module.inner.0 as *const _,
452                mod_name.as_raw(),
453            ))?;
454
455            let inst = Instance {
456                inner: InnerInstance(instance_ctx),
457            };
458
459            Ok(inst)
460        }
461    }
462
463    /// Registers and instantiates a WasmEdge [module](crate::Module) into a [store](crate::Store) as an anonymous module.
464    ///
465    /// Notice that when a new module is instantiated into the [store](crate::Store), the old instantiated module is removed; in addition, ensure that the [imports](crate::ImportModule) the module depends on are already registered into the [store](crate::Store).
466    ///
467    ///
468    /// # Arguments
469    ///
470    /// * `store` - The [store](crate::Store), in which the [module](crate::Module) to be instantiated is stored.
471    ///
472    /// * `ast_mod` - The target [module](crate::Module) to be instantiated.
473    ///
474    /// # Error
475    ///
476    /// If fail to instantiate the given [module](crate::Module), then an error is returned.
477    pub fn register_active_module(
478        &mut self,
479        store: &mut Store,
480        module: &Module,
481    ) -> WasmEdgeResult<Instance> {
482        let mut instance_ctx = std::ptr::null_mut();
483        unsafe {
484            check(ffi::WasmEdge_ExecutorInstantiate(
485                self.inner.0,
486                &mut instance_ctx,
487                store.inner.0,
488                module.inner.0 as *const _,
489            ))?;
490
491            let inst = Instance {
492                inner: InnerInstance(instance_ctx),
493            };
494
495            Ok(inst)
496        }
497    }
498}
499
500#[derive(Debug, Clone)]
501pub(crate) struct InnerExecutor(pub(crate) *mut ffi::WasmEdge_ExecutorContext);
502unsafe impl Send for InnerExecutor {}
503unsafe impl Sync for InnerExecutor {}