Retry to spawn/fork up to 3 times when it failed because of an interruption

This commit is contained in:
Florian Bartels 2023-02-10 15:27:22 +01:00
parent f1a399cc40
commit cef9d4cbc1
No known key found for this signature in database

View File

@ -31,6 +31,15 @@ use libc::{c_int, pid_t};
#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
use libc::{gid_t, uid_t};
cfg_if::cfg_if! {
if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
use crate::thread;
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
// arbitrary number of tries:
const MAX_FORKSPAWN_TRIES: u32 = 4;
}
}
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
@ -141,11 +150,31 @@ impl Command {
// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, -1)) in the parent.
#[cfg(not(target_os = "linux", target_os = "nto"))]
#[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))]
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
cvt(libc::fork()).map(|res| (res, -1))
}
// On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened
// or closed a file descriptor while the fork() was occurring".
// Documentation says "... or try calling fork() again". This is what we do here.
// See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
#[cfg(all(target_os = "nto", target_env = "nto71"))]
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
use crate::sys::os::errno;
let mut tries_left = MAX_FORKSPAWN_TRIES;
loop {
let r = libc::fork();
if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF {
thread::yield_now();
tries_left -= 1;
} else {
return cvt(r).map(|res| (res, -1));
}
}
}
// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, child_pidfd)) in the parent.
#[cfg(target_os = "linux")]
@ -439,6 +468,34 @@ impl Command {
}
}
// On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened
// or closed a file descriptor while the posix_spawn() was occurring".
// Documentation says "... or try calling posix_spawn() again". This is what we do here.
// See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
#[cfg(all(target_os = "nto", target_env = "nto71"))]
unsafe fn retrying_libc_posix_spawnp(
pid: *mut pid_t,
file: *const c_char,
file_actions: *const posix_spawn_file_actions_t,
attrp: *const posix_spawnattr_t,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> i32 {
let mut tries_left = MAX_FORKSPAWN_TRIES;
loop {
match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
libc::EBADF if tries_left > 0 => {
thread::yield_now();
tries_left -= 1;
continue;
}
r => {
return r;
}
}
}
}
// Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
// and maybe others will gain this non-POSIX function too. We'll check
// for this weak symbol as soon as it's needed, so we can return early
@ -558,7 +615,12 @@ impl Command {
// Make sure we synchronize access to the global `environ` resource
let _env_lock = sys::os::env_read_lock();
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
cvt_nz(libc::posix_spawnp(
#[cfg(not(target_os = "nto"))]
let spawn_fn = libc::posix_spawnp;
#[cfg(target_os = "nto")]
let spawn_fn = retrying_libc_posix_spawnp;
cvt_nz(spawn_fn(
&mut p.pid,
self.get_program_cstr().as_ptr(),
file_actions.0.as_ptr(),