use std::io::{Error, Result};
use std::os::unix::process::ExitStatusExt;
use std::process::ExitStatus;
pub fn ensure_single_threaded() -> Result<()> {
if unsafe { libc::unshare(libc::CLONE_VM) } == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
pub fn is_single_threaded() -> bool {
ensure_single_threaded().is_ok()
}
pub struct Child {
pid: libc::pid_t,
}
impl Child {
pub fn pid(&self) -> u32 {
self.pid as _
}
pub fn join(self) -> Result<ExitStatus> {
let mut status = 0;
let ret = unsafe { libc::waitpid(self.pid, &mut status, 0) };
if ret < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(ExitStatus::from_raw(status))
}
}
pub fn fork() -> Result<Option<Child>> {
ensure_single_threaded()?;
match unsafe { libc::fork() } {
-1 => Err(std::io::Error::last_os_error()),
0 => Ok(None),
pid => Ok(Some(Child { pid })),
}
}
pub fn fork_spawn(f: impl FnOnce() -> i32) -> Result<Child> {
Ok(match fork()? {
Some(c) => c,
None => {
std::process::exit(f());
}
})
}
pub fn fork_join(f: impl FnOnce() -> i32) -> Result<i32> {
let exit = fork_spawn(f)?.join()?;
Ok(exit
.code()
.or_else(|| exit.signal().map(|x| x + 128))
.unwrap_or(1))
}