use pidfd_spawn for faster process creation when pidfds are requested · rust-lang/rust@6687a3f (original) (raw)
`@@ -449,17 +449,70 @@ impl Command {
`
449
449
`use crate::mem::MaybeUninit;
`
450
450
`use crate::sys::weak::weak;
`
451
451
`use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used};
`
``
452
`+
#[cfg(target_os = "linux")]
`
``
453
`+
use core::sync::atomic::{AtomicU8, Ordering};
`
452
454
``
453
455
`if self.get_gid().is_some()
`
454
456
` || self.get_uid().is_some()
`
455
457
` || (self.env_saw_path() && !self.program_is_path())
`
456
458
` || !self.get_closures().is_empty()
`
457
459
` || self.get_groups().is_some()
`
458
``
`-
|| self.get_create_pidfd()
`
459
460
`{
`
460
461
`return Ok(None);
`
461
462
`}
`
462
463
``
``
464
`+
cfg_if::cfg_if! {
`
``
465
`+
if #[cfg(target_os = "linux")] {
`
``
466
`+
weak! {
`
``
467
`+
fn pidfd_spawnp(
`
``
468
`+
*mut libc::c_int,
`
``
469
`+
*const libc::c_char,
`
``
470
`+
*const libc::posix_spawn_file_actions_t,
`
``
471
`+
*const libc::posix_spawnattr_t,
`
``
472
`+
*const *mut libc::c_char,
`
``
473
`+
*const *mut libc::c_char
`
``
474
`+
) -> libc::c_int
`
``
475
`+
}
`
``
476
+
``
477
`+
weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int }
`
``
478
+
``
479
`+
static PIDFD_SPAWN_SUPPORTED: AtomicU8 = AtomicU8::new(0);
`
``
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;
`
``
485
+
``
486
`+
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
`+
return Ok(None);
`
``
490
`+
}
`
``
491
`+
if flag == UNKNOWN {
`
``
492
`+
let mut support = NO;
`
``
493
`+
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
`+
};
`
``
502
`+
}
`
``
503
`+
PIDFD_SPAWN_SUPPORTED.store(support, Ordering::Relaxed);
`
``
504
`+
if support != YES {
`
``
505
`+
return Ok(None);
`
``
506
`+
}
`
``
507
`+
}
`
``
508
`+
}
`
``
509
`+
} else {
`
``
510
`+
if self.get_create_pidfd() {
`
``
511
`+
unreachable!("only implemented on linux")
`
``
512
`+
}
`
``
513
`+
}
`
``
514
`+
}
`
``
515
+
463
516
`// Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
`
464
517
`#[cfg(all(target_os = "linux", target_env = "gnu"))]
`
465
518
`{
`
`@@ -543,9 +596,6 @@ impl Command {
`
543
596
``
544
597
`let pgroup = self.get_pgroup();
`
545
598
``
546
``
`-
// Safety: -1 indicates we don't have a pidfd.
`
547
``
`-
let mut p = unsafe { Process::new(0, -1) };
`
548
``
-
549
599
`struct PosixSpawnFileActions<'a>(&'a mut MaybeUninitlibc::posix_spawn_file_actions_t);
`
550
600
``
551
601
`impl Drop for PosixSpawnFileActions<'_> {
`
`@@ -640,6 +690,47 @@ impl Command {
`
640
690
`#[cfg(target_os = "nto")]
`
641
691
`let spawn_fn = retrying_libc_posix_spawnp;
`
642
692
``
``
693
`+
#[cfg(target_os = "linux")]
`
``
694
`+
if self.get_create_pidfd() {
`
``
695
`+
let mut pidfd: libc::c_int = -1;
`
``
696
`+
let spawn_res = pidfd_spawnp.get().unwrap()(
`
``
697
`+
&mut pidfd,
`
``
698
`+
self.get_program_cstr().as_ptr(),
`
``
699
`+
file_actions.0.as_ptr(),
`
``
700
`+
attrs.0.as_ptr(),
`
``
701
`+
self.get_argv().as_ptr() as *const _,
`
``
702
`+
envp as *const _,
`
``
703
`+
);
`
``
704
+
``
705
`+
let spawn_res = cvt_nz(spawn_res);
`
``
706
`+
if let Err(ref e) = spawn_res
`
``
707
`+
&& e.raw_os_error() == Some(libc::ENOSYS)
`
``
708
`+
{
`
``
709
`+
PIDFD_SPAWN_SUPPORTED.store(NO, Ordering::Relaxed);
`
``
710
`+
return Ok(None);
`
``
711
`+
}
`
``
712
`+
spawn_res?;
`
``
713
+
``
714
`+
let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) {
`
``
715
`+
Ok(pid) => pid,
`
``
716
`+
Err(e) => {
`
``
717
`+
// The child has been spawned and we are holding its pidfd.
`
``
718
`+
// But we cannot obtain its pid even though pidfd_getpid support was verified earlier.
`
``
719
`+
// This might happen if libc can't open procfs because the file descriptor limit has been reached.
`
``
720
`+
libc::close(pidfd);
`
``
721
`+
return Err(Error::new(
`
``
722
`+
e.kind(),
`
``
723
`+
"pidfd_spawnp succeeded but the child's PID could not be obtained",
`
``
724
`+
));
`
``
725
`+
}
`
``
726
`+
};
`
``
727
+
``
728
`+
return Ok(Some(Process::new(pid, pidfd)));
`
``
729
`+
}
`
``
730
+
``
731
`+
// Safety: -1 indicates we don't have a pidfd.
`
``
732
`+
let mut p = Process::new(0, -1);
`
``
733
+
643
734
`let spawn_res = spawn_fn(
`
644
735
`&mut p.pid,
`
645
736
`self.get_program_cstr().as_ptr(),
`