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