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}; 9 use std::{ptr, str}; 10 use wasmtime::{ 11 AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val, 12 }; 13 14 use 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)] 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 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 209 fn 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 221 async 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)] 246 pub 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)] 274 pub 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 296 async 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)] 312 pub 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 331 async 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)] 346 pub 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 363 pub 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)] 367 pub 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 373 struct CHostStackMemory { 374 foreign: crate::ForeignData, 375 get_memory: wasmtime_stack_memory_get_callback_t, 376 } 377 unsafe impl Send for CHostStackMemory {} 378 unsafe impl Sync for CHostStackMemory {} 379 unsafe 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 397 pub 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)] 405 pub 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 411 struct CHostStackCreator { 412 foreign: crate::ForeignData, 413 new_stack: wasmtime_new_stack_memory_callback_t, 414 } 415 unsafe impl Send for CHostStackCreator {} 416 unsafe impl Sync for CHostStackCreator {} 417 unsafe 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)] 443 pub 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 } 455