1
1
use std:: collections:: HashMap ;
2
2
use std:: env;
3
- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
4
3
use 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 } ;
8
5
use std:: os:: unix:: fs:: FileTypeExt ;
9
- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
10
6
use std:: os:: unix:: io:: { AsRawFd , FromRawFd } ;
11
7
use std:: path:: { Path , PathBuf } ;
12
- use std:: process:: { Child , ChildStdout , Command , Stdio } ;
8
+ use std:: process:: { Child , Command , Stdio } ;
13
9
use std:: rc:: Rc ;
14
10
15
11
use crate :: config;
@@ -108,20 +104,6 @@ lazy_static::lazy_static! {
108
104
nix:: unistd:: sysconf( nix:: unistd:: SysconfVar :: PAGE_SIZE ) . expect( "Unable to get page size" ) . unwrap( ) as usize ;
109
105
}
110
106
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
-
125
107
impl HandlerMapping {
126
108
pub fn new ( cfg : & config:: Config ) -> anyhow:: Result < HandlerMapping > {
127
109
let mut handlers_open = FileHandlers :: new ( & cfg. default_handler_open ) ;
@@ -214,7 +196,11 @@ impl HandlerMapping {
214
196
}
215
197
216
198
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 ( ) ) } ;
203
+ //let stdin = BufReader::new(stdin_file);
218
204
self . dispatch_pipe ( stdin, & mode)
219
205
}
220
206
@@ -301,7 +287,7 @@ impl HandlerMapping {
301
287
#[ allow( clippy:: wildcard_in_or_patterns) ]
302
288
fn dispatch_pipe < T > ( & self , mut pipe : T , mode : & RsopMode ) -> Result < ( ) , HandlerError >
303
289
where
304
- T : ReadPipe ,
290
+ T : Read + Send ,
305
291
{
306
292
// Handler candidates
307
293
let handlers = match mode {
@@ -520,7 +506,7 @@ impl HandlerMapping {
520
506
mode : & RsopMode ,
521
507
) -> Result < ( ) , HandlerError >
522
508
where
523
- T : ReadPipe ,
509
+ T : Read + Send ,
524
510
{
525
511
let term_size = Self :: term_size ( ) ;
526
512
@@ -609,7 +595,7 @@ impl HandlerMapping {
609
595
term_size : & termsize:: Size ,
610
596
) -> Result < ( ) , HandlerError >
611
597
where
612
- T : ReadPipe ,
598
+ T : Read + Send ,
613
599
{
614
600
// Write to a temporary file if handler does not support reading from stdin
615
601
let input = if handler. no_pipe {
@@ -683,34 +669,16 @@ impl HandlerMapping {
683
669
Ok ( ( ) )
684
670
}
685
671
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 >
672
+ // Default copy using stdlib's std::io::copy (uses splice syscall when available on Linux)
673
+ fn pipe_forward < S , D > ( src : & mut S , dst : & mut D , header : & [ u8 ] ) -> anyhow:: Result < usize >
706
674
where
707
675
S : Read ,
708
676
D : Write ,
709
677
{
710
678
dst. write_all ( header) ?;
711
679
log:: trace!( "Header written ({} bytes)" , header. len( ) ) ;
712
680
713
- let copied = copy ( src, dst) ?;
681
+ let copied = copy ( src, dst) ? as usize ;
714
682
log:: trace!(
715
683
"Pipe exhausted, moved {} bytes total" ,
716
684
header. len( ) + copied
@@ -719,49 +687,9 @@ impl HandlerMapping {
719
687
Ok ( header. len ( ) + copied)
720
688
}
721
689
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
-
762
690
fn pipe_to_tmpfile < T > ( header : & [ u8 ] , mut pipe : T ) -> anyhow:: Result < tempfile:: NamedTempFile >
763
691
where
764
- T : ReadPipe ,
692
+ T : Read + Send ,
765
693
{
766
694
let mut tmp_file = tempfile:: Builder :: new ( )
767
695
. prefix ( const_format:: concatcp!( env!( "CARGO_PKG_NAME" ) , '_' ) )
0 commit comments