Skip to content

Commit 3e4e31b

Browse files
committed
more fine-grained feature-detection for pidfd spawning
we now distinguish between pidfd_spawn support, pidfd-via-fork/exec and not-supported
1 parent 0ce3619 commit 3e4e31b

1 file changed

Lines changed: 33 additions & 21 deletions

File tree

‎library/std/src/sys/pal/unix/process/process_unix.rs‎

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -476,35 +476,47 @@ impl Command {
476476

477477
weak! {fn pidfd_getpid(libc::c_int) -> libc::c_int }
478478

479-
staticPIDFD_SPAWN_SUPPORTED:AtomicU8 = AtomicU8::new(0);
479+
staticPIDFD_SUPPORTED:AtomicU8 = AtomicU8::new(0);
480480
constUNKNOWN:u8 = 0;
481-
constYES:u8 = 1;
482-
// NO currently forces a fallback to fork/exec. We could be more nuanced here and keep using spawn
483-
// if we know pidfd's aren't supported at all and the fallback would be futile.
484-
constNO:u8 = 2;
481+
constSPAWN:u8 = 1;
482+
// Obtaining a pidfd via the fork+exec path might work
483+
constFORK_EXEC:u8 = 2;
484+
// Neither pidfd_spawn nor fork/exec will get us a pidfd.
485+
// Instead we'll just posix_spawn if the other preconditions are met.
486+
constNO:u8 = 3;
485487

486488
ifself.get_create_pidfd(){
487-
letflag = PIDFD_SPAWN_SUPPORTED.load(Ordering::Relaxed);
488-
ifflag == NO || pidfd_spawnp.get().is_none() || pidfd_getpid.get().is_none(){
489+
letmut support = PIDFD_SUPPORTED.load(Ordering::Relaxed);
490+
ifsupport == FORK_EXEC{
489491
returnOk(None);
490492
}
491-
ifflag == UNKNOWN{
492-
letmutsupport = NO;
493+
ifsupport == UNKNOWN{
494+
support = NO;
493495
let our_pid = crate::process::id();
494-
let pidfd =
495-
unsafe{ libc::syscall(libc::SYS_pidfd_open, our_pid,0)}as libc::c_int;
496-
if pidfd >= 0{
497-
let pid = unsafe{ pidfd_getpid.get().unwrap()(pidfd)}asu32;
498-
unsafe{ libc::close(pidfd)};
499-
if pid == our_pid {
500-
support = YES
501-
};
496+
let pidfd = cvt(unsafe{ libc::syscall(libc::SYS_pidfd_open, our_pid,0)}as c_int);
497+
match pidfd {
498+
Ok(pidfd) => {
499+
support = FORK_EXEC;
500+
ifletSome(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe{ f(pidfd)}asi32)){
501+
if pidfd_spawnp.get().is_some() && pid asu32 == our_pid {
502+
support = SPAWN
503+
}
504+
}
505+
unsafe{ libc::close(pidfd)};
506+
}
507+
Err(e)if e.raw_os_error() == Some(libc::EMFILE) => {
508+
// We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail
509+
// Don't update the support flag so we can probe again later.
510+
returnErr(e)
511+
}
512+
_ => {}
502513
}
503-
PIDFD_SPAWN_SUPPORTED.store(support,Ordering::Relaxed);
504-
if support != YES{
514+
PIDFD_SUPPORTED.store(support,Ordering::Relaxed);
515+
if support == FORK_EXEC{
505516
returnOk(None);
506517
}
507518
}
519+
core::assert_matches::debug_assert_matches!(support,SPAWN | NO);
508520
}
509521
} else {
510522
ifself.get_create_pidfd(){
@@ -691,7 +703,7 @@ impl Command {
691703
let spawn_fn = retrying_libc_posix_spawnp;
692704

693705
#[cfg(target_os = "linux")]
694-
ifself.get_create_pidfd(){
706+
ifself.get_create_pidfd()&& PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN{
695707
letmut pidfd: libc::c_int = -1;
696708
let spawn_res = pidfd_spawnp.get().unwrap()(
697709
&mut pidfd,
@@ -706,7 +718,7 @@ impl Command {
706718
ifletErr(ref e) = spawn_res
707719
&& e.raw_os_error() == Some(libc::ENOSYS)
708720
{
709-
PIDFD_SPAWN_SUPPORTED.store(NO,Ordering::Relaxed);
721+
PIDFD_SUPPORTED.store(FORK_EXEC,Ordering::Relaxed);
710722
returnOk(None);
711723
}
712724
spawn_res?;

0 commit comments

Comments
 (0)