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_stack_size_set(c: &mut wasm_config_t, size: usize) { 23 c.config.async_stack_size(size); 24 } 25 26 #[unsafe(no_mangle)] 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)] 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 { 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 = (); 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 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 165 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)] 193 pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {} 194 195 #[unsafe(no_mangle)] 196 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 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 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)] 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)] 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 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)] 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 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)] 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 { 378 fn top(&self) -> *mut u8 { 379 let mut len = 0; 380 let cb = self.get_memory; 381 cb(self.foreign.data, &mut len) 382 } 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 } 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 { 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)] 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