From bdb475cf6c5712e706146635c4dd8bf6b2e506ff Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Sat, 18 Mar 2023 18:45:51 +0100 Subject: [PATCH 1/3] Retry to fork/spawn with exponential backoff --- .../std/src/sys/unix/process/process_unix.rs | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 612d43fe204..f68f2541902 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -35,8 +35,20 @@ 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; + use crate::time::Duration; + // 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) + } + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); } } @@ -163,12 +175,24 @@ impl Command { 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; + let mut minimum_delay = None; + let mut delay = MIN_FORKSPAWN_SLEEP; + 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; + 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() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else { + thread::sleep(delay); + } + delay *= 2; + continue; } else { return cvt(r).map(|res| (res, -1)); } @@ -481,12 +505,22 @@ impl Command { argv: *const *mut c_char, envp: *const *mut c_char, ) -> i32 { - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut minimum_delay = None; + let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF if tries_left > 0 => { - thread::yield_now(); - tries_left -= 1; + libc::EBADF => { + if minimum_delay.is_none() { + minimum_delay = Some(get_clock_resolution()); + } + if delay < minimum_delay.unwrap() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else { + thread::sleep(delay); + } + delay *= 2; continue; } r => { From d8f21101ec383d33c68f1867faf2ba74b3dca1a8 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Sun, 19 Mar 2023 11:00:18 +0100 Subject: [PATCH 2/3] Remove "one thread in tests" limitation in nto-qnx.md --- src/doc/rustc/src/platform-support/nto-qnx.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 0d815c9b598..b376c4a84ac 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -164,18 +164,12 @@ export exclude_tests=' --exclude tests/run-make-fulldeps' env $build_env \ - ./x.py test -j 1 \ + ./x.py test \ $exclude_tests \ --stage 1 \ --target x86_64-pc-nto-qnx710 ``` -Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`. -See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html) -(error section) for more information. -This can be achieved by using the `-j 1` parameter in the `x.py` call. -This issue is being researched and we will try to allow parallelism in the future. - ## Building Rust programs Rust does not yet ship pre-compiled artifacts for this target. From 716cc5ac938ad0a5302fe924a7142c0cc1d7d515 Mon Sep 17 00:00:00 2001 From: Florian Bartels Date: Fri, 2 Jun 2023 17:52:14 +0200 Subject: [PATCH 3/3] Only determine clock res once; give up before sleeping more than 1 second --- .../std/src/sys/unix/process/process_unix.rs | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index f68f2541902..3721988b405 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -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> = 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 { 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)) } }