@@ -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