]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/pipe.rs
Auto merge of #64546 - weiznich:bugfix/rfc-2451-rerebalance-tests, r=nikomatsakis
[rust.git] / src / libstd / sys / unix / pipe.rs
1 use crate::io::{self, IoSlice, IoSliceMut};
2 use crate::mem;
3 use crate::sync::atomic::{AtomicBool, Ordering};
4 use crate::sys::fd::FileDesc;
5 use crate::sys::{cvt, cvt_r};
6
7 use libc::c_int;
8
9 ////////////////////////////////////////////////////////////////////////////////
10 // Anonymous pipes
11 ////////////////////////////////////////////////////////////////////////////////
12
13 pub struct AnonPipe(FileDesc);
14
15 pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
16     syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int }
17     static INVALID: AtomicBool = AtomicBool::new(false);
18
19     let mut fds = [0; 2];
20
21     // Unfortunately the only known way right now to create atomically set the
22     // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
23     // 2.6.27, however, and because we support 2.6.18 we must detect this
24     // support dynamically.
25     if cfg!(any(target_os = "dragonfly",
26                 target_os = "freebsd",
27                 target_os = "linux",
28                 target_os = "netbsd",
29                 target_os = "openbsd",
30                 target_os = "redox")) &&
31        !INVALID.load(Ordering::SeqCst)
32     {
33
34         // Note that despite calling a glibc function here we may still
35         // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
36         // emulate on older kernels, so if you happen to be running on
37         // an older kernel you may see `pipe2` as a symbol but still not
38         // see the syscall.
39         match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
40             Ok(_) => {
41                 return Ok((AnonPipe(FileDesc::new(fds[0])),
42                             AnonPipe(FileDesc::new(fds[1]))));
43             }
44             Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
45                 INVALID.store(true, Ordering::SeqCst);
46             }
47             Err(e) => return Err(e),
48         }
49     }
50     cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
51
52     let fd0 = FileDesc::new(fds[0]);
53     let fd1 = FileDesc::new(fds[1]);
54     fd0.set_cloexec()?;
55     fd1.set_cloexec()?;
56     Ok((AnonPipe(fd0), AnonPipe(fd1)))
57 }
58
59 impl AnonPipe {
60     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
61         self.0.read(buf)
62     }
63
64     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
65         self.0.read_vectored(bufs)
66     }
67
68     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
69         self.0.write(buf)
70     }
71
72     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
73         self.0.write_vectored(bufs)
74     }
75
76     pub fn fd(&self) -> &FileDesc { &self.0 }
77     pub fn into_fd(self) -> FileDesc { self.0 }
78 }
79
80 pub fn read2(p1: AnonPipe,
81              v1: &mut Vec<u8>,
82              p2: AnonPipe,
83              v2: &mut Vec<u8>) -> io::Result<()> {
84
85     // Set both pipes into nonblocking mode as we're gonna be reading from both
86     // in the `select` loop below, and we wouldn't want one to block the other!
87     let p1 = p1.into_fd();
88     let p2 = p2.into_fd();
89     p1.set_nonblocking(true)?;
90     p2.set_nonblocking(true)?;
91
92     let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
93     fds[0].fd = p1.raw();
94     fds[0].events = libc::POLLIN;
95     fds[1].fd = p2.raw();
96     fds[1].events = libc::POLLIN;
97     loop {
98         // wait for either pipe to become readable using `poll`
99         cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
100
101         if fds[0].revents != 0 && read(&p1, v1)? {
102             p2.set_nonblocking(false)?;
103             return p2.read_to_end(v2).map(|_| ());
104         }
105         if fds[1].revents != 0 && read(&p2, v2)? {
106             p1.set_nonblocking(false)?;
107             return p1.read_to_end(v1).map(|_| ());
108         }
109     }
110
111     // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
112     // EAGAIN. If we hit EOF, then this will happen because the underlying
113     // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
114     // this case we flip the other fd back into blocking mode and read
115     // whatever's leftover on that file descriptor.
116     fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
117         match fd.read_to_end(dst) {
118             Ok(_) => Ok(true),
119             Err(e) => {
120                 if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
121                    e.raw_os_error() == Some(libc::EAGAIN) {
122                     Ok(false)
123                 } else {
124                     Err(e)
125                 }
126             }
127         }
128     }
129 }