@@ -143,45 +143,46 @@ struct ReadStdoutFailOnError {
143143 read : std:: process:: ChildStdout ,
144144}
145145
146+ impl ReadStdoutFailOnError {
147+ fn swap_err_if_present_in_stderr ( & self , wanted : usize , res : std:: io:: Result < usize > ) -> std:: io:: Result < usize > {
148+ match self . recv . try_recv ( ) . ok ( ) {
149+ Some ( err) => Err ( err) ,
150+ None => match res {
151+ Ok ( n) if n == wanted => Ok ( n) ,
152+ Ok ( n) => {
153+ // TODO: fix this
154+ // When parsing refs this seems to happen legitimately
155+ // (even though we read packet lines only and should always know exactly how much to read)
156+ // Maybe this still happens in `read_exact()` as sometimes we just don't get enough bytes
157+ // despite knowing how many.
158+ // To prevent deadlock, we have to set a timeout which slows down legitimate parts of the protocol.
159+ // This code was specifically written to make the `cargo` test-suite pass, and we can reduce
160+ // the timeouts even more once there is a native ssh transport that is used by `cargo`, it will
161+ // be able to handle these properly.
162+ // Alternatively, one could implement something like `read2` to avoid blocking on stderr entirely.
163+ self . recv
164+ . recv_timeout ( std:: time:: Duration :: from_millis ( 5 ) )
165+ . ok ( )
166+ . map_or ( Ok ( n) , Err )
167+ }
168+ Err ( err) => Err ( self . recv . recv ( ) . ok ( ) . unwrap_or ( err) ) ,
169+ } ,
170+ }
171+ }
172+ }
173+
174+ impl std:: io:: Read for ReadStdoutFailOnError {
175+ fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
176+ let res = self . read . read ( buf) ;
177+ self . swap_err_if_present_in_stderr ( buf. len ( ) , res)
178+ }
179+ }
180+
146181fn supervise_stderr (
147182 ssh_kind : ssh:: ProgramKind ,
148183 stderr : std:: process:: ChildStderr ,
149184 stdout : std:: process:: ChildStdout ,
150185) -> ReadStdoutFailOnError {
151- impl ReadStdoutFailOnError {
152- fn swap_err_if_present_in_stderr ( & self , wanted : usize , res : std:: io:: Result < usize > ) -> std:: io:: Result < usize > {
153- match self . recv . try_recv ( ) . ok ( ) {
154- Some ( err) => Err ( err) ,
155- None => match res {
156- Ok ( n) if n == wanted => Ok ( n) ,
157- Ok ( n) => {
158- // TODO: fix this
159- // When parsing refs this seems to happen legitimately
160- // (even though we read packet lines only and should always know exactly how much to read)
161- // Maybe this still happens in `read_exact()` as sometimes we just don't get enough bytes
162- // despite knowing how many.
163- // To prevent deadlock, we have to set a timeout which slows down legitimate parts of the protocol.
164- // This code was specifically written to make the `cargo` test-suite pass, and we can reduce
165- // the timeouts even more once there is a native ssh transport that is used by `cargo`, it will
166- // be able to handle these properly.
167- // Alternatively, one could implement something like `read2` to avoid blocking on stderr entirely.
168- self . recv
169- . recv_timeout ( std:: time:: Duration :: from_millis ( 5 ) )
170- . ok ( )
171- . map_or ( Ok ( n) , Err )
172- }
173- Err ( err) => Err ( self . recv . recv ( ) . ok ( ) . unwrap_or ( err) ) ,
174- } ,
175- }
176- }
177- }
178- impl std:: io:: Read for ReadStdoutFailOnError {
179- fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
180- let res = self . read . read ( buf) ;
181- self . swap_err_if_present_in_stderr ( buf. len ( ) , res)
182- }
183- }
184-
185186 let ( send, recv) = std:: sync:: mpsc:: sync_channel ( 1 ) ;
186187 std:: thread:: Builder :: new ( )
187188 . name ( "supervise ssh stderr" . into ( ) )
0 commit comments