Skip to main content

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