Skip to content

Commit 5877b03

Browse files
committed
Auto merge of #109432 - flba-eb:108594_forkspawn_exponential_backoff, r=workingjubilee
QNX Neutrino: exponential backoff when fork/spawn needs a retry Fixes #108594: When retrying, sleep with an exponential duration. When sleep duration is lower than minimum possible sleeping time, yield instead (this will not be often due to the exponential increase of duration). Minimum possible sleeping time is determined using `libc::clock_getres` but only when spawn/fork failed the first time in a request. This is cached using a LazyLock. CC `@gh-tr` r? `@workingjubilee` `@rustbot` label +O-neutrino
2 parents 98c267e + a97184c commit 5877b03

File tree

1 file changed

+62
-14
lines changed

1 file changed

+62
-14
lines changed

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

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,26 @@ cfg_if::cfg_if! {
3535
if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
3636
use crate::thread;
3737
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
38-
// arbitrary number of tries:
39-
const MAX_FORKSPAWN_TRIES: u32 = 4;
38+
use crate::time::Duration;
39+
use crate::sync::LazyLock;
40+
// Get smallest amount of time we can sleep.
41+
// Return a common value if it cannot be determined.
42+
fn get_clock_resolution() -> Duration {
43+
static MIN_DELAY: LazyLock<Duration, fn() -> Duration> = LazyLock::new(|| {
44+
let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
45+
if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0
46+
{
47+
Duration::from_nanos(mindelay.tv_nsec as u64)
48+
} else {
49+
Duration::from_millis(1)
50+
}
51+
});
52+
*MIN_DELAY
53+
}
54+
// Arbitrary minimum sleep duration for retrying fork/spawn
55+
const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
56+
// Maximum duration of sleeping before giving up and returning an error
57+
const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000);
4058
}
4159
}
4260

@@ -163,12 +181,25 @@ impl Command {
163181
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
164182
use crate::sys::os::errno;
165183

166-
let mut tries_left = MAX_FORKSPAWN_TRIES;
184+
let mut delay = MIN_FORKSPAWN_SLEEP;
185+
167186
loop {
168187
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;
188+
if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF {
189+
if delay < get_clock_resolution() {
190+
// We cannot sleep this short (it would be longer).
191+
// Yield instead.
192+
thread::yield_now();
193+
} else if delay < MAX_FORKSPAWN_SLEEP {
194+
thread::sleep(delay);
195+
} else {
196+
return Err(io::const_io_error!(
197+
ErrorKind::WouldBlock,
198+
"forking returned EBADF too often",
199+
));
200+
}
201+
delay *= 2;
202+
continue;
172203
} else {
173204
return cvt(r).map(|res| (res, -1));
174205
}
@@ -480,17 +511,28 @@ impl Command {
480511
attrp: *const posix_spawnattr_t,
481512
argv: *const *mut c_char,
482513
envp: *const *mut c_char,
483-
) -> i32 {
484-
let mut tries_left = MAX_FORKSPAWN_TRIES;
514+
) -> io::Result<i32> {
515+
let mut delay = MIN_FORKSPAWN_SLEEP;
485516
loop {
486517
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;
518+
libc::EBADF => {
519+
if delay < get_clock_resolution() {
520+
// We cannot sleep this short (it would be longer).
521+
// Yield instead.
522+
thread::yield_now();
523+
} else if delay < MAX_FORKSPAWN_SLEEP {
524+
thread::sleep(delay);
525+
} else {
526+
return Err(io::const_io_error!(
527+
ErrorKind::WouldBlock,
528+
"posix_spawnp returned EBADF too often",
529+
));
530+
}
531+
delay *= 2;
490532
continue;
491533
}
492534
r => {
493-
return r;
535+
return Ok(r);
494536
}
495537
}
496538
}
@@ -620,14 +662,20 @@ impl Command {
620662
let spawn_fn = libc::posix_spawnp;
621663
#[cfg(target_os = "nto")]
622664
let spawn_fn = retrying_libc_posix_spawnp;
623-
cvt_nz(spawn_fn(
665+
666+
let spawn_res = spawn_fn(
624667
&mut p.pid,
625668
self.get_program_cstr().as_ptr(),
626669
file_actions.0.as_ptr(),
627670
attrs.0.as_ptr(),
628671
self.get_argv().as_ptr() as *const _,
629672
envp as *const _,
630-
))?;
673+
);
674+
675+
#[cfg(target_os = "nto")]
676+
let spawn_res = spawn_res?;
677+
678+
cvt_nz(spawn_res)?;
631679
Ok(Some(p))
632680
}
633681
}

0 commit comments

Comments
 (0)