Skip to content

Commit e1d66b5

Browse files
authored
Make runtime::fork return the PID in both the parent and child. (#924)
Use `CLONE_CHILD_SETTID` so that `runtime::fork` can return the PID in both the parent and child.
1 parent 31dfad1 commit e1d66b5

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

src/backend/linux_raw/c.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ pub(crate) const EXIT_SUCCESS: c_int = 0;
142142
pub(crate) const EXIT_FAILURE: c_int = 1;
143143
#[cfg(feature = "process")]
144144
pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + linux_raw_sys::general::SIGABRT as c_int;
145+
#[cfg(feature = "runtime")]
146+
pub(crate) const CLONE_CHILD_SETTID: c_int = linux_raw_sys::general::CLONE_CHILD_SETTID as c_int;
145147

146148
#[cfg(feature = "process")]
147149
pub(crate) use linux_raw_sys::{

src/backend/linux_raw/runtime/syscalls.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use crate::ffi::CStr;
1818
#[cfg(feature = "fs")]
1919
use crate::fs::AtFlags;
2020
use crate::io;
21-
use crate::pid::Pid;
22-
use crate::runtime::{How, Sigaction, Siginfo, Sigset, Stack};
21+
use crate::pid::{Pid, RawPid};
22+
use crate::runtime::{Fork, How, Sigaction, Siginfo, Sigset, Stack};
2323
use crate::signal::Signal;
2424
use crate::timespec::Timespec;
2525
use crate::utils::option_as_ptr;
@@ -33,16 +33,48 @@ use linux_raw_sys::prctl::PR_SET_NAME;
3333
use {crate::backend::conv::ret_infallible, linux_raw_sys::general::ARCH_SET_FS};
3434

3535
#[inline]
36-
pub(crate) unsafe fn fork() -> io::Result<Option<Pid>> {
36+
pub(crate) unsafe fn fork() -> io::Result<Fork> {
37+
let mut child_pid = MaybeUninit::<RawPid>::uninit();
38+
39+
// Unix `fork` only returns the child PID in the parent; we'd like it in
40+
// the child too, so set `CLONE_CHILD_SETTID` and pass in the address of
41+
// a memory location to store it to in the child.
42+
//
43+
// Architectures differ on the order of the parameters.
44+
#[cfg(target_arch = "x86_64")]
3745
let pid = ret_c_int(syscall_readonly!(
3846
__NR_clone,
39-
c_int(c::SIGCHLD),
40-
zero(),
47+
c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID),
4148
zero(),
4249
zero(),
50+
&mut child_pid,
4351
zero()
4452
))?;
45-
Ok(Pid::from_raw(pid))
53+
#[cfg(any(
54+
target_arch = "aarch64",
55+
target_arch = "arm",
56+
target_arch = "mips",
57+
target_arch = "mips32r6",
58+
target_arch = "mips64",
59+
target_arch = "mips64r6",
60+
target_arch = "powerpc64",
61+
target_arch = "riscv64",
62+
target_arch = "x86"
63+
))]
64+
let pid = ret_c_int(syscall_readonly!(
65+
__NR_clone,
66+
c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID),
67+
zero(),
68+
zero(),
69+
zero(),
70+
&mut child_pid
71+
))?;
72+
73+
Ok(if let Some(pid) = Pid::from_raw(pid) {
74+
Fork::Parent(pid)
75+
} else {
76+
Fork::Child(Pid::from_raw_unchecked(child_pid.assume_init()))
77+
})
4678
}
4779

4880
#[cfg(feature = "fs")]

src/runtime.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,18 @@ pub use backend::runtime::tls::StartupTlsInfo;
315315
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
316316
/// [Linux]: https://man7.org/linux/man-pages/man2/fork.2.html
317317
/// [async-signal-safe]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
318-
pub unsafe fn fork() -> io::Result<Option<Pid>> {
318+
pub unsafe fn fork() -> io::Result<Fork> {
319319
backend::runtime::syscalls::fork()
320320
}
321321

322+
/// Regular Unix `fork` doesn't tell the child its own PID because it assumes
323+
/// the child can just do `getpid`. That's true, but it's more fun if it
324+
/// doesn't have to.
325+
pub enum Fork {
326+
Child(Pid),
327+
Parent(Pid),
328+
}
329+
322330
/// `execveat(dirfd, path.as_c_str(), argv, envp, flags)`—Execute a new
323331
/// command using the current process.
324332
///

0 commit comments

Comments
 (0)