use std::f32::consts::E;
use super::*;
#[cfg(feature = "journal")]
use crate::journal::JournalEffector;
use crate::{
capture_store_snapshot,
os::task::thread::WasiMemoryLayout,
runtime::{
task_manager::{TaskWasm, TaskWasmRunProperties},
TaintReason,
},
syscalls::*,
WasiThreadHandle,
};
use wasmer::Memory;
use wasmer_wasix_types::wasi::ThreadStart;
#[instrument(level = "trace", skip_all, ret)]
pub fn thread_spawn_v2<M: MemorySize>(
mut ctx: FunctionEnvMut<'_, WasiEnv>,
start_ptr: WasmPtr<ThreadStart<M>, M>,
ret_tid: WasmPtr<Tid, M>,
) -> Errno {
let tid = wasi_try!(thread_spawn_internal_from_wasi(&mut ctx, start_ptr));
let memory = unsafe { ctx.data().memory_view(&ctx) };
wasi_try_mem!(ret_tid.write(&memory, tid));
tracing::debug!(
tid,
from_tid = ctx.data().thread.id().raw(),
"spawned new thread"
);
Errno::Success
}
pub fn thread_spawn_internal_from_wasi<M: MemorySize>(
ctx: &mut FunctionEnvMut<'_, WasiEnv>,
start_ptr: WasmPtr<ThreadStart<M>, M>,
) -> Result<Tid, Errno> {
let env = ctx.data();
let memory = unsafe { env.memory_view(&ctx) };
let runtime = env.runtime.clone();
let tasks = env.tasks().clone();
let start_ptr_offset = start_ptr.offset();
let layout = {
let start: ThreadStart<M> = start_ptr.read(&memory).map_err(mem_error_to_wasi)?;
let stack_upper: u64 = start.stack_upper.into();
let stack_size: u64 = start.stack_size.into();
let guard_size: u64 = start.guard_size.into();
let tls_base: u64 = start.tls_base.into();
let stack_lower = stack_upper - stack_size;
WasiMemoryLayout {
stack_upper,
stack_lower,
guard_size,
stack_size,
}
};
tracing::trace!(
from_tid = env.thread.id().raw(),
"thread_spawn with layout {:?}",
layout
);
let thread_start = ThreadStartType::ThreadSpawn {
start_ptr: start_ptr_offset.into(),
};
let mut thread_handle = match env.process.new_thread(layout.clone(), thread_start) {
Ok(h) => Arc::new(h),
Err(err) => {
error!(
stack_base = layout.stack_lower,
"failed to create thread handle",
);
return Err(Errno::Access);
}
};
let thread_id: Tid = thread_handle.id().into();
Span::current().record("tid", thread_id);
thread_spawn_internal_using_layout::<M>(ctx, thread_handle, layout, start_ptr_offset, None)?;
Ok(thread_id)
}
pub fn thread_spawn_internal_using_layout<M: MemorySize>(
ctx: &mut FunctionEnvMut<'_, WasiEnv>,
thread_handle: Arc<WasiThreadHandle>,
layout: WasiMemoryLayout,
start_ptr_offset: M::Offset,
rewind_state: Option<(RewindState, RewindResultType)>,
) -> Result<(), Errno> {
let env = ctx.data();
let tasks = env.tasks().clone();
let thread_memory = unsafe { env.inner() }.memory_clone();
let state = env.state.clone();
let mut thread_env = env.clone();
thread_env.thread = thread_handle.as_thread();
thread_env.layout = layout;
thread_env.enable_deep_sleep = if cfg!(feature = "js") {
false
} else {
unsafe { env.capable_of_deep_sleep() }
};
let mut execute_module = {
let thread_handle = thread_handle;
move |ctx: WasiFunctionEnv, mut store: Store| {
call_module::<M>(ctx, store, start_ptr_offset, thread_handle, rewind_state)
}
};
if unsafe { env.inner() }.thread_spawn.is_none() {
warn!("thread failed - the program does not export a `wasi_thread_start` function");
return Err(Errno::Notcapable);
}
let thread_module = unsafe { env.inner() }.module_clone();
let globals = capture_store_snapshot(&mut ctx.as_store_mut());
let spawn_type =
crate::runtime::SpawnMemoryType::ShareMemory(thread_memory, ctx.as_store_ref());
trace!("threading: spawning background thread");
let run = move |props: TaskWasmRunProperties| {
execute_module(props.ctx, props.store);
};
tasks
.task_wasm(
TaskWasm::new(Box::new(run), thread_env, thread_module, false)
.with_globals(&globals)
.with_memory(spawn_type),
)
.map_err(Into::<Errno>::into)?;
Ok(())
}
fn call_module<M: MemorySize>(
mut ctx: WasiFunctionEnv,
mut store: Store,
start_ptr_offset: M::Offset,
thread_handle: Arc<WasiThreadHandle>,
rewind_state: Option<(RewindState, RewindResultType)>,
) -> Result<Tid, Errno> {
let env = ctx.data(&store);
let tasks = env.tasks().clone();
let call_module_internal = move |env: &WasiFunctionEnv, store: &mut Store| {
let spawn = unsafe { env.data(&store).inner() }
.thread_spawn
.clone()
.unwrap();
let tid = env.data(&store).tid();
let call_ret = spawn.call(
store,
tid.raw().try_into().map_err(|_| Errno::Overflow).unwrap(),
start_ptr_offset
.try_into()
.map_err(|_| Errno::Overflow)
.unwrap(),
);
trace!("callback finished (ret={:?})", call_ret);
let mut ret = Errno::Success;
let mut exit_code = None;
if let Err(err) = call_ret {
match err.downcast::<WasiError>() {
Ok(WasiError::ThreadExit) => {
trace!("thread exited cleanly");
ret = Errno::Success;
}
Ok(WasiError::Exit(code)) => {
trace!(exit_code = ?code, "thread requested exit");
exit_code = Some(code);
ret = if code.is_success() {
Errno::Success
} else {
env.data(&store)
.runtime
.on_taint(TaintReason::NonZeroExitCode(code));
Errno::Noexec
};
}
Ok(WasiError::DeepSleep(deep)) => {
trace!("entered a deep sleep");
return Err(deep);
}
Ok(WasiError::UnknownWasiVersion) => {
debug!("failed as wasi version is unknown",);
env.data(&store)
.runtime
.on_taint(TaintReason::UnknownWasiVersion);
ret = Errno::Noexec;
exit_code = Some(ExitCode::from(128 + ret as i32));
}
Err(err) => {
debug!("failed with runtime error: {}", err);
env.data(&store)
.runtime
.on_taint(TaintReason::RuntimeError(err));
ret = Errno::Noexec;
exit_code = Some(ExitCode::from(128 + ret as i32));
}
}
} else {
debug!("thread exited cleanly without calling thread_exit");
}
env.on_exit(store, exit_code);
Ok(ret as u32)
};
if let Some((rewind_state, rewind_result)) = rewind_state {
let mut ctx = ctx.env.clone().into_mut(&mut store);
let res = rewind_ext::<M>(
&mut ctx,
Some(rewind_state.memory_stack),
rewind_state.rewind_stack,
rewind_state.store_data,
rewind_result,
);
if res != Errno::Success {
return Err(res);
}
}
let ret = call_module_internal(&ctx, &mut store);
match ret {
Ok(ret) => {
drop(thread_handle);
Ok(ret as Pid)
}
Err(deep) => {
let rewind = deep.rewind;
let respawn = {
let tasks = tasks.clone();
move |ctx, store, trigger_res| {
call_module::<M>(
ctx,
store,
start_ptr_offset,
thread_handle,
Some((rewind, RewindResultType::RewindWithResult(trigger_res))),
);
}
};
unsafe {
tasks.resume_wasm_after_poller(Box::new(respawn), ctx, store, deep.trigger)
};
Err(Errno::Unknown)
}
}
}