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