@@ -285,6 +285,33 @@ impl Client {
285
285
pub fn try_acquire ( & self ) -> io:: Result < Option < Acquired > > {
286
286
let mut buf = [ 0 ] ;
287
287
288
+ // On Linux, we can use preadv2 to do non-blocking read,
289
+ // even if `O_NONBLOCK` is not set.
290
+ //
291
+ // TODO: musl libc supports preadv2 since 1.2.5, but `libc` crate
292
+ // hasn't yet added it.
293
+ #[ cfg( target_os = "linux" ) ]
294
+ {
295
+ let read = self . read ( ) . as_raw_fd ( ) ;
296
+ loop {
297
+ match linux:: non_blocking_read ( read, & mut buf) {
298
+ Ok ( 1 ) => return Ok ( Some ( Acquired { byte : buf[ 0 ] } ) ) ,
299
+ Ok ( _) => {
300
+ return Err ( io:: Error :: new (
301
+ io:: ErrorKind :: UnexpectedEof ,
302
+ "early EOF on jobserver pipe" ,
303
+ ) )
304
+ }
305
+
306
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: WouldBlock => return Ok ( None ) ,
307
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: Interrupted => continue ,
308
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: Unsupported => break ,
309
+
310
+ Err ( err) => return Err ( err) ,
311
+ }
312
+ }
313
+ }
314
+
288
315
let ( mut fifo, is_non_blocking) = match self {
289
316
Self :: Fifo {
290
317
file,
@@ -368,6 +395,82 @@ impl Client {
368
395
}
369
396
}
370
397
398
+ // This should be available for all linux targets,
399
+ // though only [`non_blocking_read`] currently uses it so adding gnu cfg.
400
+ #[ cfg( target_os = "linux" ) ]
401
+ mod linux {
402
+ use super :: * ;
403
+
404
+ use libc:: { iovec, off_t, ssize_t, syscall, SYS_preadv2 } ;
405
+
406
+ fn cvt_ssize ( t : ssize_t ) -> io:: Result < ssize_t > {
407
+ if t == -1 {
408
+ Err ( io:: Error :: last_os_error ( ) )
409
+ } else {
410
+ Ok ( t)
411
+ }
412
+ }
413
+
414
+ unsafe fn preadv2 (
415
+ fd : c_int ,
416
+ iov : * const iovec ,
417
+ iovcnt : c_int ,
418
+ offset : off_t ,
419
+ flags : c_int ,
420
+ ) -> ssize_t {
421
+ #[ cfg( all( target_arch = "x86_64" , target_pointer_width = "64" ) ) ]
422
+ let res = syscall ( SYS_preadv2 , fd, iov, iovcnt, offset, 0 , flags) ;
423
+
424
+ #[ cfg( all( target_arch = "x86_64" , target_pointer_width = "32" ) ) ]
425
+ let res = syscall ( SYS_preadv2 , fd, iov, iovcnt, offset, 0 , flags) ;
426
+
427
+ #[ cfg( not( target_arch = "x86_64" ) ) ]
428
+ let res = syscall (
429
+ SYS_preadv2 ,
430
+ fd,
431
+ iov,
432
+ iovcnt,
433
+ offset as libc:: c_long ,
434
+ ( ( offset as u64 ) >> 32 ) as libc:: c_long ,
435
+ flags,
436
+ ) ;
437
+
438
+ res. try_into ( ) . unwrap ( )
439
+ }
440
+
441
+ pub fn non_blocking_read ( fd : c_int , buf : & mut [ u8 ] ) -> io:: Result < usize > {
442
+ static IS_NONBLOCKING_READ_UNSUPPORTED : AtomicBool = AtomicBool :: new ( false ) ;
443
+
444
+ if IS_NONBLOCKING_READ_UNSUPPORTED . load ( Ordering :: Relaxed ) {
445
+ return Err ( io:: ErrorKind :: Unsupported . into ( ) ) ;
446
+ }
447
+
448
+ match cvt_ssize ( unsafe {
449
+ preadv2 (
450
+ fd,
451
+ & iovec {
452
+ iov_base : buf. as_ptr ( ) as * mut _ ,
453
+ iov_len : buf. len ( ) ,
454
+ } ,
455
+ 1 ,
456
+ -1 ,
457
+ libc:: RWF_NOWAIT ,
458
+ )
459
+ } ) {
460
+ Ok ( cnt) => Ok ( cnt. try_into ( ) . unwrap ( ) ) ,
461
+ Err ( err) if err. raw_os_error ( ) == Some ( libc:: EOPNOTSUPP ) => {
462
+ IS_NONBLOCKING_READ_UNSUPPORTED . store ( true , Ordering :: Relaxed ) ;
463
+ Err ( io:: ErrorKind :: Unsupported . into ( ) )
464
+ }
465
+ Err ( err) if err. kind ( ) == io:: ErrorKind :: Unsupported => {
466
+ IS_NONBLOCKING_READ_UNSUPPORTED . store ( true , Ordering :: Relaxed ) ;
467
+ Err ( err)
468
+ }
469
+ Err ( err) => Err ( err) ,
470
+ }
471
+ }
472
+ }
473
+
371
474
#[ derive( Debug ) ]
372
475
pub struct Helper {
373
476
thread : JoinHandle < ( ) > ,
0 commit comments