@@ -476,35 +476,47 @@ impl Command {
476476
477477 weak! { fn pidfd_getpid( libc:: c_int) -> libc:: c_int }
478478
479- static PIDFD_SPAWN_SUPPORTED : AtomicU8 = AtomicU8 :: new( 0 ) ;
479+ static PIDFD_SUPPORTED : AtomicU8 = AtomicU8 :: new( 0 ) ;
480480 const UNKNOWN : u8 = 0 ;
481- const YES : 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- const NO : u8 = 2 ;
481+ const SPAWN : u8 = 1 ;
482+ // Obtaining a pidfd via the fork+exec path might work
483+ const FORK_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+ const NO : u8 = 3 ;
485487
486488 if self . get_create_pidfd( ) {
487- let flag = PIDFD_SPAWN_SUPPORTED . load( Ordering :: Relaxed ) ;
488- if flag == NO || pidfd_spawnp . get ( ) . is_none ( ) || pidfd_getpid . get ( ) . is_none ( ) {
489+ let mut support = PIDFD_SUPPORTED . load( Ordering :: Relaxed ) ;
490+ if support == FORK_EXEC {
489491 return Ok ( None ) ;
490492 }
491- if flag == UNKNOWN {
492- let mut support = NO ;
493+ if support == 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) } as u32 ;
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+ if let Some ( Ok ( pid) ) = pidfd_getpid. get( ) . map( |f| cvt( unsafe { f( pidfd) } as i32 ) ) {
501+ if pidfd_spawnp. get( ) . is_some( ) && pid as u32 == 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+ return Err ( 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 return Ok ( None ) ;
506517 }
507518 }
519+ core:: assert_matches:: debug_assert_matches!( support, SPAWN | NO ) ;
508520 }
509521 } else {
510522 if self . get_create_pidfd( ) {
@@ -691,7 +703,7 @@ impl Command {
691703 let spawn_fn = retrying_libc_posix_spawnp;
692704
693705 #[ cfg( target_os = "linux" ) ]
694- if self . get_create_pidfd ( ) {
706+ if self . get_create_pidfd ( ) && PIDFD_SUPPORTED . load ( Ordering :: Relaxed ) == SPAWN {
695707 let mut pidfd: libc:: c_int = -1 ;
696708 let spawn_res = pidfd_spawnp. get ( ) . unwrap ( ) (
697709 & mut pidfd,
@@ -706,7 +718,7 @@ impl Command {
706718 if let Err ( 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 return Ok ( None ) ;
711723 }
712724 spawn_res?;
0 commit comments