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}