xref: /wasmtime-44.0.1/crates/c-api/src/async.rs (revision b112bb85)
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, Waker};
9 use std::{ptr, str};
10 use wasmtime::{
11     AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val,
12 };
13 
14 use 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)]
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     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 
212 fn 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 
224 async 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)]
249 pub 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)]
277 pub 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 
299 async 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)]
315 pub 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 
334 async 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)]
349 pub 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 
366 pub 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)]
370 pub 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 
376 struct CHostStackMemory {
377     foreign: crate::ForeignData,
378     get_memory: wasmtime_stack_memory_get_callback_t,
379 }
380 unsafe impl Send for CHostStackMemory {}
381 unsafe impl Sync for CHostStackMemory {}
382 unsafe 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 
400 pub 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)]
408 pub 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 
414 struct CHostStackCreator {
415     foreign: crate::ForeignData,
416     new_stack: wasmtime_new_stack_memory_callback_t,
417 }
418 unsafe impl Send for CHostStackCreator {}
419 unsafe impl Sync for CHostStackCreator {}
420 unsafe 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)]
446 pub 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 }
458