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 11 use wasmtime::{ 12 AsContextMut, Caller, Func, Instance, Result, StackCreator, StackMemory, Trap, Val, 13 }; 14 15 use crate::{ 16 bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, 17 wasmtime_caller_t, wasmtime_error_t, wasmtime_instance_pre_t, wasmtime_linker_t, 18 wasmtime_module_t, wasmtime_val_t, wasmtime_val_union, CStoreContextMut, WASMTIME_I32, 19 }; 20 21 #[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 #[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 #[no_mangle] 32 pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update( 33 mut store: CStoreContextMut<'_>, 34 delta: u64, 35 ) { 36 store.epoch_deadline_async_yield_and_update(delta); 37 } 38 39 #[no_mangle] 40 pub extern "C" fn wasmtime_context_fuel_async_yield_interval( 41 mut store: CStoreContextMut<'_>, 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: Caller<'a, crate::StoreData>, 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(&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 continutation 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(&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 Caller<'a, crate::StoreData>, 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 #[no_mangle] 198 pub extern "C" fn wasmtime_call_future_delete(_future: Box<wasmtime_call_future_t>) {} 199 200 #[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: CStoreContextMut<'_>, 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 store = store.as_context_mut(); 230 let mut params = mem::take(&mut store.data_mut().wasm_val_storage); 231 let (wt_params, wt_results) = translate_args(&mut params, args, results.len()); 232 let result = func.call_async(&mut store, wt_params, wt_results).await; 233 234 match result { 235 Ok(()) => { 236 for (slot, val) in results.iter_mut().zip(wt_results.iter()) { 237 crate::initialize(slot, wasmtime_val_t::from_val(&mut store, val.clone())); 238 } 239 params.truncate(0); 240 store.data_mut().wasm_val_storage = params; 241 } 242 Err(err) => handle_call_error(err, trap_ret, err_ret), 243 } 244 } 245 246 #[no_mangle] 247 pub unsafe extern "C" fn wasmtime_func_call_async<'a>( 248 mut store: CStoreContextMut<'a>, 249 func: &'a Func, 250 args: *const wasmtime_val_t, 251 nargs: usize, 252 results: *mut MaybeUninit<wasmtime_val_t>, 253 nresults: usize, 254 trap_ret: &'a mut *mut wasm_trap_t, 255 err_ret: &'a mut *mut wasmtime_error_t, 256 ) -> Box<wasmtime_call_future_t<'a>> { 257 let args = crate::slice_from_raw_parts(args, nargs) 258 .iter() 259 .map(|i| i.to_val(&mut store)) 260 .collect::<Vec<_>>(); 261 let results = crate::slice_from_raw_parts_mut(results, nresults); 262 let fut = Box::pin(do_func_call_async( 263 store, 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 #[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: CStoreContextMut<'_>, 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 #[no_mangle] 312 pub extern "C" fn wasmtime_linker_instantiate_async<'a>( 313 linker: &'a wasmtime_linker_t, 314 store: CStoreContextMut<'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: CStoreContextMut<'_>, 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 #[no_mangle] 346 pub extern "C" fn wasmtime_instance_pre_instantiate_async<'a>( 347 instance_pre: &'a wasmtime_instance_pre_t, 348 store: CStoreContextMut<'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 } 393 394 pub type wasmtime_new_stack_memory_callback_t = extern "C" fn( 395 env: *mut std::ffi::c_void, 396 size: usize, 397 stack_ret: &mut wasmtime_stack_memory_t, 398 ) -> Option<Box<wasmtime_error_t>>; 399 400 #[repr(C)] 401 pub struct wasmtime_stack_creator_t { 402 env: *mut std::ffi::c_void, 403 new_stack: wasmtime_new_stack_memory_callback_t, 404 finalizer: Option<extern "C" fn(arg1: *mut std::ffi::c_void)>, 405 } 406 407 struct CHostStackCreator { 408 foreign: crate::ForeignData, 409 new_stack: wasmtime_new_stack_memory_callback_t, 410 } 411 unsafe impl Send for CHostStackCreator {} 412 unsafe impl Sync for CHostStackCreator {} 413 unsafe impl StackCreator for CHostStackCreator { 414 fn new_stack(&self, size: usize) -> Result<Box<dyn wasmtime::StackMemory>> { 415 extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 { 416 panic!("a callback must be set"); 417 } 418 let mut out = wasmtime_stack_memory_t { 419 env: ptr::null_mut(), 420 get_stack_memory: panic_callback, 421 finalizer: None, 422 }; 423 let cb = self.new_stack; 424 let result = cb(self.foreign.data, size, &mut out); 425 match result { 426 Some(error) => Err((*error).into()), 427 None => Ok(Box::new(CHostStackMemory { 428 foreign: crate::ForeignData { 429 data: out.env, 430 finalizer: out.finalizer, 431 }, 432 get_memory: out.get_stack_memory, 433 })), 434 } 435 } 436 } 437 438 #[no_mangle] 439 pub unsafe extern "C" fn wasmtime_config_host_stack_creator_set( 440 c: &mut wasm_config_t, 441 creator: &wasmtime_stack_creator_t, 442 ) { 443 c.config.with_host_stack(Arc::new(CHostStackCreator { 444 foreign: crate::ForeignData { 445 data: creator.env, 446 finalizer: creator.finalizer, 447 }, 448 new_stack: creator.new_stack, 449 })); 450 } 451