xref: /wasmtime-44.0.1/crates/c-api/src/async.rs (revision cccc4e64)
1 use std::ffi::c_void;
2 use std::future::Future;
3 use std::mem::{self, MaybeUninit};
4 use std::num::NonZeroU64;
5 use std::ops::Range;
6 use std::pin::Pin;
7 use std::sync::Arc;
8 use std::task::{Context, Poll};
9 use std::{ptr, str};
10 use wasmtime::{
11     AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val,
12 };
13 
14 use crate::{
15     bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t,
16     wasmtime_caller_t, wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t,
17     wasmtime_module_t, wasmtime_val_t, wasmtime_val_union, WasmtimeCaller, WasmtimeStoreContextMut,
18     WASMTIME_I32,
19 };
20 
21 #[unsafe(no_mangle)]
22 pub 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)]
27 pub 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)]
32 pub 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)]
40 pub 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 
50 pub 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)]
62 pub 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 
68 unsafe impl Send for wasmtime_async_continuation_t {}
69 unsafe impl Sync for wasmtime_async_continuation_t {}
70 impl 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 }
77 impl 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)]
94 struct CallbackDataPtr {
95     pub ptr: *mut std::ffi::c_void,
96 }
97 
98 unsafe impl Send for CallbackDataPtr {}
99 unsafe impl Sync for CallbackDataPtr {}
100 
101 pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;
102 
103 async 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 
170 unsafe 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)]
193 pub struct wasmtime_call_future_t<'a> {
194     underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
195 }
196 
197 #[unsafe(no_mangle)]
198 pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
199 
200 #[unsafe(no_mangle)]
201 pub extern "C" fn wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool {
202     let w = futures::task::noop_waker_ref();
203     match future.underlying.as_mut().poll(&mut Context::from_waker(w)) {
204         Poll::Ready(()) => true,
205         Poll::Pending => false,
206     }
207 }
208 
209 fn handle_call_error(
210     err: wasmtime::Error,
211     trap_ret: &mut *mut wasm_trap_t,
212     err_ret: &mut *mut wasmtime_error_t,
213 ) {
214     if err.is::<Trap>() {
215         *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err)));
216     } else {
217         *err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err)));
218     }
219 }
220 
221 async fn do_func_call_async(
222     mut store: RootScope<WasmtimeStoreContextMut<'_>>,
223     func: &Func,
224     args: impl ExactSizeIterator<Item = Val>,
225     results: &mut [MaybeUninit<wasmtime_val_t>],
226     trap_ret: &mut *mut wasm_trap_t,
227     err_ret: &mut *mut wasmtime_error_t,
228 ) {
229     let mut params = mem::take(&mut store.as_context_mut().data_mut().wasm_val_storage);
230     let (wt_params, wt_results) = translate_args(&mut params, args, results.len());
231     let result = func.call_async(&mut store, wt_params, wt_results).await;
232 
233     match result {
234         Ok(()) => {
235             for (slot, val) in results.iter_mut().zip(wt_results.iter()) {
236                 crate::initialize(slot, wasmtime_val_t::from_val(&mut store, *val));
237             }
238             params.truncate(0);
239             store.as_context_mut().data_mut().wasm_val_storage = params;
240         }
241         Err(err) => handle_call_error(err, trap_ret, err_ret),
242     }
243 }
244 
245 #[unsafe(no_mangle)]
246 pub unsafe extern "C" fn wasmtime_func_call_async<'a>(
247     store: WasmtimeStoreContextMut<'a>,
248     func: &'a Func,
249     args: *const wasmtime_val_t,
250     nargs: usize,
251     results: *mut MaybeUninit<wasmtime_val_t>,
252     nresults: usize,
253     trap_ret: &'a mut *mut wasm_trap_t,
254     err_ret: &'a mut *mut wasmtime_error_t,
255 ) -> Box<wasmtime_call_future_t<'a>> {
256     let mut scope = RootScope::new(store);
257     let args = crate::slice_from_raw_parts(args, nargs)
258         .iter()
259         .map(|i| i.to_val(&mut scope))
260         .collect::<Vec<_>>();
261     let results = crate::slice_from_raw_parts_mut(results, nresults);
262     let fut = Box::pin(do_func_call_async(
263         scope,
264         func,
265         args.into_iter(),
266         results,
267         trap_ret,
268         err_ret,
269     ));
270     Box::new(wasmtime_call_future_t { underlying: fut })
271 }
272 
273 #[unsafe(no_mangle)]
274 pub unsafe extern "C" fn wasmtime_linker_define_async_func(
275     linker: &mut wasmtime_linker_t,
276     module: *const u8,
277     module_len: usize,
278     name: *const u8,
279     name_len: usize,
280     ty: &wasm_functype_t,
281     callback: crate::wasmtime_func_async_callback_t,
282     data: *mut c_void,
283     finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>,
284 ) -> Option<Box<wasmtime_error_t>> {
285     let ty = ty.ty().ty(linker.linker.engine());
286     let module = to_str!(module, module_len);
287     let name = to_str!(name, name_len);
288     let cb = c_async_callback_to_rust_fn(callback, data, finalizer);
289 
290     handle_result(
291         linker.linker.func_new_async(module, name, ty, cb),
292         |_linker| (),
293     )
294 }
295 
296 async fn do_linker_instantiate_async(
297     linker: &wasmtime_linker_t,
298     store: WasmtimeStoreContextMut<'_>,
299     module: &wasmtime_module_t,
300     instance_ptr: &mut Instance,
301     trap_ret: &mut *mut wasm_trap_t,
302     err_ret: &mut *mut wasmtime_error_t,
303 ) {
304     let result = linker.linker.instantiate_async(store, &module.module).await;
305     match result {
306         Ok(instance) => *instance_ptr = instance,
307         Err(err) => handle_call_error(err, trap_ret, err_ret),
308     }
309 }
310 
311 #[unsafe(no_mangle)]
312 pub extern "C" fn wasmtime_linker_instantiate_async<'a>(
313     linker: &'a wasmtime_linker_t,
314     store: WasmtimeStoreContextMut<'a>,
315     module: &'a wasmtime_module_t,
316     instance_ptr: &'a mut Instance,
317     trap_ret: &'a mut *mut wasm_trap_t,
318     err_ret: &'a mut *mut wasmtime_error_t,
319 ) -> Box<crate::wasmtime_call_future_t<'a>> {
320     let fut = Box::pin(do_linker_instantiate_async(
321         linker,
322         store,
323         module,
324         instance_ptr,
325         trap_ret,
326         err_ret,
327     ));
328     Box::new(crate::wasmtime_call_future_t { underlying: fut })
329 }
330 
331 async fn do_instance_pre_instantiate_async(
332     instance_pre: &wasmtime_instance_pre_t,
333     store: WasmtimeStoreContextMut<'_>,
334     instance_ptr: &mut Instance,
335     trap_ret: &mut *mut wasm_trap_t,
336     err_ret: &mut *mut wasmtime_error_t,
337 ) {
338     let result = instance_pre.underlying.instantiate_async(store).await;
339     match result {
340         Ok(instance) => *instance_ptr = instance,
341         Err(err) => handle_call_error(err, trap_ret, err_ret),
342     }
343 }
344 
345 #[unsafe(no_mangle)]
346 pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>(
347     instance_pre: &'a wasmtime_instance_pre_t,
348     store: WasmtimeStoreContextMut<'a>,
349     instance_ptr: &'a mut Instance,
350     trap_ret: &'a mut *mut wasm_trap_t,
351     err_ret: &'a mut *mut wasmtime_error_t,
352 ) -> Box<crate::wasmtime_call_future_t<'a>> {
353     let fut = Box::pin(do_instance_pre_instantiate_async(
354         instance_pre,
355         store,
356         instance_ptr,
357         trap_ret,
358         err_ret,
359     ));
360     Box::new(crate::wasmtime_call_future_t { underlying: fut })
361 }
362 
363 pub type wasmtime_stack_memory_get_callback_t =
364     extern "C" fn(env: *mut std::ffi::c_void, out_len: &mut usize) -> *mut u8;
365 
366 #[repr(C)]
367 pub struct wasmtime_stack_memory_t {
368     env: *mut std::ffi::c_void,
369     get_stack_memory: wasmtime_stack_memory_get_callback_t,
370     finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
371 }
372 
373 struct CHostStackMemory {
374     foreign: crate::ForeignData,
375     get_memory: wasmtime_stack_memory_get_callback_t,
376 }
377 unsafe impl Send for CHostStackMemory {}
378 unsafe impl Sync for CHostStackMemory {}
379 unsafe impl StackMemory for CHostStackMemory {
380     fn top(&self) -> *mut u8 {
381         let mut len = 0;
382         let cb = self.get_memory;
383         cb(self.foreign.data, &mut len)
384     }
385     fn range(&self) -> Range<usize> {
386         let mut len = 0;
387         let cb = self.get_memory;
388         let top = cb(self.foreign.data, &mut len);
389         let base = unsafe { top.sub(len) as usize };
390         base..base + len
391     }
392     fn guard_range(&self) -> Range<*mut u8> {
393         std::ptr::null_mut()..std::ptr::null_mut()
394     }
395 }
396 
397 pub type wasmtime_new_stack_memory_callback_t = extern "C" fn(
398     env: *mut std::ffi::c_void,
399     size: usize,
400     zeroed: bool,
401     stack_ret: &mut wasmtime_stack_memory_t,
402 ) -> Option<Box<wasmtime_error_t>>;
403 
404 #[repr(C)]
405 pub struct wasmtime_stack_creator_t {
406     env: *mut std::ffi::c_void,
407     new_stack: wasmtime_new_stack_memory_callback_t,
408     finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>,
409 }
410 
411 struct CHostStackCreator {
412     foreign: crate::ForeignData,
413     new_stack: wasmtime_new_stack_memory_callback_t,
414 }
415 unsafe impl Send for CHostStackCreator {}
416 unsafe impl Sync for CHostStackCreator {}
417 unsafe impl StackCreator for CHostStackCreator {
418     fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>> {
419         extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 {
420             panic!("a callback must be set");
421         }
422         let mut out = wasmtime_stack_memory_t {
423             env: ptr::null_mut(),
424             get_stack_memory: panic_callback,
425             finalizer: None,
426         };
427         let cb = self.new_stack;
428         let result = cb(self.foreign.data, size, zeroed, &mut out);
429         match result {
430             Some(error) => Err((*error).into()),
431             None => Ok(Box::new(CHostStackMemory {
432                 foreign: crate::ForeignData {
433                     data: out.env,
434                     finalizer: out.finalizer,
435                 },
436                 get_memory: out.get_stack_memory,
437             })),
438         }
439     }
440 }
441 
442 #[unsafe(no_mangle)]
443 pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set(
444     c: &mut wasm_config_t,
445     creator: &wasmtime_stack_creator_t,
446 ) {
447     c.config.with_host_stack(Arc::new(CHostStackCreator {
448         foreign: crate::ForeignData {
449             data: creator.env,
450             finalizer: creator.finalizer,
451         },
452         new_stack: creator.new_stack,
453     }));
454 }
455