xref: /wasmtime-44.0.1/crates/c-api/src/async.rs (revision cc8d04f4)
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)]
wasmtime_config_async_stack_size_set(c: &mut wasm_config_t, size: usize)22 pub 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)]
wasmtime_context_epoch_deadline_async_yield_and_update( mut store: WasmtimeStoreContextMut<'_>, delta: u64, )27 pub 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)]
wasmtime_context_fuel_async_yield_interval( mut store: WasmtimeStoreContextMut<'_>, interval: Option<NonZeroU64>, ) -> Option<Box<wasmtime_error_t>>35 pub 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 
45 pub 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)]
57 pub 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 
63 unsafe impl Send for wasmtime_async_continuation_t {}
64 unsafe impl Sync for wasmtime_async_continuation_t {}
65 impl Drop for wasmtime_async_continuation_t {
drop(&mut self)66     fn drop(&mut self) {
67         if let Some(f) = self.finalizer {
68             f(self.env);
69         }
70     }
71 }
72 impl Future for wasmtime_async_continuation_t {
73     type Output = ();
poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::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)]
89 struct CallbackDataPtr {
90     pub ptr: *mut std::ffi::c_void,
91 }
92 
93 unsafe impl Send for CallbackDataPtr {}
94 unsafe impl Sync for CallbackDataPtr {}
95 
96 pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;
97 
invoke_c_async_callback<'a>( cb: wasmtime_func_async_callback_t, data: CallbackDataPtr, mut caller: WasmtimeCaller<'a>, params: &'a [Val], results: &'a mut [Val], ) -> Result<()>98 async 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 
c_async_callback_to_rust_fn( callback: wasmtime_func_async_callback_t, data: *mut c_void, finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, ) -> impl for<'a> Fn( WasmtimeCaller<'a>, &'a [Val], &'a mut [Val], ) -> Box<dyn Future<Output = Result<()>> + Send + 'a> + Send + Sync + 'static165 unsafe 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)]
188 pub struct wasmtime_call_future_t<'a> {
189     underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
190 }
191 
192 #[unsafe(no_mangle)]
wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>)193 pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
194 
195 #[unsafe(no_mangle)]
wasmtime_call_future_poll(future: &mut wasmtime_call_future_t) -> bool196 pub 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 
handle_call_error( err: wasmtime::Error, trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, )207 fn 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 
do_func_call_async( mut store: RootScope<WasmtimeStoreContextMut<'_>>, func: &Func, args: impl ExactSizeIterator<Item = Val>, results: &mut [MaybeUninit<wasmtime_val_t>], trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, )219 async 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)]
wasmtime_func_call_async<'a>( store: WasmtimeStoreContextMut<'a>, func: &'a Func, args: *const wasmtime_val_t, nargs: usize, results: *mut MaybeUninit<wasmtime_val_t>, nresults: usize, trap_ret: &'a mut *mut wasm_trap_t, err_ret: &'a mut *mut wasmtime_error_t, ) -> Box<wasmtime_call_future_t<'a>>244 pub 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)]
wasmtime_linker_define_async_func( linker: &mut wasmtime_linker_t, module: *const u8, module_len: usize, name: *const u8, name_len: usize, ty: &wasm_functype_t, callback: crate::wasmtime_func_async_callback_t, data: *mut c_void, finalizer: Option<extern "C" fn(*mut std::ffi::c_void)>, ) -> Option<Box<wasmtime_error_t>>272 pub 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 
do_linker_instantiate_async( linker: &wasmtime_linker_t, store: WasmtimeStoreContextMut<'_>, module: &wasmtime_module_t, instance_ptr: &mut Instance, trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, )294 async 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)]
wasmtime_linker_instantiate_async<'a>( linker: &'a wasmtime_linker_t, store: WasmtimeStoreContextMut<'a>, module: &'a wasmtime_module_t, instance_ptr: &'a mut Instance, trap_ret: &'a mut *mut wasm_trap_t, err_ret: &'a mut *mut wasmtime_error_t, ) -> Box<crate::wasmtime_call_future_t<'a>>310 pub 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 
do_instance_pre_instantiate_async( instance_pre: &wasmtime_instance_pre_t, store: WasmtimeStoreContextMut<'_>, instance_ptr: &mut Instance, trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, )329 async 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)]
wasmtime_instance_pre_instantiate_async<'a>( instance_pre: &'a wasmtime_instance_pre_t, store: WasmtimeStoreContextMut<'a>, instance_ptr: &'a mut Instance, trap_ret: &'a mut *mut wasm_trap_t, err_ret: &'a mut *mut wasmtime_error_t, ) -> Box<crate::wasmtime_call_future_t<'a>>344 pub 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 
361 pub 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)]
365 pub 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 
371 struct CHostStackMemory {
372     foreign: crate::ForeignData,
373     get_memory: wasmtime_stack_memory_get_callback_t,
374 }
375 unsafe impl Send for CHostStackMemory {}
376 unsafe impl Sync for CHostStackMemory {}
377 unsafe impl StackMemory for CHostStackMemory {
top(&self) -> *mut u8378     fn top(&self) -> *mut u8 {
379         let mut len = 0;
380         let cb = self.get_memory;
381         cb(self.foreign.data, &mut len)
382     }
range(&self) -> Range<usize>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     }
guard_range(&self) -> Range<*mut u8>390     fn guard_range(&self) -> Range<*mut u8> {
391         std::ptr::null_mut()..std::ptr::null_mut()
392     }
393 }
394 
395 pub 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)]
403 pub 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 
409 struct CHostStackCreator {
410     foreign: crate::ForeignData,
411     new_stack: wasmtime_new_stack_memory_callback_t,
412 }
413 unsafe impl Send for CHostStackCreator {}
414 unsafe impl Sync for CHostStackCreator {}
415 unsafe impl StackCreator for CHostStackCreator {
new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>>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)]
wasmtime_config_host_stack_creator_set( c: &mut wasm_config_t, creator: &wasmtime_stack_creator_t, )441 pub 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 }
453