@@ -499,6 +499,10 @@ impl AsyncRead for File {
499
499
return Ready ( Ok ( ( ) ) ) ;
500
500
}
501
501
502
+ if let Some ( x) = read_nowait:: try_nonblocking_read ( me. std . as_ref ( ) , dst) {
503
+ return Ready ( x) ;
504
+ }
505
+
502
506
buf. ensure_capacity_for ( dst) ;
503
507
let std = me. std . clone ( ) ;
504
508
@@ -756,3 +760,74 @@ impl Inner {
756
760
}
757
761
}
758
762
}
763
+
764
+ #[ cfg( all( target_os = "linux" , not( test) ) ) ]
765
+ mod read_nowait {
766
+ use crate :: io:: ReadBuf ;
767
+ use libc:: { c_int, c_void, iovec, off_t, preadv2} ;
768
+ use std:: {
769
+ os:: unix:: prelude:: AsRawFd ,
770
+ sync:: atomic:: { AtomicBool , Ordering } ,
771
+ } ;
772
+
773
+ static NONBLOCKING_READ_SUPPORTED : AtomicBool = AtomicBool :: new ( true ) ;
774
+
775
+ pub ( crate ) fn try_nonblocking_read (
776
+ file : & crate :: fs:: sys:: File ,
777
+ dst : & mut ReadBuf < ' _ > ,
778
+ ) -> Option < std:: io:: Result < ( ) > > {
779
+ if !NONBLOCKING_READ_SUPPORTED . load ( Ordering :: Relaxed ) {
780
+ return None ;
781
+ }
782
+ let out = preadv2_safe ( file, dst, -1 , libc:: RWF_NOWAIT ) ;
783
+ if let Err ( err) = & out {
784
+ if matches ! ( err. raw_os_error( ) , Some ( libc:: ENOSYS ) ) {
785
+ NONBLOCKING_READ_SUPPORTED . store ( false , Ordering :: Relaxed ) ;
786
+ return None ;
787
+ }
788
+ }
789
+ Some ( out)
790
+ }
791
+
792
+ fn preadv2_safe (
793
+ file : & crate :: fs:: sys:: File ,
794
+ dst : & mut ReadBuf < ' _ > ,
795
+ offset : off_t ,
796
+ flags : c_int ,
797
+ ) -> std:: io:: Result < ( ) > {
798
+ unsafe {
799
+ /* We're manually have to defend against buffer overflows here. The slice API makes
800
+ * this fairly streightforward. */
801
+ let unfilled = dst. unfilled_mut ( ) ;
802
+ let iov = iovec {
803
+ iov_base : unfilled. as_mut_ptr ( ) as * mut c_void ,
804
+ iov_len : unfilled. len ( ) ,
805
+ } ;
806
+ /* We take a File object rather than an fd as reading from a sensitive fd may confuse
807
+ * other unsafe code that assumes that only they have access to that fd. */
808
+ let bytes_read = preadv2 ( file. as_raw_fd ( ) , & iov as * const iovec , 1 , offset, flags) ;
809
+ if bytes_read < 0 {
810
+ Err ( std:: io:: Error :: last_os_error ( ) )
811
+ } else {
812
+ /* preadv2 returns the number of bytes read, e.g. the number of bytes that have
813
+ * written into `unfilled`. So it's safe to assume that the data is now
814
+ * initialised */
815
+ dst. assume_init ( dst. filled ( ) . len ( ) + bytes_read as usize ) ;
816
+ dst. advance ( bytes_read as usize ) ;
817
+ Ok ( ( ) )
818
+ }
819
+ }
820
+ }
821
+ }
822
+
823
+ #[ cfg( any( not( target_os = "linux" ) , test) ) ]
824
+ mod read_nowait {
825
+ use crate :: io:: ReadBuf ;
826
+
827
+ pub ( crate ) fn try_nonblocking_read (
828
+ _file : & crate :: fs:: sys:: File ,
829
+ _dst : & mut ReadBuf < ' _ > ,
830
+ ) -> Option < std:: io:: Result < ( ) > > {
831
+ None
832
+ }
833
+ }
0 commit comments