]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/read2.rs
Rollup merge of #52769 - sinkuu:stray_test, r=alexcrichton
[rust.git] / src / tools / compiletest / src / read2.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
12 // Consider unify the read2() in libstd, cargo and this to prevent further code duplication.
13
14 pub use self::imp::read2;
15
16 #[cfg(not(any(unix, windows)))]
17 mod imp {
18     use std::io::{self, Read};
19     use std::process::{ChildStderr, ChildStdout};
20
21     pub fn read2(
22         out_pipe: ChildStdout,
23         err_pipe: ChildStderr,
24         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
25     ) -> io::Result<()> {
26         let mut buffer = Vec::new();
27         out_pipe.read_to_end(&mut buffer)?;
28         data(true, &mut buffer, true);
29         buffer.clear();
30         err_pipe.read_to_end(&mut buffer)?;
31         data(false, &mut buffer, true);
32         Ok(())
33     }
34 }
35
36 #[cfg(unix)]
37 mod imp {
38     use libc;
39     use std::io;
40     use std::io::prelude::*;
41     use std::mem;
42     use std::os::unix::prelude::*;
43     use std::process::{ChildStderr, ChildStdout};
44
45     pub fn read2(
46         mut out_pipe: ChildStdout,
47         mut err_pipe: ChildStderr,
48         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
49     ) -> io::Result<()> {
50         unsafe {
51             libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
52             libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
53         }
54
55         let mut out_done = false;
56         let mut err_done = false;
57         let mut out = Vec::new();
58         let mut err = Vec::new();
59
60         let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
61         fds[0].fd = out_pipe.as_raw_fd();
62         fds[0].events = libc::POLLIN;
63         fds[1].fd = err_pipe.as_raw_fd();
64         fds[1].events = libc::POLLIN;
65         let mut nfds = 2;
66         let mut errfd = 1;
67
68         while nfds > 0 {
69             // wait for either pipe to become readable using `select`
70             let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
71             if r == -1 {
72                 let err = io::Error::last_os_error();
73                 if err.kind() == io::ErrorKind::Interrupted {
74                     continue;
75                 }
76                 return Err(err);
77             }
78
79             // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
80             // EAGAIN. If we hit EOF, then this will happen because the underlying
81             // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
82             // this case we flip the other fd back into blocking mode and read
83             // whatever's leftover on that file descriptor.
84             let handle = |res: io::Result<_>| match res {
85                 Ok(_) => Ok(true),
86                 Err(e) => {
87                     if e.kind() == io::ErrorKind::WouldBlock {
88                         Ok(false)
89                     } else {
90                         Err(e)
91                     }
92                 }
93             };
94             if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
95                 err_done = true;
96                 nfds -= 1;
97             }
98             data(false, &mut err, err_done);
99             if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
100                 out_done = true;
101                 fds[0].fd = err_pipe.as_raw_fd();
102                 errfd = 0;
103                 nfds -= 1;
104             }
105             data(true, &mut out, out_done);
106         }
107         Ok(())
108     }
109 }
110
111 #[cfg(windows)]
112 mod imp {
113     extern crate miow;
114     extern crate winapi;
115
116     use std::io;
117     use std::os::windows::prelude::*;
118     use std::process::{ChildStderr, ChildStdout};
119     use std::slice;
120
121     use self::miow::iocp::{CompletionPort, CompletionStatus};
122     use self::miow::pipe::NamedPipe;
123     use self::miow::Overlapped;
124     use self::winapi::shared::winerror::ERROR_BROKEN_PIPE;
125
126     struct Pipe<'a> {
127         dst: &'a mut Vec<u8>,
128         overlapped: Overlapped,
129         pipe: NamedPipe,
130         done: bool,
131     }
132
133     pub fn read2(
134         out_pipe: ChildStdout,
135         err_pipe: ChildStderr,
136         data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
137     ) -> io::Result<()> {
138         let mut out = Vec::new();
139         let mut err = Vec::new();
140
141         let port = CompletionPort::new(1)?;
142         port.add_handle(0, &out_pipe)?;
143         port.add_handle(1, &err_pipe)?;
144
145         unsafe {
146             let mut out_pipe = Pipe::new(out_pipe, &mut out);
147             let mut err_pipe = Pipe::new(err_pipe, &mut err);
148
149             out_pipe.read()?;
150             err_pipe.read()?;
151
152             let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
153
154             while !out_pipe.done || !err_pipe.done {
155                 for status in port.get_many(&mut status, None)? {
156                     if status.token() == 0 {
157                         out_pipe.complete(status);
158                         data(true, out_pipe.dst, out_pipe.done);
159                         out_pipe.read()?;
160                     } else {
161                         err_pipe.complete(status);
162                         data(false, err_pipe.dst, err_pipe.done);
163                         err_pipe.read()?;
164                     }
165                 }
166             }
167
168             Ok(())
169         }
170     }
171
172     impl<'a> Pipe<'a> {
173         unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
174             Pipe {
175                 dst: dst,
176                 pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
177                 overlapped: Overlapped::zero(),
178                 done: false,
179             }
180         }
181
182         unsafe fn read(&mut self) -> io::Result<()> {
183             let dst = slice_to_end(self.dst);
184             match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
185                 Ok(_) => Ok(()),
186                 Err(e) => {
187                     if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
188                         self.done = true;
189                         Ok(())
190                     } else {
191                         Err(e)
192                     }
193                 }
194             }
195         }
196
197         unsafe fn complete(&mut self, status: &CompletionStatus) {
198             let prev = self.dst.len();
199             self.dst.set_len(prev + status.bytes_transferred() as usize);
200             if status.bytes_transferred() == 0 {
201                 self.done = true;
202             }
203         }
204     }
205
206     unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
207         if v.capacity() == 0 {
208             v.reserve(16);
209         }
210         if v.capacity() == v.len() {
211             v.reserve(1);
212         }
213         slice::from_raw_parts_mut(
214             v.as_mut_ptr().offset(v.len() as isize),
215             v.capacity() - v.len(),
216         )
217     }
218 }