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};
9use std::{ptr, str};
10use wasmtime::{
11 AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val,
12};
13
14use 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)]
22pub 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)]
27pub 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)]
32pub 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)]
40pub 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
50pub 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)]
62pub 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
68unsafe impl Send for wasmtime_async_continuation_t {}
69unsafe impl Sync for wasmtime_async_continuation_t {}
70impl 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}
77impl 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#[derive(Debug)]
94struct CallbackDataPtr {
95 pub ptr: *mut std::ffi::c_void,
96}
97
98unsafe impl Send for CallbackDataPtr {}
99unsafe impl Sync for CallbackDataPtr {}
100
101pub type wasmtime_func_async_continuation_callback_t = extern "C" fn(*mut c_void) -> bool;
102
103async 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 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 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 for (i, result) in out_results.iter().enumerate() {
158 unsafe {
159 results[i] = result.to_val_unscoped(&mut caller.caller);
160 }
161 }
162 hostcall_val_storage.truncate(0);
166 caller.caller.data_mut().hostcall_val_storage = hostcall_val_storage;
167 Ok(())
168}
169
170unsafe 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; 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)]
193pub struct wasmtime_call_future_t<'a> {
194 underlying: Pin<Box<dyn Future<Output = ()> + 'a>>,
195}
196
197#[unsafe(no_mangle)]
198pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {}
199
200#[unsafe(no_mangle)]
201pub 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
209fn 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
221async 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)]
246pub 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)]
274pub 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
296async 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)]
312pub 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
331async 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)]
346pub 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
363pub 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)]
367pub 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
373struct CHostStackMemory {
374 foreign: crate::ForeignData,
375 get_memory: wasmtime_stack_memory_get_callback_t,
376}
377unsafe impl Send for CHostStackMemory {}
378unsafe impl Sync for CHostStackMemory {}
379unsafe 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
397pub 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)]
405pub 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
411struct CHostStackCreator {
412 foreign: crate::ForeignData,
413 new_stack: wasmtime_new_stack_memory_callback_t,
414}
415unsafe impl Send for CHostStackCreator {}
416unsafe impl Sync for CHostStackCreator {}
417unsafe 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)]
443pub 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}