@@ -285,6 +285,33 @@ impl Client {
285285 pub fn try_acquire ( & self ) -> io:: Result < Option < Acquired > > {
286286 let mut buf = [ 0 ] ;
287287
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+
288315 let ( mut fifo, is_non_blocking) = match self {
289316 Self :: Fifo {
290317 file,
@@ -368,6 +395,82 @@ impl Client {
368395 }
369396}
370397
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+
371474#[ derive( Debug ) ]
372475pub struct Helper {
373476 thread : JoinHandle < ( ) > ,
0 commit comments