@@ -476,35 +476,47 @@ impl Command {
476
476
477
477
weak! { fn pidfd_getpid( libc:: c_int) -> libc:: c_int }
478
478
479
- static PIDFD_SPAWN_SUPPORTED : AtomicU8 = AtomicU8 :: new( 0 ) ;
479
+ static PIDFD_SUPPORTED : AtomicU8 = AtomicU8 :: new( 0 ) ;
480
480
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 ;
485
487
486
488
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 {
489
491
return Ok ( None ) ;
490
492
}
491
- if flag == UNKNOWN {
492
- let mut support = NO ;
493
+ if support == UNKNOWN {
494
+ support = NO ;
493
495
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
+ _ => { }
502
513
}
503
- PIDFD_SPAWN_SUPPORTED . store( support, Ordering :: Relaxed ) ;
504
- if support != YES {
514
+ PIDFD_SUPPORTED . store( support, Ordering :: Relaxed ) ;
515
+ if support == FORK_EXEC {
505
516
return Ok ( None ) ;
506
517
}
507
518
}
519
+ core:: assert_matches:: debug_assert_matches!( support, SPAWN | NO ) ;
508
520
}
509
521
} else {
510
522
if self . get_create_pidfd( ) {
@@ -691,7 +703,7 @@ impl Command {
691
703
let spawn_fn = retrying_libc_posix_spawnp;
692
704
693
705
#[ cfg( target_os = "linux" ) ]
694
- if self . get_create_pidfd ( ) {
706
+ if self . get_create_pidfd ( ) && PIDFD_SUPPORTED . load ( Ordering :: Relaxed ) == SPAWN {
695
707
let mut pidfd: libc:: c_int = -1 ;
696
708
let spawn_res = pidfd_spawnp. get ( ) . unwrap ( ) (
697
709
& mut pidfd,
@@ -706,7 +718,7 @@ impl Command {
706
718
if let Err ( ref e) = spawn_res
707
719
&& e. raw_os_error ( ) == Some ( libc:: ENOSYS )
708
720
{
709
- PIDFD_SPAWN_SUPPORTED . store ( NO , Ordering :: Relaxed ) ;
721
+ PIDFD_SUPPORTED . store ( FORK_EXEC , Ordering :: Relaxed ) ;
710
722
return Ok ( None ) ;
711
723
}
712
724
spawn_res?;
0 commit comments