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 if let Some(memory) = ctx.data().process.lock().memory.clone() {
115 child_env.process.register_memory(memory);
116 }
117 child_env.swap_inner(ctx.data_mut());
118 std::mem::swap(ctx.data_mut(), &mut child_env);
119 let previous_vfork = ctx.data_mut().vfork.replace(WasiVFork {
120 asyncify: Some(WasiVForkAsyncify {
121 rewind_stack: rewind_stack.clone(),
122 store_data: store_data.clone(),
123 is_64bit: M::is_64bit(),
124 }),
125 env: Box::new(child_env),
126 handle: child_handle,
127 });
128 assert!(previous_vfork.is_none()); match rewind::<M, _>(
134 ctx,
135 Some(memory_stack.freeze()),
136 rewind_stack.freeze(),
137 store_data,
138 ForkResult {
139 pid: 0,
140 ret: Errno::Success,
141 },
142 ) {
143 Errno::Success => OnCalledAction::InvokeAgain,
144 err => {
145 warn!("failed - could not rewind the stack - errno={}", err);
146 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
147 }
148 }
149 });
150 }
151
152 let state = env.state.clone();
154 let bin_factory = env.bin_factory.clone();
155
156 let snapshot = capture_store_snapshot(&mut ctx.as_store_mut());
158 unwind::<M, _>(ctx, move |mut ctx, mut memory_stack, rewind_stack| {
159 let tasks = ctx.data().tasks().clone();
160 let span = debug_span!(
161 "unwind",
162 memory_stack_len = memory_stack.len(),
163 rewind_stack_len = rewind_stack.len()
164 );
165 let _span_guard = span.enter();
166 let memory_stack = memory_stack.freeze();
167 let rewind_stack = rewind_stack.freeze();
168
169 let store_data = snapshot.serialize().unwrap();
171 let store_data = Bytes::from(store_data);
172
173 let runtime = child_env.runtime.clone();
175 let tasks = child_env.tasks().clone();
176 let child_memory_stack = memory_stack.clone();
177 let child_rewind_stack = rewind_stack.clone();
178
179 let env_inner = ctx.data().inner();
180 let instance_handles = env_inner.static_module_instance_handles().unwrap();
181 let module = instance_handles.module_clone();
182 let memory = instance_handles.memory_clone();
183 let spawn_type = SpawnType::AttachMemory(match memory.copy(&ctx.as_store_ref()) {
184 Ok(shared) => shared,
185 Err(e) => return OnCalledAction::Trap(Box::new(e)),
186 });
187
188 let signaler = Box::new(child_env.process.clone());
190 {
191 let runtime = runtime.clone();
192 let tasks = tasks.clone();
193 let tasks_outer = tasks.clone();
194 let store_data = store_data.clone();
195
196 let run = move |mut props: TaskWasmRunProperties| {
197 let ctx = props.ctx;
198 let mut store = props.store;
199
200 {
202 trace!("rewinding child");
203 let mut ctx = ctx.env.clone().into_mut(&mut store);
204 let (data, mut store) = ctx.data_and_store_mut();
205 match rewind::<M, _>(
206 ctx,
207 Some(child_memory_stack),
208 child_rewind_stack,
209 store_data.clone(),
210 ForkResult {
211 pid: 0,
212 ret: Errno::Success,
213 },
214 ) {
215 Errno::Success => OnCalledAction::InvokeAgain,
216 err => {
217 warn!(
218 "wasm rewind failed - could not rewind the stack - errno={}",
219 err
220 );
221 return;
222 }
223 };
224 }
225
226 run::<M>(ctx, store, child_handle, None);
228 };
229
230 tasks_outer
231 .task_wasm(
232 TaskWasm::new(Box::new(run), child_env, module, false, false)
233 .with_globals(snapshot)
234 .with_memory(spawn_type),
235 )
236 .map_err(|err| {
237 warn!(
238 "failed to fork as the process could not be spawned - {}",
239 err
240 );
241 err
242 })
243 .ok();
244 };
245
246 match rewind::<M, _>(
248 ctx,
249 Some(memory_stack),
250 rewind_stack,
251 store_data,
252 ForkResult {
253 pid: child_pid.raw() as Pid,
254 ret: Errno::Success,
255 },
256 ) {
257 Errno::Success => OnCalledAction::InvokeAgain,
258 err => {
259 warn!("failed - could not rewind the stack - errno={}", err);
260 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
261 }
262 }
263 })
264}
265
266fn run<M: MemorySize>(
267 ctx: WasiFunctionEnv,
268 mut store: Store,
269 child_handle: WasiThreadHandle,
270 rewind_state: Option<(RewindState, RewindResultType)>,
271) -> ExitCode {
272 let env = ctx.data(&store);
273 let tasks = env.tasks().clone();
274 let pid = env.pid();
275 let tid = env.tid();
276
277 if let Some((rewind_state, rewind_result)) = rewind_state {
279 let mut ctx = ctx.env.clone().into_mut(&mut store);
280 let res = rewind_ext::<M>(
281 &mut ctx,
282 Some(rewind_state.memory_stack),
283 rewind_state.rewind_stack,
284 rewind_state.store_data,
285 rewind_result,
286 );
287 if res != Errno::Success {
288 return res.into();
289 }
290 }
291
292 let mut ret: ExitCode = Errno::Success.into();
293 let (mut store, err) = if ctx.data(&store).thread.is_main() {
294 trace!(%pid, %tid, "re-invoking main");
295 let start = ctx
296 .data(&store)
297 .inner()
298 .static_module_instance_handles()
299 .unwrap()
300 .start
301 .clone()
302 .unwrap();
303 ContextSwitchingEnvironment::run_main_context(&ctx, store, start.into(), vec![])
304 } else {
305 trace!(%pid, %tid, "re-invoking thread_spawn");
306 let start = ctx
307 .data(&store)
308 .inner()
309 .static_module_instance_handles()
310 .unwrap()
311 .thread_spawn
312 .clone()
313 .unwrap();
314 let params = vec![0i32.into(), 0i32.into()];
315 ContextSwitchingEnvironment::run_main_context(&ctx, store, start.into(), params)
316 };
317 if let Err(err) = err {
318 match err.downcast::<WasiError>() {
319 Ok(WasiError::Exit(exit_code)) => {
320 ret = exit_code;
321 }
322 Ok(WasiError::DeepSleep(deep)) => {
323 trace!(%pid, %tid, "entered a deep sleep");
324
325 let respawn = {
327 let tasks = tasks.clone();
328 let rewind_state = deep.rewind;
329 move |ctx, store, rewind_result| {
330 run::<M>(
331 ctx,
332 store,
333 child_handle,
334 Some((
335 rewind_state,
336 RewindResultType::RewindWithResult(rewind_result),
337 )),
338 );
339 }
340 };
341
342 unsafe {
344 tasks.resume_wasm_after_poller(Box::new(respawn), ctx, store, deep.trigger)
345 };
346 return Errno::Success.into();
347 }
348 _ => {}
349 }
350 }
351 trace!(%pid, %tid, "child exited (code = {})", ret);
352
353 ctx.on_exit((&mut store), Some(ret));
355
356 drop(child_handle);
358 ret
359}