wasmtime_c_api/
async.rs

1use std::ffi::c_void;
2use std::future::Future;
3use std::mem::{self, MaybeUninit};
4use std::num::NonZeroU64;
5use std::ops::Range;
6use std::pin::Pin;
7use std::sync::Arc;
8use std::task::{Context, Poll, Waker};
9use std::{ptr, str};
10use wasmtime::{
11    AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val,
12};
13
14use crate::{
15    WASMTIME_I32, WasmtimeCaller, WasmtimeStoreContextMut, bad_utf8, handle_result, to_str,
16    translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, wasmtime_caller_t,
17    wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t, wasmtime_module_t,
18    wasmtime_val_t, wasmtime_val_union,
19};
20
21#[unsafe(no_mangle)]
22pub extern "C" fn wasmtime_config_async_support_set(c: &mut wasm_config_t, enable: bool) {
23    c.config.async_support(enable);
24}
25
26#[unsafe(no_mangle)]
27pub extern "C" fn wasmtime_config_async_stack_size_set(c: &mut wasm_config_t, size: usize) {
28    c.config.async_stack_size(size);
29}
30
31#[unsafe(no_mangle)]
32pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update(
33    mut store: WasmtimeStoreContextMut<'_>,
34    delta: u64,
35) {
36    store.epoch_deadline_async_yield_and_update(delta);
37}
38
39#[unsafe(no_mangle)]
40pub extern "C" fn wasmtime_context_fuel_async_yield_interval(
41    mut store: WasmtimeStoreContextMut<'_>,
42    interval: Option<NonZeroU64>,
43) -> Option<Box<wasmtime_error_t>> {
44    handle_result(
45        store.fuel_async_yield_interval(interval.map(|n| n.get())),
46        |()| {},
47    )
48}
49
50pub type wasmtime_func_async_callback_t = extern "C" fn(
51    *mut c_void,
52    *mut wasmtime_caller_t,
53    *const wasmtime_val_t,
54    usize,
55    *mut wasmtime_val_t,
56    usize,
57    &mut Option<Box<wasm_trap_t>>,
58    &mut wasmtime_async_continuation_t,
59);
60
61#[repr(C)]
62pub struct wasmtime_async_continuation_t {
63    pub callback: wasmtime_func_async_continuation_callback_t,
64    pub env: *mut c_void,
65    pub finalizer: Option<extern "C" fn(*mut c_void)>,
66}
67
68unsafe impl Send for wasmtime_async_continuation_t {}
69unsafe impl Sync for wasmtime_async_continuation_t {}
70impl Drop for wasmtime_async_continuation_t {
71    fn drop(&mut self) {
72        if let Some(f) = self.finalizer {
73            f(self.env);
74        }
75    }
76}
77impl Future for wasmtime_async_continuation_t {
78    type Output = ();
79    fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
80        let this = self.get_mut();
81        let cb = this.callback;
82        if cb(this.env) {
83            Poll::Ready(())
84        } else {
85            Poll::Pending
86        }
87    }
88}
89
90/// Internal structure to add Send/Sync to a c_void member.
91///
92/// This is useful in closures that need to capture some C data.
93#[derive(Debug)]
94struct CallbackDataPtr {
95    pub ptr: *mut std::ffi::c_void,
96}
97
98unsafe impl Send for CallbackDataPtr {}
99unsafe impl Sync for CallbackDataPtr {}
100
101pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;
102
103async fn invoke_c_async_callback<'a>(
104    cb: wasmtime_func_async_callback_t,
105    data: CallbackDataPtr,
106    mut caller: WasmtimeCaller<'a>,
107    params: &'a [Val],
108    results: &'a mut [Val],
109) -> Result<()> {
110    // Convert `params/results` to `wasmtime_val_t`. Use the previous
111    // storage in `hostcall_val_storage` to help avoid allocations all the
112    // time.
113    let mut hostcall_val_storage = mem::take(&mut caller.data_mut().hostcall_val_storage);
114    debug_assert!(hostcall_val_storage.is_empty());
115    hostcall_val_storage.reserve(params.len() + results.len());
116    hostcall_val_storage.extend(
117        params
118            .iter()
119            .cloned()
120            .map(|p| wasmtime_val_t::from_val_unscoped(&mut caller, p)),
121    );
122    hostcall_val_storage.extend((0..results.len()).map(|_| wasmtime_val_t {
123        kind: WASMTIME_I32,
124        of: wasmtime_val_union { i32: 0 },
125    }));
126    let (params, out_results) = hostcall_val_storage.split_at_mut(params.len());
127
128    // Invoke the C function pointer.
129    // The result will be a continuation which we will wrap in a Future.
130    let mut caller = wasmtime_caller_t { caller };
131    let mut trap = None;
132    extern "C" fn panic_callback(_: *mut c_void) -> bool {
133        panic!("callback must be set")
134    }
135    let mut continuation = wasmtime_async_continuation_t {
136        callback: panic_callback,
137        env: ptr::null_mut(),
138        finalizer: None,
139    };
140    cb(
141        data.ptr,
142        &mut caller,
143        params.as_ptr(),
144        params.len(),
145        out_results.as_mut_ptr(),
146        out_results.len(),
147        &mut trap,
148        &mut continuation,
149    );
150    continuation.await;
151
152    if let Some(trap) = trap {
153        return Err(trap.error);
154    }
155
156    // Translate the `wasmtime_val_t` results into the `results` space
157    for (i, result) in out_results.iter().enumerate() {
158        unsafe {
159            results[i] = result.to_val_unscoped(&mut caller.caller);
160        }
161    }
162    // Move our `vals` storage back into the store now that we no longer
163    // need it. This'll get picked up by the next hostcall and reuse our
164    // same storage.
165    hostcall_val_storage.truncate(0);
166    caller.caller.data_mut().hostcall_val_storage = hostcall_val_storage;
167    Ok(())
168}
169
170unsafe fn c_async_callback_to_rust_fn(
171    callback: wasmtime_func_async_callback_t,
172    data: *mut c_void,
173    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
174) -> impl for<'a> Fn(
175    WasmtimeCaller<'a>,
176    &'a [Val],
177    &'a mut [Val],
178) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
179+ Send
180+ Sync
181+ 'static {
182    let foreign = crate::ForeignData { data, finalizer };
183    move |caller, params, results| {
184        let _ = &foreign; // move entire foreign into this closure
185        let data = CallbackDataPtr { ptr: foreign.data };
186        Box::new(invoke_c_async_callback(
187            callback, data, caller, params, results,
188        ))
189    }
190}
191
192#[repr(transparent)]
193pub struct wasmtime_call_future_t<'a> {
194    underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
195}
196
197#[unsafe(no_mangle)]
198pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
199
200#[unsafe(no_mangle)]
201pub extern "C" fn wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool {
202    match future
203        .underlying
204        .as_mut()
205        .poll(&mut Context::from_waker(Waker::noop()))
206    {
207        Poll::Ready(()) => true,
208        Poll::Pending => false,
209    }
210}
211
212fn handle_call_error(
213    err: wasmtime::Error,
214    trap_ret: &mut *mut wasm_trap_t,
215    err_ret: &mut *mut wasmtime_error_t,
216) {
217    if err.is::<Trap>() {
218        *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
219    } else {
220        *err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err)));
221    }
222}
223
224async fn do_func_call_async(
225    mut store: RootScope<WasmtimeStoreContextMut<'_>>,
226    func: &Func,
227    args: impl ExactSizeIterator<Item = Val>,
228    results: &mut [MaybeUninit<wasmtime_val_t>],
229    trap_ret: &mut *mut wasm_trap_t,
230    err_ret: &mut *mut wasmtime_error_t,
231) {
232    let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage);
233    let (wt_params, wt_results) = translate_args(&mut params, args, results.len());
234    let result = func.call_async(&mut store, wt_params, wt_results).await;
235
236    match result {
237        Ok(()) => {
238            for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
239                crate::initialize(slot, wasmtime_val_t::from_val(&mut store, *val));
240            }
241            params.truncate(0);
242            store.as_context_mut().data_mut().wasm_val_storage = params;
243        }
244        Err(err) => handle_call_error(err, trap_ret, err_ret),
245    }
246}
247
248#[unsafe(no_mangle)]
249pub unsafe extern "C" fn wasmtime_func_call_async<'a>(
250    store: WasmtimeStoreContextMut<'a>,
251    func: &'a Func,
252    args: *const wasmtime_val_t,
253    nargs: usize,
254    results: *mut MaybeUninit<wasmtime_val_t>,
255    nresults: usize,
256    trap_ret: &'a mut *mut wasm_trap_t,
257    err_ret: &'a mut *mut wasmtime_error_t,
258) -> Box<wasmtime_call_future_t<'a>> {
259    let mut scope = RootScope::new(store);
260    let args = crate::slice_from_raw_parts(args, nargs)
261        .iter()
262        .map(|i| i.to_val(&mut scope))
263        .collect::<Vec<_>>();
264    let results = crate::slice_from_raw_parts_mut(results, nresults);
265    let fut = Box::pin(do_func_call_async(
266        scope,
267        func,
268        args.into_iter(),
269        results,
270        trap_ret,
271        err_ret,
272    ));
273    Box::new(wasmtime_call_future_t { underlying: fut })
274}
275
276#[unsafe(no_mangle)]
277pub unsafe extern "C" fn wasmtime_linker_define_async_func(
278    linker: &mut wasmtime_linker_t,
279    module: *const u8,
280    module_len: usize,
281    name: *const u8,
282    name_len: usize,
283    ty: &wasm_functype_t,
284    callback: crate::wasmtime_func_async_callback_t,
285    data: *mut c_void,
286    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
287) -> Option<Box<wasmtime_error_t>> {
288    let ty = ty.ty().ty(linker.linker.engine());
289    let module = to_str!(module, module_len);
290    let name = to_str!(name, name_len);
291    let cb = c_async_callback_to_rust_fn(callback, data, finalizer);
292
293    handle_result(
294        linker.linker.func_new_async(module, name, ty, cb),
295        |_linker| (),
296    )
297}
298
299async fn do_linker_instantiate_async(
300    linker: &wasmtime_linker_t,
301    store: WasmtimeStoreContextMut<'_>,
302    module: &wasmtime_module_t,
303    instance_ptr: &mut Instance,
304    trap_ret: &mut *mut wasm_trap_t,
305    err_ret: &mut *mut wasmtime_error_t,
306) {
307    let result = linker.linker.instantiate_async(store, &module.module).await;
308    match result {
309        Ok(instance) => *instance_ptr = instance,
310        Err(err) => handle_call_error(err, trap_ret, err_ret),
311    }
312}
313
314#[unsafe(no_mangle)]
315pub extern "C" fn wasmtime_linker_instantiate_async<'a>(
316    linker: &'a wasmtime_linker_t,
317    store: WasmtimeStoreContextMut<'a>,
318    module: &'a wasmtime_module_t,
319    instance_ptr: &'a mut Instance,
320    trap_ret: &'a mut *mut wasm_trap_t,
321    err_ret: &'a mut *mut wasmtime_error_t,
322) -> Box<crate::wasmtime_call_future_t<'a>> {
323    let fut = Box::pin(do_linker_instantiate_async(
324        linker,
325        store,
326        module,
327        instance_ptr,
328        trap_ret,
329        err_ret,
330    ));
331    Box::new(crate::wasmtime_call_future_t { underlying: fut })
332}
333
334async fn do_instance_pre_instantiate_async(
335    instance_pre: &wasmtime_instance_pre_t,
336    store: WasmtimeStoreContextMut<'_>,
337    instance_ptr: &mut Instance,
338    trap_ret: &mut *mut wasm_trap_t,
339    err_ret: &mut *mut wasmtime_error_t,
340) {
341    let result = instance_pre.underlying.instantiate_async(store).await;
342    match result {
343        Ok(instance) => *instance_ptr = instance,
344        Err(err) => handle_call_error(err, trap_ret, err_ret),
345    }
346}
347
348#[unsafe(no_mangle)]
349pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>(
350    instance_pre: &'a wasmtime_instance_pre_t,
351    store: WasmtimeStoreContextMut<'a>,
352    instance_ptr: &'a mut Instance,
353    trap_ret: &'a mut *mut wasm_trap_t,
354    err_ret: &'a mut *mut wasmtime_error_t,
355) -> Box<crate::wasmtime_call_future_t<'a>> {
356    let fut = Box::pin(do_instance_pre_instantiate_async(
357        instance_pre,
358        store,
359        instance_ptr,
360        trap_ret,
361        err_ret,
362    ));
363    Box::new(crate::wasmtime_call_future_t { underlying: fut })
364}
365
366pub type wasmtime_stack_memory_get_callback_t =
367    extern "C" fn(env: *mut std::ffi::c_void, out_len: &mut usize) -> *mut u8;
368
369#[repr(C)]
370pub struct wasmtime_stack_memory_t {
371    env: *mut std::ffi::c_void,
372    get_stack_memory: wasmtime_stack_memory_get_callback_t,
373    finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
374}
375
376struct CHostStackMemory {
377    foreign: crate::ForeignData,
378    get_memory: wasmtime_stack_memory_get_callback_t,
379}
380unsafe impl Send for CHostStackMemory {}
381unsafe impl Sync for CHostStackMemory {}
382unsafe impl StackMemory for CHostStackMemory {
383    fn top(&self) -> *mut u8 {
384        let mut len = 0;
385        let cb = self.get_memory;
386        cb(self.foreign.data, &mut len)
387    }
388    fn range(&self) -> Range<usize> {
389        let mut len = 0;
390        let cb = self.get_memory;
391        let top = cb(self.foreign.data, &mut len);
392        let base = unsafe { top.sub(len) as usize };
393        base..base + len
394    }
395    fn guard_range(&self) -> Range<*mut u8> {
396        std::ptr::null_mut()..std::ptr::null_mut()
397    }
398}
399
400pub type wasmtime_new_stack_memory_callback_t = extern "C" fn(
401    env: *mut std::ffi::c_void,
402    size: usize,
403    zeroed: bool,
404    stack_ret: &mut wasmtime_stack_memory_t,
405) -> Option<Box<wasmtime_error_t>>;
406
407#[repr(C)]
408pub struct wasmtime_stack_creator_t {
409    env: *mut std::ffi::c_void,
410    new_stack: wasmtime_new_stack_memory_callback_t,
411    finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
412}
413
414struct CHostStackCreator {
415    foreign: crate::ForeignData,
416    new_stack: wasmtime_new_stack_memory_callback_t,
417}
418unsafe impl Send for CHostStackCreator {}
419unsafe impl Sync for CHostStackCreator {}
420unsafe impl StackCreator for CHostStackCreator {
421    fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>> {
422        extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 {
423            panic!("a callback must be set");
424        }
425        let mut out = wasmtime_stack_memory_t {
426            env: ptr::null_mut(),
427            get_stack_memory: panic_callback,
428            finalizer: None,
429        };
430        let cb = self.new_stack;
431        let result = cb(self.foreign.data, size, zeroed, &mut out);
432        match result {
433            Some(error) => Err((*error).into()),
434            None => Ok(Box::new(CHostStackMemory {
435                foreign: crate::ForeignData {
436                    data: out.env,
437                    finalizer: out.finalizer,
438                },
439                get_memory: out.get_stack_memory,
440            })),
441        }
442    }
443}
444
445#[unsafe(no_mangle)]
446pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set(
447    c: &mut wasm_config_t,
448    creator: &wasmtime_stack_creator_t,
449) {
450    c.config.with_host_stack(Arc::new(CHostStackCreator {
451        foreign: crate::ForeignData {
452            data: creator.env,
453            finalizer: creator.finalizer,
454        },
455        new_stack: creator.new_stack,
456    }));
457}