11use std:: collections:: HashMap ;
22use std:: env;
3- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
43use std:: fs:: File ;
5- use std:: io:: { self , stdin, Read , Write } ;
6- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
7- use std:: io:: { copy, StdinLock } ;
4+ use std:: io:: { self , copy, Read , Write } ;
85use std:: os:: unix:: fs:: FileTypeExt ;
9- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
106use std:: os:: unix:: io:: { AsRawFd , FromRawFd } ;
117use std:: path:: { Path , PathBuf } ;
12- use std:: process:: { Child , ChildStdout , Command , Stdio } ;
8+ use std:: process:: { Child , Command , Stdio } ;
139use std:: rc:: Rc ;
1410
1511use crate :: config;
@@ -108,20 +104,6 @@ lazy_static::lazy_static! {
108104 nix:: unistd:: sysconf( nix:: unistd:: SysconfVar :: PAGE_SIZE ) . expect( "Unable to get page size" ) . unwrap( ) as usize ;
109105}
110106
111- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
112- trait ReadPipe : Read + Send { }
113-
114- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
115- trait ReadPipe : Read + AsRawFd + Send { }
116-
117- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
118- impl ReadPipe for StdinLock < ' _ > { }
119-
120- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
121- impl ReadPipe for File { }
122-
123- impl ReadPipe for ChildStdout { }
124-
125107impl HandlerMapping {
126108 pub fn new ( cfg : & config:: Config ) -> anyhow:: Result < HandlerMapping > {
127109 let mut handlers_open = FileHandlers :: new ( & cfg. default_handler_open ) ;
@@ -214,7 +196,10 @@ impl HandlerMapping {
214196 }
215197
216198 pub fn handle_pipe ( & self , mode : RsopMode ) -> Result < ( ) , HandlerError > {
217- let stdin = Self :: stdin_reader ( ) ?;
199+ let stdin_locked = io:: stdin ( ) . lock ( ) ;
200+ // Unlocked stdin via io::stdin does not allow use of copy optimisation (splice & co)
201+ // The conversion to File via its fd allows breaking the Send requirement of the MutexGuard
202+ let stdin = unsafe { File :: from_raw_fd ( stdin_locked. as_raw_fd ( ) ) } ;
218203 self . dispatch_pipe ( stdin, & mode)
219204 }
220205
@@ -301,7 +286,7 @@ impl HandlerMapping {
301286 #[ allow( clippy:: wildcard_in_or_patterns) ]
302287 fn dispatch_pipe < T > ( & self , mut pipe : T , mode : & RsopMode ) -> Result < ( ) , HandlerError >
303288 where
304- T : ReadPipe ,
289+ T : Read + Send ,
305290 {
306291 // Handler candidates
307292 let handlers = match mode {
@@ -520,7 +505,7 @@ impl HandlerMapping {
520505 mode : & RsopMode ,
521506 ) -> Result < ( ) , HandlerError >
522507 where
523- T : ReadPipe ,
508+ T : Read + Send ,
524509 {
525510 let term_size = Self :: term_size ( ) ;
526511
@@ -609,7 +594,7 @@ impl HandlerMapping {
609594 term_size : & termsize:: Size ,
610595 ) -> Result < ( ) , HandlerError >
611596 where
612- T : ReadPipe ,
597+ T : Read + Send ,
613598 {
614599 // Write to a temporary file if handler does not support reading from stdin
615600 let input = if handler. no_pipe {
@@ -683,34 +668,16 @@ impl HandlerMapping {
683668 Ok ( ( ) )
684669 }
685670
686- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
687- fn stdin_reader ( ) -> anyhow:: Result < StdinLock < ' static > > {
688- let stdin = Box :: leak ( Box :: new ( stdin ( ) ) ) ;
689- Ok ( stdin. lock ( ) )
690- }
691-
692- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
693- fn stdin_reader ( ) -> anyhow:: Result < File > {
694- // Unfortunately, stdin is buffered, and there is no clean way to get it
695- // unbuffered to read only what we want for the header, so use fd hack to get an unbuffered reader
696- // see https://users.rust-lang.org/t/add-unbuffered-rawstdin-rawstdout/26013
697- // On plaforms other than linux we don't care about buffering because we use chunk copy instead of splice
698- let stdin = stdin ( ) ;
699- let reader = unsafe { File :: from_raw_fd ( stdin. as_raw_fd ( ) ) } ;
700- Ok ( reader)
701- }
702-
703- #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
704- // Default chunk copy using stdlib's std::io::copy when splice syscall is not available
705- fn pipe_forward < S , D > ( src : & mut S , dst : & mut D , header : & [ u8 ] ) -> anyhow:: Result < u64 >
671+ // Default copy using stdlib's std::io::copy (uses splice syscall when available on Linux)
672+ fn pipe_forward < S , D > ( src : & mut S , dst : & mut D , header : & [ u8 ] ) -> anyhow:: Result < usize >
706673 where
707674 S : Read ,
708675 D : Write ,
709676 {
710677 dst. write_all ( header) ?;
711678 log:: trace!( "Header written ({} bytes)" , header. len( ) ) ;
712679
713- let copied = copy ( src, dst) ?;
680+ let copied = copy ( src, dst) ? as usize ;
714681 log:: trace!(
715682 "Pipe exhausted, moved {} bytes total" ,
716683 header. len( ) + copied
@@ -719,49 +686,9 @@ impl HandlerMapping {
719686 Ok ( header. len ( ) + copied)
720687 }
721688
722- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
723- // Efficient 0-copy implementation using splice
724- fn pipe_forward < S , D > ( src : & mut S , dst : & mut D , header : & [ u8 ] ) -> anyhow:: Result < usize >
725- where
726- S : AsRawFd ,
727- D : AsRawFd + Write ,
728- {
729- dst. write_all ( header) ?;
730- log:: trace!( "Header written ({} bytes)" , header. len( ) ) ;
731-
732- let mut c = 0 ;
733- const SPLICE_LEN : usize = 2usize . pow ( 62 ) ; // splice returns -EINVAL for pipe to file with usize::MAX len
734- const SPLICE_FLAGS : nix:: fcntl:: SpliceFFlags = nix:: fcntl:: SpliceFFlags :: empty ( ) ;
735-
736- loop {
737- let rc = nix:: fcntl:: splice (
738- src. as_raw_fd ( ) ,
739- None ,
740- dst. as_raw_fd ( ) ,
741- None ,
742- SPLICE_LEN ,
743- SPLICE_FLAGS ,
744- ) ;
745- let moved = match rc {
746- Err ( e) if e == nix:: errno:: Errno :: EPIPE => 0 ,
747- Err ( e) => return Err ( anyhow:: Error :: new ( e) ) ,
748- Ok ( m) => m,
749- } ;
750- log:: trace!( "moved = {}" , moved) ;
751- if moved == 0 {
752- break ;
753- }
754- c += moved;
755- }
756-
757- log:: trace!( "Pipe exhausted, moved {} bytes total" , header. len( ) + c) ;
758-
759- Ok ( header. len ( ) + c)
760- }
761-
762689 fn pipe_to_tmpfile < T > ( header : & [ u8 ] , mut pipe : T ) -> anyhow:: Result < tempfile:: NamedTempFile >
763690 where
764- T : ReadPipe ,
691+ T : Read + Send ,
765692 {
766693 let mut tmp_file = tempfile:: Builder :: new ( )
767694 . prefix ( const_format:: concatcp!( env!( "CARGO_PKG_NAME" ) , '_' ) )
0 commit comments