Skip to content

Commit bdb475c

Browse files
committed
Retry to fork/spawn with exponential backoff
1 parent 0939ec1 commit bdb475c

File tree

1 file changed

+44
-10
lines changed

1 file changed

+44
-10
lines changed

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

+44-10
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,20 @@ 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+
// Get smallest amount of time we can sleep.
40+
// Return a common value if it cannot be determined.
41+
fn get_clock_resolution() -> Duration {
42+
let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
43+
if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0
44+
{
45+
Duration::from_nanos(mindelay.tv_nsec as u64)
46+
} else {
47+
Duration::from_millis(1)
48+
}
49+
}
50+
// Arbitrary minimum sleep duration for retrying fork/spawn
51+
const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
4052
}
4153
}
4254

@@ -163,12 +175,24 @@ impl Command {
163175
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
164176
use crate::sys::os::errno;
165177

166-
let mut tries_left = MAX_FORKSPAWN_TRIES;
178+
let mut minimum_delay = None;
179+
let mut delay = MIN_FORKSPAWN_SLEEP;
180+
167181
loop {
168182
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;
183+
if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF {
184+
if minimum_delay.is_none() {
185+
minimum_delay = Some(get_clock_resolution());
186+
}
187+
if delay < minimum_delay.unwrap() {
188+
// We cannot sleep this short (it would be longer).
189+
// Yield instead.
190+
thread::yield_now();
191+
} else {
192+
thread::sleep(delay);
193+
}
194+
delay *= 2;
195+
continue;
172196
} else {
173197
return cvt(r).map(|res| (res, -1));
174198
}
@@ -481,12 +505,22 @@ impl Command {
481505
argv: *const *mut c_char,
482506
envp: *const *mut c_char,
483507
) -> i32 {
484-
let mut tries_left = MAX_FORKSPAWN_TRIES;
508+
let mut minimum_delay = None;
509+
let mut delay = MIN_FORKSPAWN_SLEEP;
485510
loop {
486511
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;
512+
libc::EBADF => {
513+
if minimum_delay.is_none() {
514+
minimum_delay = Some(get_clock_resolution());
515+
}
516+
if delay < minimum_delay.unwrap() {
517+
// We cannot sleep this short (it would be longer).
518+
// Yield instead.
519+
thread::yield_now();
520+
} else {
521+
thread::sleep(delay);
522+
}
523+
delay *= 2;
490524
continue;
491525
}
492526
r => {

0 commit comments

Comments
 (0)