#![allow(clippy::indexing_slicing)]
use super::epoch::{apply_epoch_deadline_async, yield_now};
use super::host::{enter, finish};
use super::{Execution, Outcome};
use crate::extern_::{Memory, Table};
use crate::func::{Caller, Func};
use crate::instance::Instance;
use crate::module::code::Code;
use crate::store::{FuncEntity, Store};
use crate::value::{Ref, Val, ValType};
use crate::Result;
pub(crate) async fn execute_async<T>(
store: &mut Store<T>,
instance: Instance,
func_index: u32,
code: Code,
args: Vec<Val>,
result_tys: &[ValType],
) -> Result<Vec<Val>> {
let (mut exec, b) = enter(store, instance, func_index, code, args);
let outcome = drive_async(&mut exec, store, b.run_stop).await;
finish(store, exec, &b, result_tys, outcome)
}
async fn drive_async<T>(exec: &mut Execution, store: &mut Store<T>, run_stop: usize) -> Result<()> {
loop {
match exec.run(store, run_stop)? {
Outcome::Finished => return Ok(()),
Outcome::HostAsync { func, instance } => {
exec.invoke_host_async(store, func, instance, run_stop)
.await?;
exec.gc_pressure_safepoint(&mut store.inner);
}
Outcome::FuelYield => {
yield_now().await;
store.inner.refuel_from_reserve();
}
Outcome::EpochDeadline => {
if let Err(e) = apply_epoch_deadline_async(exec, store).await {
return Err(exec.attach_suspension_backtrace(&store.inner, e));
}
}
Outcome::Grow { memory, delta } => exec.do_grow_async(store, memory, delta).await?,
Outcome::TableGrow { table, delta, init } => {
exec.do_grow_table_async(store, table, delta, init).await?;
}
Outcome::GcGrow {
reserved_target,
bytes_needed,
} => store.grow_gc_reservation(reserved_target, bytes_needed)?,
}
}
}
impl Execution {
async fn invoke_host_async<T>(
&mut self,
store: &mut Store<T>,
func: Func,
instance: Instance,
stop_depth: usize,
) -> Result<()> {
let (mut scratch, roots_mark, host_index) = self.prep_host_async(store, func);
let cb = store.async_host_funcs[host_index as usize].clone();
store.inner.swap_exec(self);
let outcome = {
let caller = Caller::new(store.as_context_mut(), Some(instance));
let fut = cb(caller, &scratch.0, &mut scratch.1);
super::guard::CatchUnwind(std::boxed::Box::into_pin(fut)).await
};
store.inner.swap_exec(self);
let outcome = match outcome {
Ok(outcome) => outcome,
Err(payload) => {
super::guard::restore_after_panic(&mut store.inner, roots_mark);
super::guard::reraise(payload);
}
};
self.finish_host_async(store, outcome, scratch, roots_mark, stop_depth)
}
fn prep_host_async<T>(
&mut self,
store: &mut Store<T>,
func: Func,
) -> ((Vec<Val>, Vec<Val>), usize, u32) {
let (mut params, mut results) = store.inner.take_host_scratch();
let host_index = match store.inner.func(func) {
FuncEntity::HostAsync {
sig, host_index, ..
} => {
results.clear();
results.extend_from_slice(&sig.result_defaults);
params.clear();
self.pop_params_into(&sig.params, &mut params);
*host_index
}
_ => unreachable!("HostAsync only suspends on async host funcs"),
};
let roots_mark = store.inner.gc_roots_mark();
super::host::root_ref_params(&mut store.inner, ¶ms);
((params, results), roots_mark, host_index)
}
fn finish_host_async<T>(
&mut self,
store: &mut Store<T>,
outcome: Result<()>,
scratch: (Vec<Val>, Vec<Val>),
roots_mark: usize,
stop_depth: usize,
) -> Result<()> {
let (params, results) = scratch;
if let Err(e) = outcome {
store.inner.gc_roots_truncate(roots_mark);
return self.host_call_error(&mut store.inner, e, stop_depth);
}
store.inner.take_pending_exception();
self.push_results_slice(&results);
store.inner.put_host_scratch(params, results);
store.inner.gc_roots_truncate(roots_mark);
Ok(())
}
async fn do_grow_async<T>(
&mut self,
store: &mut Store<T>,
memory: Memory,
delta: u64,
) -> Result<()> {
let is_64 = store.inner.memory(memory).ty.is_64();
let old = store.grow_memory_async(memory, delta).await?;
self.push_index(is_64, old.unwrap_or(u64::MAX));
Ok(())
}
async fn do_grow_table_async<T>(
&mut self,
store: &mut Store<T>,
table: Table,
delta: u64,
init: Ref,
) -> Result<()> {
let is_64 = store.inner.table(table).ty.is_64();
let old = store.grow_table_async(table, delta, init).await?;
self.push_index(is_64, old.unwrap_or(u64::MAX));
Ok(())
}
}