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