1 //! Read both stdout and stderr of child without deadlocks.
3 //! <https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs>
4 //! <https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231>
8 process::{Command, Output, Stdio},
11 pub fn streaming_output(
13 on_stdout_line: &mut dyn FnMut(&str),
14 on_stderr_line: &mut dyn FnMut(&str),
15 ) -> io::Result<Output> {
16 let mut stdout = Vec::new();
17 let mut stderr = Vec::new();
19 let cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
22 let mut child = cmd.spawn()?;
23 let out = child.stdout.take().unwrap();
24 let err = child.stderr.take().unwrap();
25 imp::read2(out, err, &mut |is_out, data, eof| {
29 match data.iter().rposition(|b| *b == b'\n') {
35 // scope for new_lines
37 let dst = if is_out { &mut stdout } else { &mut stderr };
38 let start = dst.len();
39 let data = data.drain(..idx);
43 for line in String::from_utf8_lossy(new_lines).lines() {
55 Ok(Output { status, stdout, stderr })
61 io::{self, prelude::*},
64 process::{ChildStderr, ChildStdout},
68 mut out_pipe: ChildStdout,
69 mut err_pipe: ChildStderr,
70 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
73 libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
74 libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
77 let mut out_done = false;
78 let mut err_done = false;
79 let mut out = Vec::new();
80 let mut err = Vec::new();
82 let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
83 fds[0].fd = out_pipe.as_raw_fd();
84 fds[0].events = libc::POLLIN;
85 fds[1].fd = err_pipe.as_raw_fd();
86 fds[1].events = libc::POLLIN;
91 // wait for either pipe to become readable using `select`
92 let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
94 let err = io::Error::last_os_error();
95 if err.kind() == io::ErrorKind::Interrupted {
101 // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
102 // EAGAIN. If we hit EOF, then this will happen because the underlying
103 // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
104 // this case we flip the other fd back into blocking mode and read
105 // whatever's leftover on that file descriptor.
106 let handle = |res: io::Result<_>| match res {
109 if e.kind() == io::ErrorKind::WouldBlock {
116 if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
120 data(false, &mut err, err_done);
121 if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
123 fds[0].fd = err_pipe.as_raw_fd();
127 data(true, &mut out, out_done);
137 os::windows::prelude::*,
138 process::{ChildStderr, ChildStdout},
143 iocp::{CompletionPort, CompletionStatus},
147 use winapi::shared::winerror::ERROR_BROKEN_PIPE;
150 dst: &'a mut Vec<u8>,
151 overlapped: Overlapped,
157 out_pipe: ChildStdout,
158 err_pipe: ChildStderr,
159 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
160 ) -> io::Result<()> {
161 let mut out = Vec::new();
162 let mut err = Vec::new();
164 let port = CompletionPort::new(1)?;
165 port.add_handle(0, &out_pipe)?;
166 port.add_handle(1, &err_pipe)?;
169 let mut out_pipe = Pipe::new(out_pipe, &mut out);
170 let mut err_pipe = Pipe::new(err_pipe, &mut err);
175 let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
177 while !out_pipe.done || !err_pipe.done {
178 for status in port.get_many(&mut status, None)? {
179 if status.token() == 0 {
180 out_pipe.complete(status);
181 data(true, out_pipe.dst, out_pipe.done);
184 err_pipe.complete(status);
185 data(false, err_pipe.dst, err_pipe.done);
196 unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
199 pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
200 overlapped: Overlapped::zero(),
205 unsafe fn read(&mut self) -> io::Result<()> {
206 let dst = slice_to_end(self.dst);
207 match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
210 if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
220 unsafe fn complete(&mut self, status: &CompletionStatus) {
221 let prev = self.dst.len();
222 self.dst.set_len(prev + status.bytes_transferred() as usize);
223 if status.bytes_transferred() == 0 {
229 unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
230 if v.capacity() == 0 {
233 if v.capacity() == v.len() {
236 slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len())
240 #[cfg(target_arch = "wasm32")]
244 process::{ChildStderr, ChildStdout},
248 _out_pipe: ChildStdout,
249 _err_pipe: ChildStderr,
250 _data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
251 ) -> io::Result<()> {
252 panic!("no processes on wasm")