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(),

`