1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
pub struct Pipe {
read: OwnedFd,
write: OwnedFd,
}
impl Pipe {
pub fn new() -> io::Result<Self> {
Self::shutdown()
}
pub fn write_fd(&self) -> RawFd {
self.write.as_raw_fd()
}
pub fn shutdown() -> io::Result<Self> {
let mut fds = [0 as RawFd; 2];
cfg_select! {
target_os = "linux" => {
// SAFETY: `fds` is a valid 2-element array; pipe2 fills both descriptors on success.
let rc = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) };
if rc != 0 {
return Err(io::Error::last_os_error());
}
}
_ => {
// SAFETY: `fds` is a valid 2-element array; pipe fills both descriptors on success.
let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
if rc != 0 {
return Err(io::Error::last_os_error());
}
for fd in fds {
// SAFETY: fd was just returned by pipe and is live.
unsafe {
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
let flags = libc::fcntl(fd, libc::F_GETFL, 0);
if flags >= 0 {
libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
}
}
}
}
}
Ok(Self::from_fds(fds[0], fds[1]))
}
pub(super) fn from_fds(read: RawFd, write: RawFd) -> Self {
// SAFETY: fds freshly returned by pipe(2); ownership transfers here, drop closes both.
unsafe {
Self {
read: OwnedFd::from_raw_fd(read),
write: OwnedFd::from_raw_fd(write),
}
}
}
pub fn read_fd(&self) -> RawFd {
self.read.as_raw_fd()
}
pub fn fire(&self) {
let byte = 1u8;
// SAFETY: writes one byte from a live stack address to the pipe write end; never blocks on an empty pipe.
unsafe { libc::write(self.write.as_raw_fd(), (&byte as *const u8).cast(), 1) };
}
}