1use std::io::{Error, Result};
2use std::os::unix::process::ExitStatusExt;
3use std::process::ExitStatus;
4
5pub fn ensure_single_threaded() -> Result<()> {
7 if unsafe { libc::unshare(libc::CLONE_VM) } == 0 {
9 Ok(())
10 } else {
11 Err(Error::last_os_error())
12 }
13}
14
15pub fn is_single_threaded() -> bool {
17 ensure_single_threaded().is_ok()
18}
19
20pub struct Child {
24 pid: libc::pid_t,
25}
26
27impl Child {
28 pub fn pid(&self) -> u32 {
30 self.pid as _
31 }
32
33 pub fn join(self) -> Result<ExitStatus> {
36 let mut status = 0;
38 let ret = unsafe { libc::waitpid(self.pid, &mut status, 0) };
39 if ret < 0 {
40 return Err(std::io::Error::last_os_error());
41 }
42 Ok(ExitStatus::from_raw(status))
43 }
44}
45
46pub fn fork() -> Result<Option<Child>> {
50 ensure_single_threaded()?;
51
52 match unsafe { libc::fork() } {
54 -1 => Err(std::io::Error::last_os_error()),
55 0 => Ok(None),
56 pid => Ok(Some(Child { pid })),
57 }
58}
59
60pub fn fork_spawn(f: impl FnOnce() -> i32) -> Result<Child> {
62 Ok(match fork()? {
63 Some(c) => c,
64 None => {
65 std::process::exit(f());
66 }
67 })
68}
69
70pub fn fork_join(f: impl FnOnce() -> i32) -> Result<i32> {
72 let exit = fork_spawn(f)?.join()?;
73 Ok(exit
74 .code()
75 .or_else(|| exit.signal().map(|x| x + 128))
76 .unwrap_or(1))
77}