@@ -35,8 +35,26 @@ cfg_if::cfg_if! {
35
35
if #[ cfg( all( target_os = "nto" , target_env = "nto71" ) ) ] {
36
36
use crate :: thread;
37
37
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 ) ;
40
58
}
41
59
}
42
60
@@ -163,12 +181,25 @@ impl Command {
163
181
unsafe fn do_fork ( & mut self ) -> Result < ( pid_t , pid_t ) , io:: Error > {
164
182
use crate :: sys:: os:: errno;
165
183
166
- let mut tries_left = MAX_FORKSPAWN_TRIES ;
184
+ let mut delay = MIN_FORKSPAWN_SLEEP ;
185
+
167
186
loop {
168
187
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 ;
172
203
} else {
173
204
return cvt ( r) . map ( |res| ( res, -1 ) ) ;
174
205
}
@@ -480,17 +511,28 @@ impl Command {
480
511
attrp : * const posix_spawnattr_t ,
481
512
argv : * const * mut c_char ,
482
513
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 ;
485
516
loop {
486
517
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 ;
490
532
continue ;
491
533
}
492
534
r => {
493
- return r ;
535
+ return Ok ( r ) ;
494
536
}
495
537
}
496
538
}
@@ -620,14 +662,20 @@ impl Command {
620
662
let spawn_fn = libc:: posix_spawnp;
621
663
#[ cfg( target_os = "nto" ) ]
622
664
let spawn_fn = retrying_libc_posix_spawnp;
623
- cvt_nz ( spawn_fn (
665
+
666
+ let spawn_res = spawn_fn (
624
667
& mut p. pid ,
625
668
self . get_program_cstr ( ) . as_ptr ( ) ,
626
669
file_actions. 0 . as_ptr ( ) ,
627
670
attrs. 0 . as_ptr ( ) ,
628
671
self . get_argv ( ) . as_ptr ( ) as * const _ ,
629
672
envp as * const _ ,
630
- ) ) ?;
673
+ ) ;
674
+
675
+ #[ cfg( target_os = "nto" ) ]
676
+ let spawn_res = spawn_res?;
677
+
678
+ cvt_nz ( spawn_res) ?;
631
679
Ok ( Some ( p) )
632
680
}
633
681
}
0 commit comments