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