wasmer_wasix/syscalls/wasix/
thread_spawn.rs1use std::f32::consts::E;
2
3use super::*;
4#[cfg(feature = "journal")]
5use crate::journal::JournalEffector;
6use crate::{
7    WasiThreadHandle,
8    os::task::thread::WasiMemoryLayout,
9    runtime::{
10        TaintReason,
11        task_manager::{TaskWasm, TaskWasmRunProperties},
12    },
13    syscalls::*,
14};
15
16use wasmer::Memory;
17use wasmer_wasix_types::wasi::ThreadStart;
18
19#[instrument(level = "trace", skip_all, ret)]
33pub fn thread_spawn_v2<M: MemorySize>(
34    mut ctx: FunctionEnvMut<'_, WasiEnv>,
35    start_ptr: WasmPtr<ThreadStart<M>, M>,
36    ret_tid: WasmPtr<Tid, M>,
37) -> Result<Errno, WasiError> {
38    WasiEnv::do_pending_operations(&mut ctx)?;
39
40    let tid = wasi_try_ok!(thread_spawn_internal_from_wasi(&mut ctx, start_ptr));
42
43    let memory = unsafe { ctx.data().memory_view(&ctx) };
45    wasi_try_mem_ok!(ret_tid.write(&memory, tid));
46
47    tracing::debug!(
48        tid,
49        from_tid = ctx.data().thread.id().raw(),
50        "spawned new thread"
51    );
52
53    Ok(Errno::Success)
54}
55
56pub fn thread_spawn_internal_from_wasi<M: MemorySize>(
57    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
58    start_ptr: WasmPtr<ThreadStart<M>, M>,
59) -> Result<Tid, Errno> {
60    let env = ctx.data();
62    let memory = unsafe { env.memory_view(&ctx) };
63    let runtime = env.runtime.clone();
64    let tasks = env.tasks().clone();
65    let start_ptr_offset = start_ptr.offset();
66
67    let layout = {
69        let start: ThreadStart<M> = start_ptr.read(&memory).map_err(mem_error_to_wasi)?;
70        let stack_upper: u64 = start.stack_upper.into();
71        let stack_size: u64 = start.stack_size.into();
72        let guard_size: u64 = start.guard_size.into();
73        let tls_base: u64 = start.tls_base.into();
74        let stack_lower = stack_upper - stack_size;
75
76        WasiMemoryLayout {
77            stack_upper,
78            stack_lower,
79            guard_size,
80            stack_size,
81            tls_base: Some(tls_base),
82        }
83    };
84    tracing::trace!(
85        from_tid = env.thread.id().raw(),
86        "thread_spawn with layout {:?}",
87        layout
88    );
89
90    let thread_start = ThreadStartType::ThreadSpawn {
92        start_ptr: start_ptr_offset.into(),
93    };
94    let mut thread_handle = match env.process.new_thread(layout.clone(), thread_start) {
95        Ok(h) => Arc::new(h),
96        Err(err) => {
97            error!(
98                stack_base = layout.stack_lower,
99                "failed to create thread handle",
100            );
101            return Err(Errno::Access);
103        }
104    };
105    let thread_id: Tid = thread_handle.id().into();
106    Span::current().record("tid", thread_id);
107
108    thread_spawn_internal_using_layout::<M>(ctx, thread_handle, layout, start_ptr_offset, None)?;
110
111    Ok(thread_id)
113}
114
115pub fn thread_spawn_internal_using_layout<M: MemorySize>(
116    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
117    thread_handle: Arc<WasiThreadHandle>,
118    layout: WasiMemoryLayout,
119    start_ptr_offset: M::Offset,
120    rewind_state: Option<(RewindState, RewindResultType)>,
121) -> Result<(), Errno> {
122    let func_env = ctx.as_ref();
124    let mut store = ctx.as_store_mut();
125    let env = func_env.as_ref(&store);
126    let tasks = env.tasks().clone();
127
128    let env_inner = env.inner();
129    let module_handles = env_inner.main_module_instance_handles();
130
131    let thread_memory = module_handles.memory_clone();
132    let linker = env_inner.linker().cloned();
133
134    let state = env.state.clone();
136    let mut thread_env = env.clone();
137    thread_env.thread = thread_handle.as_thread();
138    thread_env.layout = layout;
139
140    thread_env.enable_deep_sleep = if cfg!(feature = "js") {
144        false
145    } else {
146        unsafe { env.capable_of_deep_sleep() }
147    };
148
149    let mut execute_module = {
152        let thread_handle = thread_handle;
153        move |ctx: WasiFunctionEnv, mut store: Store| {
154            call_module::<M>(ctx, store, start_ptr_offset, thread_handle, rewind_state)
156        }
157    };
158
159    if module_handles.thread_spawn.is_none() {
162        warn!("thread failed - the program does not export a `wasi_thread_start` function");
163        return Err(Errno::Notcapable);
164    }
165    let thread_module = module_handles.module_clone();
166    let spawn_type = match linker {
167        Some(linker) => crate::runtime::SpawnType::NewLinkerInstanceGroup(linker, func_env, store),
168        None => crate::runtime::SpawnType::ShareMemory(thread_memory, store.as_store_ref()),
169    };
170
171    trace!("threading: spawning background thread");
173    let run = move |props: TaskWasmRunProperties| {
174        execute_module(props.ctx, props.store);
175    };
176
177    let mut task_wasm = TaskWasm::new(Box::new(run), thread_env, thread_module, false, false)
178        .with_memory(spawn_type);
179
180    tasks.task_wasm(task_wasm).map_err(Into::<Errno>::into)?;
181
182    Ok(())
184}
185
186fn call_module_internal<M: MemorySize>(
188    env: &WasiFunctionEnv,
189    store: &mut Store,
190    start_ptr_offset: M::Offset,
191) -> Result<(), DeepSleepWork> {
192    let spawn = env
197        .data(&store)
198        .inner()
199        .main_module_instance_handles()
200        .thread_spawn
201        .clone()
202        .unwrap();
203    let tid = env.data(&store).tid();
204    let thread_result = spawn.call(
205        store,
206        tid.raw().try_into().map_err(|_| Errno::Overflow).unwrap(),
207        start_ptr_offset
208            .try_into()
209            .map_err(|_| Errno::Overflow)
210            .unwrap(),
211    );
212    trace!("callback finished (ret={:?})", thread_result);
213
214    let exit_code = handle_thread_result(env, store, thread_result)?;
215
216    env.on_exit(store, exit_code);
218    Ok(())
219}
220
221fn handle_thread_result(
222    env: &WasiFunctionEnv,
223    store: &mut Store,
224    err: Result<(), RuntimeError>,
225) -> Result<Option<ExitCode>, DeepSleepWork> {
226    let tid = env.data(&store).tid();
227    let pid = env.data(&store).pid();
228    let Err(err) = err else {
229        trace!("thread exited cleanly without calling thread_exit");
230        return Ok(None);
231    };
232    match err.downcast::<WasiError>() {
233        Ok(WasiError::ThreadExit) => {
234            trace!("thread exited cleanly");
235            Ok(None)
236        }
237        Ok(WasiError::Exit(code)) => {
238            trace!(exit_code = ?code, "thread requested exit");
239            if !code.is_success() {
240                env.data(&store)
242                    .runtime
243                    .on_taint(TaintReason::NonZeroExitCode(code));
244            };
245            Ok(Some(code))
246        }
247        Ok(WasiError::DeepSleep(deep)) => {
248            trace!("entered a deep sleep");
249            Err(deep)
250        }
251        Ok(WasiError::UnknownWasiVersion) => {
252            eprintln!(
253                "Thread {tid} of process {pid} failed because it has an unknown wasix version"
254            );
255            env.data(&store)
256                .runtime
257                .on_taint(TaintReason::UnknownWasiVersion);
258            Ok(Some(ExitCode::from(129)))
259        }
260        Ok(WasiError::DlSymbolResolutionFailed(symbol)) => {
261            eprintln!("Thread {tid} of process {pid} failed to find required symbol: {symbol}");
262            env.data(&store)
263                .runtime
264                .on_taint(TaintReason::DlSymbolResolutionFailed(symbol.clone()));
265            Ok(Some(ExitCode::from(129)))
266        }
267        Err(err) => {
268            eprintln!("Thread {tid} of process {pid} failed with runtime error: {err}");
269            env.data(&store)
270                .runtime
271                .on_taint(TaintReason::RuntimeError(err));
272            Ok(Some(ExitCode::from(129)))
273        }
274    }
275}
276
277fn call_module<M: MemorySize>(
279    mut ctx: WasiFunctionEnv,
280    mut store: Store,
281    start_ptr_offset: M::Offset,
282    thread_handle: Arc<WasiThreadHandle>,
283    rewind_state: Option<(RewindState, RewindResultType)>,
284) {
285    let env = ctx.data(&store);
286    let tasks = env.tasks().clone();
287
288    if let Some((rewind_state, rewind_result)) = rewind_state {
290        let mut ctx = ctx.env.clone().into_mut(&mut store);
291        let res = rewind_ext::<M>(
292            &mut ctx,
293            Some(rewind_state.memory_stack),
294            rewind_state.rewind_stack,
295            rewind_state.store_data,
296            rewind_result,
297        );
298        if res != Errno::Success {
299            return;
300        }
301    }
302
303    let ret = call_module_internal::<M>(&ctx, &mut store, start_ptr_offset);
305
306    if let Err(deep) = ret {
308        let rewind = deep.rewind;
310        let respawn = {
311            let tasks = tasks.clone();
312            move |ctx, store, trigger_res| {
313                call_module::<M>(
315                    ctx,
316                    store,
317                    start_ptr_offset,
318                    thread_handle,
319                    Some((rewind, RewindResultType::RewindWithResult(trigger_res))),
320                );
321            }
322        };
323
324        unsafe {
326            tasks.resume_wasm_after_poller(Box::new(respawn), ctx, store, deep.trigger)
327        };
328        return;
329    };
330    drop(thread_handle);
332}