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    if trap.is_none() {
144        continuation.await;
145    }
146
147    if let Some(trap) = trap {
148        return Err(trap.error);
149    }
150
151    // Translate the `wasmtime_val_t` results into the `results` space
152    for (i, result) in out_results.iter().enumerate() {
153        unsafe {
154            results[i] = result.to_val_unscoped(&mut caller.caller);
155        }
156    }
157    // Move our `vals` storage back into the store now that we no longer
158    // need it. This'll get picked up by the next hostcall and reuse our
159    // same storage.
160    hostcall_val_storage.truncate(0);
161    caller.caller.data_mut().hostcall_val_storage = hostcall_val_storage;
162    Ok(())
163}
164
165unsafe fn c_async_callback_to_rust_fn(
166    callback: wasmtime_func_async_callback_t,
167    data: *mut c_void,
168    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
169) -> impl for<'a> Fn(
170    WasmtimeCaller<'a>,
171    &'a [Val],
172    &'a mut [Val],
173) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
174+ Send
175+ Sync
176+ 'static {
177    let foreign = crate::ForeignData { data, finalizer };
178    move |caller, params, results| {
179        let _ = &foreign; // move entire foreign into this closure
180        let data = CallbackDataPtr { ptr: foreign.data };
181        Box::new(invoke_c_async_callback(
182            callback, data, caller, params, results,
183        ))
184    }
185}
186
187#[repr(transparent)]
188pub struct wasmtime_call_future_t<'a> {
189    underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
190}
191
192impl<'a> wasmtime_call_future_t<'a> {
193    pub(crate) fn new(future: Pin<Box<dyn Future<Output = ()> + 'a>>) -> Self {
194        Self { underlying: future }
195    }
196}
197
198#[unsafe(no_mangle)]
199pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
200
201#[unsafe(no_mangle)]
202pub extern "C" fn wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool {
203    match future
204        .underlying
205        .as_mut()
206        .poll(&mut Context::from_waker(Waker::noop()))
207    {
208        Poll::Ready(()) => true,
209        Poll::Pending => false,
210    }
211}
212
213fn handle_call_error(
214    err: wasmtime::Error,
215    trap_ret: &mut *mut wasm_trap_t,
216    err_ret: &mut *mut wasmtime_error_t,
217) {
218    if err.is::<Trap>() {
219        *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
220    } else {
221        *err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err)));
222    }
223}
224
225async fn do_func_call_async(
226    #[cfg(feature = "gc")] mut store: wasmtime::RootScope<WasmtimeStoreContextMut<'_>>,
227    #[cfg(not(feature = "gc"))] mut store: WasmtimeStoreContextMut<'_>,
228    func: &Func,
229    args: impl ExactSizeIterator<Item = Val>,
230    results: &mut [MaybeUninit<wasmtime_val_t>],
231    trap_ret: &mut *mut wasm_trap_t,
232    err_ret: &mut *mut wasmtime_error_t,
233) {
234    let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage);
235    let (wt_params, wt_results) = translate_args(&mut params, args, results.len());
236    let result = func.call_async(&mut store, wt_params, wt_results).await;
237
238    match result {
239        Ok(()) => {
240            for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
241                slot.write(wasmtime_val_t::from_val(&mut store, *val));
242            }
243            params.truncate(0);
244            store.as_context_mut().data_mut().wasm_val_storage = params;
245        }
246        Err(err) => handle_call_error(err, trap_ret, err_ret),
247    }
248}
249
250#[unsafe(no_mangle)]
251pub unsafe extern "C" fn wasmtime_func_call_async<'a>(
252    mut store: WasmtimeStoreContextMut<'a>,
253    func: &'a Func,
254    args: *const wasmtime_val_t,
255    nargs: usize,
256    results: *mut MaybeUninit<wasmtime_val_t>,
257    nresults: usize,
258    trap_ret: &'a mut *mut wasm_trap_t,
259    err_ret: &'a mut *mut wasmtime_error_t,
260) -> Box<wasmtime_call_future_t<'a>> {
261    let _ = &mut store;
262    #[cfg(feature = "gc")]
263    let mut store = wasmtime::RootScope::new(store);
264    let args = crate::slice_from_raw_parts(args, nargs)
265        .iter()
266        .map(|i| i.to_val(&mut store))
267        .collect::<Vec<_>>();
268    let results = crate::slice_from_raw_parts_mut(results, nresults);
269    let fut = Box::pin(do_func_call_async(
270        store,
271        func,
272        args.into_iter(),
273        results,
274        trap_ret,
275        err_ret,
276    ));
277    Box::new(wasmtime_call_future_t::new(fut))
278}
279
280#[unsafe(no_mangle)]
281pub unsafe extern "C" fn wasmtime_linker_define_async_func(
282    linker: &mut wasmtime_linker_t,
283    module: *const u8,
284    module_len: usize,
285    name: *const u8,
286    name_len: usize,
287    ty: &wasm_functype_t,
288    callback: crate::wasmtime_func_async_callback_t,
289    data: *mut c_void,
290    finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
291) -> Option<Box<wasmtime_error_t>> {
292    let ty = ty.ty().ty(linker.linker.engine());
293    let cb = c_async_callback_to_rust_fn(callback, data, finalizer);
294    let module = to_str!(module, module_len);
295    let name = to_str!(name, name_len);
296
297    handle_result(
298        linker.linker.func_new_async(module, name, ty, cb),
299        |_linker| (),
300    )
301}
302
303async fn do_linker_instantiate_async(
304    linker: &wasmtime_linker_t,
305    store: WasmtimeStoreContextMut<'_>,
306    module: &wasmtime_module_t,
307    instance_ptr: &mut Instance,
308    trap_ret: &mut *mut wasm_trap_t,
309    err_ret: &mut *mut wasmtime_error_t,
310) {
311    let result = linker.linker.instantiate_async(store, &module.module).await;
312    match result {
313        Ok(instance) => *instance_ptr = instance,
314        Err(err) => handle_call_error(err, trap_ret, err_ret),
315    }
316}
317
318#[unsafe(no_mangle)]
319pub extern "C" fn wasmtime_linker_instantiate_async<'a>(
320    linker: &'a wasmtime_linker_t,
321    store: WasmtimeStoreContextMut<'a>,
322    module: &'a wasmtime_module_t,
323    instance_ptr: &'a mut Instance,
324    trap_ret: &'a mut *mut wasm_trap_t,
325    err_ret: &'a mut *mut wasmtime_error_t,
326) -> Box<crate::wasmtime_call_future_t<'a>> {
327    let fut = Box::pin(do_linker_instantiate_async(
328        linker,
329        store,
330        module,
331        instance_ptr,
332        trap_ret,
333        err_ret,
334    ));
335    Box::new(crate::wasmtime_call_future_t::new(fut))
336}
337
338async fn do_instance_pre_instantiate_async(
339    instance_pre: &wasmtime_instance_pre_t,
340    store: WasmtimeStoreContextMut<'_>,
341    instance_ptr: &mut Instance,
342    trap_ret: &mut *mut wasm_trap_t,
343    err_ret: &mut *mut wasmtime_error_t,
344) {
345    let result = instance_pre.underlying.instantiate_async(store).await;
346    match result {
347        Ok(instance) => *instance_ptr = instance,
348        Err(err) => handle_call_error(err, trap_ret, err_ret),
349    }
350}
351
352#[unsafe(no_mangle)]
353pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>(
354    instance_pre: &'a wasmtime_instance_pre_t,
355    store: WasmtimeStoreContextMut<'a>,
356    instance_ptr: &'a mut Instance,
357    trap_ret: &'a mut *mut wasm_trap_t,
358    err_ret: &'a mut *mut wasmtime_error_t,
359) -> Box<crate::wasmtime_call_future_t<'a>> {
360    let fut = Box::pin(do_instance_pre_instantiate_async(
361        instance_pre,
362        store,
363        instance_ptr,
364        trap_ret,
365        err_ret,
366    ));
367    Box::new(crate::wasmtime_call_future_t::new(fut))
368}
369
370pub type wasmtime_stack_memory_get_callback_t =
371    extern "C" fn(env: *mut std::ffi::c_void, out_len: &mut usize) -> *mut u8;
372
373#[repr(C)]
374pub struct wasmtime_stack_memory_t {
375    env: *mut std::ffi::c_void,
376    get_stack_memory: wasmtime_stack_memory_get_callback_t,
377    finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
378}
379
380struct CHostStackMemory {
381    foreign: crate::ForeignData,
382    get_memory: wasmtime_stack_memory_get_callback_t,
383}
384unsafe impl Send for CHostStackMemory {}
385unsafe impl Sync for CHostStackMemory {}
386unsafe impl StackMemory for CHostStackMemory {
387    fn top(&self) -> *mut u8 {
388        let mut len = 0;
389        let cb = self.get_memory;
390        cb(self.foreign.data, &mut len)
391    }
392    fn range(&self) -> Range<usize> {
393        let mut len = 0;
394        let cb = self.get_memory;
395        let top = cb(self.foreign.data, &mut len);
396        let base = unsafe { top.sub(len) as usize };
397        base..base + len
398    }
399    fn guard_range(&self) -> Range<*mut u8> {
400        std::ptr::null_mut()..std::ptr::null_mut()
401    }
402}
403
404pub type wasmtime_new_stack_memory_callback_t = extern "C" fn(
405    env: *mut std::ffi::c_void,
406    size: usize,
407    zeroed: bool,
408    stack_ret: &mut wasmtime_stack_memory_t,
409) -> Option<Box<wasmtime_error_t>>;
410
411#[repr(C)]
412pub struct wasmtime_stack_creator_t {
413    env: *mut std::ffi::c_void,
414    new_stack: wasmtime_new_stack_memory_callback_t,
415    finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
416}
417
418struct CHostStackCreator {
419    foreign: crate::ForeignData,
420    new_stack: wasmtime_new_stack_memory_callback_t,
421}
422unsafe impl Send for CHostStackCreator {}
423unsafe impl Sync for CHostStackCreator {}
424unsafe impl StackCreator for CHostStackCreator {
425    fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>> {
426        extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 {
427            panic!("a callback must be set");
428        }
429        let mut out = wasmtime_stack_memory_t {
430            env: ptr::null_mut(),
431            get_stack_memory: panic_callback,
432            finalizer: None,
433        };
434        let cb = self.new_stack;
435        let result = cb(self.foreign.data, size, zeroed, &mut out);
436        match result {
437            Some(error) => Err((*error).into()),
438            None => Ok(Box::new(CHostStackMemory {
439                foreign: crate::ForeignData {
440                    data: out.env,
441                    finalizer: out.finalizer,
442                },
443                get_memory: out.get_stack_memory,
444            })),
445        }
446    }
447}
448
449#[unsafe(no_mangle)]
450pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set(
451    c: &mut wasm_config_t,
452    creator: &wasmtime_stack_creator_t,
453) {
454    c.config.with_host_stack(Arc::new(CHostStackCreator {
455        foreign: crate::ForeignData {
456            data: creator.env,
457            finalizer: creator.finalizer,
458        },
459        new_stack: creator.new_stack,
460    }));
461}