use std::sync::Arc;
use super::direct_call::{
call_direct_host, finish_direct_host_call, preflight_direct_call, DirectCallBoundary,
};
use super::wasm_ty::valtypes_of;
use super::{check_args, default_results, validate_results, Callee, Caller, Func, TypedFunc};
use super::{WasmParams, WasmResults, WasmRet};
use crate::func::into_async_func;
use crate::store::{AsContextMut, FuncEntity};
use crate::value::{FuncType, Val};
use crate::Result;
async fn call_direct_host_async<S: AsContextMut>(
store: &mut S,
host_index: u32,
params: &[Val],
ty: &crate::value::FuncType,
) -> Result<Vec<Val>> {
preflight_direct_call(store.as_context_mut().inner_mut())?;
let cb = store.as_context_mut().store_mut().async_host_funcs[host_index as usize].clone();
let mut out = default_results(ty);
let boundary = DirectCallBoundary::capture(store.as_context().inner());
let token = store.as_context_mut().inner_mut().begin_async_call_cleanup(
Some(boundary.roots_mark),
boundary.pending,
boundary.pending_generation,
None,
);
let mut cancellation = crate::exec::guard::AsyncCancellation::new(token);
let guarded = async {
Box::into_pin(cb(
Caller::new(store.as_context_mut(), None),
params,
&mut out,
))
.await
};
let outcome = crate::exec::guard::CatchUnwind(Box::pin(guarded)).await;
let cleanup = store
.as_context_mut()
.inner_mut()
.complete_async_call_cleanup(cancellation.token());
cancellation.disarm();
let result = match outcome {
Ok(result) => result,
Err(payload) => {
boundary.restore_after_panic(store.as_context_mut().inner_mut());
crate::exec::guard::reraise(payload);
}
};
finish_async_cleanup(store.as_context_mut().inner_mut(), boundary, cleanup)?;
let result_types: Vec<_> = ty.results().collect();
finish_direct_host_call(
store.as_context_mut().inner_mut(),
boundary,
out,
&result_types,
result,
)
}
fn finish_async_cleanup(
inner: &mut crate::store::StoreInner,
boundary: DirectCallBoundary,
cleanup: Result<()>,
) -> Result<()> {
let Err(error) = cleanup else {
return Ok(());
};
inner.gc_roots_truncate(boundary.roots_mark);
inner.restore_pending_exception(boundary.pending, boundary.pending_generation);
if inner.has_parked_execution() {
if let Some(internal) = error.downcast_ref::<crate::error::InternalError>() {
inner.latch_internal_error(*internal);
}
}
Err(error)
}
impl Func {
pub fn new_async<T, F>(mut store: impl AsContextMut<Data = T>, ty: FuncType, func: F) -> Func
where
F: for<'a> Fn(
Caller<'a, T>,
&'a [Val],
&'a mut [Val],
)
-> std::boxed::Box<dyn std::future::Future<Output = Result<()>> + Send + 'a>
+ Send
+ Sync
+ 'static,
T: Send + 'static,
{
assert!(
ty.engine().same(store.as_context().engine()),
"function type belongs to a different engine"
);
let mut ctx = store.as_context_mut();
let host_index = ctx.store_mut().push_async_host_func(Arc::new(func));
ctx.inner_mut().alloc_func(FuncEntity::HostAsync {
sig: crate::store::HostSig::new(&ty),
ty,
host_index,
})
}
pub fn wrap_async<T, F, P, R>(mut store: impl AsContextMut<Data = T>, func: F) -> Func
where
F: for<'a> Fn(
Caller<'a, T>,
P,
)
-> std::boxed::Box<dyn std::future::Future<Output = R> + Send + 'a>
+ Send
+ Sync
+ 'static,
P: WasmResults,
R: WasmRet + 'static,
T: Send + 'static,
{
let engine = store.as_context().engine().clone();
let (ty, cb) = into_async_func(&engine, func);
let mut ctx = store.as_context_mut();
let host_index = ctx.store_mut().push_async_host_func(cb);
ctx.inner_mut().alloc_func(FuncEntity::HostAsync {
sig: crate::store::HostSig::new(&ty),
ty,
host_index,
})
}
pub async fn call_async(
&self,
mut store: impl AsContextMut,
params: &[Val],
results: &mut [Val],
) -> Result<()> {
if !store.as_context().engine().is_async() {
return Err(crate::Error::msg(
"cannot use `call_async` without `Config::async_support(true)`",
));
}
let ty = self.ty(&store);
check_args(store.as_context().inner(), params, &ty)?;
if results.len() != ty.results().len() {
return Err(crate::Error::msg("wrong number of results"));
}
let callee = self.resolve_callee(store.as_context().inner());
let out = match callee {
Callee::Wasm(instance, func_index) => {
let code = store
.as_context()
.inner()
.instance(instance)
.module
.code(func_index);
let result_tys: Vec<crate::value::ValType> = ty.results().collect();
let args = crate::extern_::coerce_args(
&mut store.as_context_mut().store_mut().inner,
params,
&ty,
)?;
crate::exec::host_async::execute_async(
store.as_context_mut().store_mut(),
instance,
func_index,
code,
args,
&result_tys,
)
.await?
}
Callee::Host(host_index) => call_direct_host(&mut store, host_index, params, &ty)?,
Callee::HostAsync(host_index) => {
call_direct_host_async(&mut store, host_index, params, &ty).await?
}
};
let result_tys: Vec<_> = ty.results().collect();
validate_results(store.as_context_mut().inner_mut(), &out, &result_tys)?;
results.clone_from_slice(&out);
Ok(())
}
}
impl<Params, Results> TypedFunc<Params, Results>
where
Params: WasmParams,
Results: WasmResults,
{
pub async fn call_async(
&self,
mut store: impl AsContextMut,
params: Params,
) -> Result<Results> {
let mut args = Vec::new();
params.into_vals(&mut args);
let mut results = vec![Val::I32(0); valtypes_of::<Results>().len()];
self.func
.call_async(&mut store, &args, &mut results)
.await?;
Ok(Results::from_vals(&results))
}
}