Skip to content

Commit cef9d4c

Browse files
committed
Retry to spawn/fork up to 3 times when it failed because of an interruption
1 parent f1a399c commit cef9d4c

File tree

1 file changed

+64
-2
lines changed

1 file changed

+64
-2
lines changed

library/std/src/sys/unix/process/process_unix.rs

+64-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ use libc::{c_int, pid_t};
3131
#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
3232
use libc::{gid_t, uid_t};
3333

34+
cfg_if::cfg_if! {
35+
if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
36+
use crate::thread;
37+
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
38+
// arbitrary number of tries:
39+
const MAX_FORKSPAWN_TRIES: u32 = 4;
40+
}
41+
}
42+
3443
////////////////////////////////////////////////////////////////////////////////
3544
// Command
3645
////////////////////////////////////////////////////////////////////////////////
@@ -141,11 +150,31 @@ impl Command {
141150

142151
// Attempts to fork the process. If successful, returns Ok((0, -1))
143152
// in the child, and Ok((child_pid, -1)) in the parent.
144-
#[cfg(not(target_os = "linux", target_os = "nto"))]
153+
#[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))]
145154
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
146155
cvt(libc::fork()).map(|res| (res, -1))
147156
}
148157

158+
// On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened
159+
// or closed a file descriptor while the fork() was occurring".
160+
// Documentation says "... or try calling fork() again". This is what we do here.
161+
// See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
162+
#[cfg(all(target_os = "nto", target_env = "nto71"))]
163+
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
164+
use crate::sys::os::errno;
165+
166+
let mut tries_left = MAX_FORKSPAWN_TRIES;
167+
loop {
168+
let r = libc::fork();
169+
if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF {
170+
thread::yield_now();
171+
tries_left -= 1;
172+
} else {
173+
return cvt(r).map(|res| (res, -1));
174+
}
175+
}
176+
}
177+
149178
// Attempts to fork the process. If successful, returns Ok((0, -1))
150179
// in the child, and Ok((child_pid, child_pidfd)) in the parent.
151180
#[cfg(target_os = "linux")]
@@ -439,6 +468,34 @@ impl Command {
439468
}
440469
}
441470

471+
// On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened
472+
// or closed a file descriptor while the posix_spawn() was occurring".
473+
// Documentation says "... or try calling posix_spawn() again". This is what we do here.
474+
// See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
475+
#[cfg(all(target_os = "nto", target_env = "nto71"))]
476+
unsafe fn retrying_libc_posix_spawnp(
477+
pid: *mut pid_t,
478+
file: *const c_char,
479+
file_actions: *const posix_spawn_file_actions_t,
480+
attrp: *const posix_spawnattr_t,
481+
argv: *const *mut c_char,
482+
envp: *const *mut c_char,
483+
) -> i32 {
484+
let mut tries_left = MAX_FORKSPAWN_TRIES;
485+
loop {
486+
match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
487+
libc::EBADF if tries_left > 0 => {
488+
thread::yield_now();
489+
tries_left -= 1;
490+
continue;
491+
}
492+
r => {
493+
return r;
494+
}
495+
}
496+
}
497+
}
498+
442499
// Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
443500
// and maybe others will gain this non-POSIX function too. We'll check
444501
// for this weak symbol as soon as it's needed, so we can return early
@@ -558,7 +615,12 @@ impl Command {
558615
// Make sure we synchronize access to the global `environ` resource
559616
let _env_lock = sys::os::env_read_lock();
560617
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
561-
cvt_nz(libc::posix_spawnp(
618+
619+
#[cfg(not(target_os = "nto"))]
620+
let spawn_fn = libc::posix_spawnp;
621+
#[cfg(target_os = "nto")]
622+
let spawn_fn = retrying_libc_posix_spawnp;
623+
cvt_nz(spawn_fn(
562624
&mut p.pid,
563625
self.get_program_cstr().as_ptr(),
564626
file_actions.0.as_ptr(),

0 commit comments

Comments
 (0)