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