wasmer_wasix/syscalls/wasix/
proc_fork.rs1use super::*;
2use crate::{
3 WasiThreadHandle, WasiVForkAsyncify, capture_store_snapshot,
4 os::task::OwnedTaskStatus,
5 runtime::task_manager::{TaskWasm, TaskWasmRunProperties},
6 state::context_switching::ContextSwitchingEnvironment,
7 syscalls::*,
8};
9use serde::{Deserialize, Serialize};
10use wasmer::Memory;
11
12#[derive(Serialize, Deserialize)]
13pub(crate) struct ForkResult {
14 pub pid: Pid,
15 pub ret: Errno,
16}
17
18#[instrument(level = "trace", skip_all, fields(pid = ctx.data().process.pid().raw()), ret)]
23pub fn proc_fork<M: MemorySize>(
24 mut ctx: FunctionEnvMut<'_, WasiEnv>,
25 mut copy_memory: Bool,
26 pid_ptr: WasmPtr<Pid, M>,
27) -> Result<Errno, WasiError> {
28 WasiEnv::do_pending_operations(&mut ctx)?;
29
30 wasi_try_ok!(ctx.data().ensure_static_module().map_err(|_| {
31 warn!("process forking not supported for dynamically linked modules");
32 Errno::Notsup
33 }));
34
35 if let Some(context_switching_environment) = ctx.data().context_switching_environment.as_ref()
36 && context_switching_environment.active_context_id()
37 != context_switching_environment.main_context_id()
38 {
39 warn!(
40 "process forking is only supported from the main context when using WASIX context-switching features"
41 );
42 return Ok(Errno::Notsup);
43 }
44
45 if let Some(result) = unsafe { handle_rewind::<M, ForkResult>(&mut ctx) } {
47 if result.pid == 0 {
48 trace!("handle_rewind - i am child (ret={})", result.ret);
49 } else {
50 trace!(
51 "handle_rewind - i am parent (child={}, ret={})",
52 result.pid, result.ret
53 );
54 }
55 let memory = unsafe { ctx.data().memory_view(&ctx) };
56 wasi_try_mem_ok!(pid_ptr.write(&memory, result.pid));
57 return Ok(result.ret);
58 }
59 trace!(%copy_memory, "capturing");
60
61 if let Some(vfork) = ctx.data().vfork.as_ref() {
62 warn!("process forking not supported in an active vfork");
63 return Ok(Errno::Notsup);
64 }
65
66 let (mut child_env, mut child_handle) = match ctx.data().fork() {
71 Ok(p) => p,
72 Err(err) => {
73 debug!("could not fork process: {err}");
74 return Ok(Errno::Perm);
76 }
77 };
78 let child_pid = child_env.process.pid();
79 let child_finished = child_env.process.finished.clone();
80
81 {
84 let mut inner = ctx.data().process.lock();
85 inner.children.push(child_env.process.clone());
86 }
87 let env = ctx.data();
88 let memory = unsafe { env.memory_view(&ctx) };
89
90 wasi_try_mem_ok!(pid_ptr.write(&memory, 0));
92 let pid = child_env.pid();
93 let tid = child_env.tid();
94
95 let pid_offset = pid_ptr.offset();
97
98 if copy_memory == Bool::False {
103 return unwind::<M, _>(ctx, move |mut ctx, mut memory_stack, rewind_stack| {
105 let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut())
107 .serialize()
108 .unwrap();
109 let store_data = Bytes::from(store_data);
110
111 child_env.swap_inner(ctx.data_mut());
115 std::mem::swap(ctx.data_mut(), &mut child_env);
116 let previous_vfork = ctx.data_mut().vfork.replace(WasiVFork {
117 asyncify: Some(WasiVForkAsyncify {
118 rewind_stack: rewind_stack.clone(),
119 store_data: store_data.clone(),
120 is_64bit: M::is_64bit(),
121 }),
122 env: Box::new(child_env),
123 handle: child_handle,
124 });
125 assert!(previous_vfork.is_none()); match rewind::<M, _>(
131 ctx,
132 Some(memory_stack.freeze()),
133 rewind_stack.freeze(),
134 store_data,
135 ForkResult {
136 pid: 0,
137 ret: Errno::Success,
138 },
139 ) {
140 Errno::Success => OnCalledAction::InvokeAgain,
141 err => {
142 warn!("failed - could not rewind the stack - errno={}", err);
143 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
144 }
145 }
146 });
147 }
148
149 let state = env.state.clone();
151 let bin_factory = env.bin_factory.clone();
152
153 let snapshot = capture_store_snapshot(&mut ctx.as_store_mut());
155 unwind::<M, _>(ctx, move |mut ctx, mut memory_stack, rewind_stack| {
156 let tasks = ctx.data().tasks().clone();
157 let span = debug_span!(
158 "unwind",
159 memory_stack_len = memory_stack.len(),
160 rewind_stack_len = rewind_stack.len()
161 );
162 let _span_guard = span.enter();
163 let memory_stack = memory_stack.freeze();
164 let rewind_stack = rewind_stack.freeze();
165
166 let store_data = snapshot.serialize().unwrap();
168 let store_data = Bytes::from(store_data);
169
170 let runtime = child_env.runtime.clone();
172 let tasks = child_env.tasks().clone();
173 let child_memory_stack = memory_stack.clone();
174 let child_rewind_stack = rewind_stack.clone();
175
176 let env_inner = ctx.data().inner();
177 let instance_handles = env_inner.static_module_instance_handles().unwrap();
178 let module = instance_handles.module_clone();
179 let memory = instance_handles.memory_clone();
180 let spawn_type = SpawnType::CopyMemory(memory, ctx.as_store_ref());
181
182 let signaler = Box::new(child_env.process.clone());
184 {
185 let runtime = runtime.clone();
186 let tasks = tasks.clone();
187 let tasks_outer = tasks.clone();
188 let store_data = store_data.clone();
189
190 let run = move |mut props: TaskWasmRunProperties| {
191 let ctx = props.ctx;
192 let mut store = props.store;
193
194 {
196 trace!("rewinding child");
197 let mut ctx = ctx.env.clone().into_mut(&mut store);
198 let (data, mut store) = ctx.data_and_store_mut();
199 match rewind::<M, _>(
200 ctx,
201 Some(child_memory_stack),
202 child_rewind_stack,
203 store_data.clone(),
204 ForkResult {
205 pid: 0,
206 ret: Errno::Success,
207 },
208 ) {
209 Errno::Success => OnCalledAction::InvokeAgain,
210 err => {
211 warn!(
212 "wasm rewind failed - could not rewind the stack - errno={}",
213 err
214 );
215 return;
216 }
217 };
218 }
219
220 run::<M>(ctx, store, child_handle, None);
222 };
223
224 tasks_outer
225 .task_wasm(
226 TaskWasm::new(Box::new(run), child_env, module, false, false)
227 .with_globals(snapshot)
228 .with_memory(spawn_type),
229 )
230 .map_err(|err| {
231 warn!(
232 "failed to fork as the process could not be spawned - {}",
233 err
234 );
235 err
236 })
237 .ok();
238 };
239
240 match rewind::<M, _>(
242 ctx,
243 Some(memory_stack),
244 rewind_stack,
245 store_data,
246 ForkResult {
247 pid: child_pid.raw() as Pid,
248 ret: Errno::Success,
249 },
250 ) {
251 Errno::Success => OnCalledAction::InvokeAgain,
252 err => {
253 warn!("failed - could not rewind the stack - errno={}", err);
254 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
255 }
256 }
257 })
258}
259
260fn run<M: MemorySize>(
261 ctx: WasiFunctionEnv,
262 mut store: Store,
263 child_handle: WasiThreadHandle,
264 rewind_state: Option<(RewindState, RewindResultType)>,
265) -> ExitCode {
266 let env = ctx.data(&store);
267 let tasks = env.tasks().clone();
268 let pid = env.pid();
269 let tid = env.tid();
270
271 if let Some((rewind_state, rewind_result)) = rewind_state {
273 let mut ctx = ctx.env.clone().into_mut(&mut store);
274 let res = rewind_ext::<M>(
275 &mut ctx,
276 Some(rewind_state.memory_stack),
277 rewind_state.rewind_stack,
278 rewind_state.store_data,
279 rewind_result,
280 );
281 if res != Errno::Success {
282 return res.into();
283 }
284 }
285
286 let mut ret: ExitCode = Errno::Success.into();
287 let (mut store, err) = if ctx.data(&store).thread.is_main() {
288 trace!(%pid, %tid, "re-invoking main");
289 let start = ctx
290 .data(&store)
291 .inner()
292 .static_module_instance_handles()
293 .unwrap()
294 .start
295 .clone()
296 .unwrap();
297 ContextSwitchingEnvironment::run_main_context(&ctx, store, start.into(), vec![])
298 } else {
299 trace!(%pid, %tid, "re-invoking thread_spawn");
300 let start = ctx
301 .data(&store)
302 .inner()
303 .static_module_instance_handles()
304 .unwrap()
305 .thread_spawn
306 .clone()
307 .unwrap();
308 let params = vec![0i32.into(), 0i32.into()];
309 ContextSwitchingEnvironment::run_main_context(&ctx, store, start.into(), params)
310 };
311 if let Err(err) = err {
312 match err.downcast::<WasiError>() {
313 Ok(WasiError::Exit(exit_code)) => {
314 ret = exit_code;
315 }
316 Ok(WasiError::DeepSleep(deep)) => {
317 trace!(%pid, %tid, "entered a deep sleep");
318
319 let respawn = {
321 let tasks = tasks.clone();
322 let rewind_state = deep.rewind;
323 move |ctx, store, rewind_result| {
324 run::<M>(
325 ctx,
326 store,
327 child_handle,
328 Some((
329 rewind_state,
330 RewindResultType::RewindWithResult(rewind_result),
331 )),
332 );
333 }
334 };
335
336 unsafe {
338 tasks.resume_wasm_after_poller(Box::new(respawn), ctx, store, deep.trigger)
339 };
340 return Errno::Success.into();
341 }
342 _ => {}
343 }
344 }
345 trace!(%pid, %tid, "child exited (code = {})", ret);
346
347 ctx.on_exit((&mut store), Some(ret));
349
350 drop(child_handle);
352 ret
353}