about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorFlorian Bartels <Florian.Bartels@elektrobit.com>2023-02-10 15:27:22 +0100
committerFlorian Bartels <Florian.Bartels@elektrobit.com>2023-02-28 15:59:53 +0100
commitcef9d4cbc1809bc29dee105fc8593d3e569155d7 (patch)
treeba13d2a7bce2238b0d82353fdd86caa4fde91860 /library/std/src/sys
parentf1a399cc40dce94a0dca7324ac0a8d8d2fedb506 (diff)
downloadrust-cef9d4cbc1809bc29dee105fc8593d3e569155d7.tar.gz
rust-cef9d4cbc1809bc29dee105fc8593d3e569155d7.zip
Retry to spawn/fork up to 3 times when it failed because of an interruption
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs66
1 files changed, 64 insertions, 2 deletions
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index ebdcb5acf22..ceaff596684 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -31,6 +31,15 @@ use libc::{c_int, pid_t};
 #[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
 use libc::{gid_t, uid_t};
 
+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;
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Command
 ////////////////////////////////////////////////////////////////////////////////
@@ -141,11 +150,31 @@ impl Command {
 
     // Attempts to fork the process. If successful, returns Ok((0, -1))
     // in the child, and Ok((child_pid, -1)) in the parent.
-    #[cfg(not(target_os = "linux", target_os = "nto"))]
+    #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))]
     unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
         cvt(libc::fork()).map(|res| (res, -1))
     }
 
+    // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened
+    // or closed a file descriptor while the fork() was occurring".
+    // Documentation says "... or try calling fork() again". This is what we do here.
+    // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
+    #[cfg(all(target_os = "nto", target_env = "nto71"))]
+    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;
+        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;
+            } else {
+                return cvt(r).map(|res| (res, -1));
+            }
+        }
+    }
+
     // Attempts to fork the process. If successful, returns Ok((0, -1))
     // in the child, and Ok((child_pid, child_pidfd)) in the parent.
     #[cfg(target_os = "linux")]
@@ -439,6 +468,34 @@ impl Command {
             }
         }
 
+        // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened
+        // or closed a file descriptor while the posix_spawn() was occurring".
+        // Documentation says "... or try calling posix_spawn() again". This is what we do here.
+        // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
+        #[cfg(all(target_os = "nto", target_env = "nto71"))]
+        unsafe fn retrying_libc_posix_spawnp(
+            pid: *mut pid_t,
+            file: *const c_char,
+            file_actions: *const posix_spawn_file_actions_t,
+            attrp: *const posix_spawnattr_t,
+            argv: *const *mut c_char,
+            envp: *const *mut c_char,
+        ) -> i32 {
+            let mut tries_left = MAX_FORKSPAWN_TRIES;
+            loop {
+                match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
+                    libc::EBADF if tries_left > 0 => {
+                        thread::yield_now();
+                        tries_left -= 1;
+                        continue;
+                    }
+                    r => {
+                        return r;
+                    }
+                }
+            }
+        }
+
         // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
         // and maybe others will gain this non-POSIX function too. We'll check
         // for this weak symbol as soon as it's needed, so we can return early
@@ -558,7 +615,12 @@ impl Command {
             // Make sure we synchronize access to the global `environ` resource
             let _env_lock = sys::os::env_read_lock();
             let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
-            cvt_nz(libc::posix_spawnp(
+
+            #[cfg(not(target_os = "nto"))]
+            let spawn_fn = libc::posix_spawnp;
+            #[cfg(target_os = "nto")]
+            let spawn_fn = retrying_libc_posix_spawnp;
+            cvt_nz(spawn_fn(
                 &mut p.pid,
                 self.get_program_cstr().as_ptr(),
                 file_actions.0.as_ptr(),