Only determine clock res once; give up before sleeping more than 1 second

This commit is contained in:
Florian Bartels 2023-06-02 17:52:14 +02:00
parent d8f21101ec
commit 716cc5ac93

View File

@ -36,19 +36,25 @@ cfg_if::cfg_if! {
use crate::thread;
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
use crate::time::Duration;
use crate::sync::LazyLock;
// Get smallest amount of time we can sleep.
// Return a common value if it cannot be determined.
fn get_clock_resolution() -> Duration {
let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0
{
Duration::from_nanos(mindelay.tv_nsec as u64)
} else {
Duration::from_millis(1)
}
static MIN_DELAY: LazyLock<Duration, fn() -> Duration> = LazyLock::new(|| {
let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0
{
Duration::from_nanos(mindelay.tv_nsec as u64)
} else {
Duration::from_millis(1)
}
});
*MIN_DELAY
}
// Arbitrary minimum sleep duration for retrying fork/spawn
const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
// Maximum duration of sleeping before giving up and returning an error
const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000);
}
}
@ -175,21 +181,22 @@ impl Command {
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
use crate::sys::os::errno;
let mut minimum_delay = None;
let mut delay = MIN_FORKSPAWN_SLEEP;
loop {
let r = libc::fork();
if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF {
if minimum_delay.is_none() {
minimum_delay = Some(get_clock_resolution());
}
if delay < minimum_delay.unwrap() {
if delay < get_clock_resolution() {
// We cannot sleep this short (it would be longer).
// Yield instead.
thread::yield_now();
} else {
} else if delay < MAX_FORKSPAWN_SLEEP {
thread::sleep(delay);
} else {
return Err(io::const_io_error!(
ErrorKind::WouldBlock,
"forking returned EBADF too often",
));
}
delay *= 2;
continue;
@ -504,27 +511,28 @@ impl Command {
attrp: *const posix_spawnattr_t,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> i32 {
let mut minimum_delay = None;
) -> io::Result<i32> {
let mut delay = MIN_FORKSPAWN_SLEEP;
loop {
match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
libc::EBADF => {
if minimum_delay.is_none() {
minimum_delay = Some(get_clock_resolution());
}
if delay < minimum_delay.unwrap() {
if delay < get_clock_resolution() {
// We cannot sleep this short (it would be longer).
// Yield instead.
thread::yield_now();
} else {
} else if delay < MAX_FORKSPAWN_SLEEP {
thread::sleep(delay);
} else {
return Err(io::const_io_error!(
ErrorKind::WouldBlock,
"posix_spawnp returned EBADF too often",
));
}
delay *= 2;
continue;
}
r => {
return r;
return Ok(r);
}
}
}
@ -654,14 +662,20 @@ impl Command {
let spawn_fn = libc::posix_spawnp;
#[cfg(target_os = "nto")]
let spawn_fn = retrying_libc_posix_spawnp;
cvt_nz(spawn_fn(
let spawn_res = spawn_fn(
&mut p.pid,
self.get_program_cstr().as_ptr(),
file_actions.0.as_ptr(),
attrs.0.as_ptr(),
self.get_argv().as_ptr() as *const _,
envp as *const _,
))?;
);
#[cfg(target_os = "nto")]
let spawn_res = spawn_res?;
cvt_nz(spawn_res)?;
Ok(Some(p))
}
}