use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::process::{ChildStderr, ChildStdin, ChildStdout};
use crate::io_source::IoSource;
use crate::{event, Interest, Registry, Token};
pub fn new() -> io::Result<(Sender, Receiver)> {
let mut fds: [RawFd; 2] = [-1, -1];
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "redox",
))]
unsafe {
if libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) != 0 {
return Err(io::Error::last_os_error());
}
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
unsafe {
if libc::pipe(fds.as_mut_ptr()) != 0 {
return Err(io::Error::last_os_error());
}
for fd in &fds {
if libc::fcntl(*fd, libc::F_SETFL, libc::O_NONBLOCK) != 0
|| libc::fcntl(*fd, libc::F_SETFD, libc::FD_CLOEXEC) != 0
{
let err = io::Error::last_os_error();
let _ = libc::close(fds[0]);
let _ = libc::close(fds[1]);
return Err(err);
}
}
}
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios",
target_os = "macos",
target_os = "illumos",
target_os = "redox",
)))]
compile_error!("unsupported target for `mio::unix::pipe`");
let r = unsafe { Receiver::from_raw_fd(fds[0]) };
let w = unsafe { Sender::from_raw_fd(fds[1]) };
Ok((w, r))
}
#[derive(Debug)]
pub struct Sender {
inner: IoSource<File>,
}
impl Sender {
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
set_nonblocking(self.inner.as_raw_fd(), nonblocking)
}
pub fn try_io<F, T>(&self, f: F) -> io::Result<T>
where
F: FnOnce() -> io::Result<T>,
{
self.inner.do_io(|_| f())
}
}
impl event::Source for Sender {
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.inner.register(registry, token, interests)
}
fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.inner.reregister(registry, token, interests)
}
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
self.inner.deregister(registry)
}
}
impl Write for Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|sender| (&*sender).flush())
}
}
impl Write for &Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|sender| (&*sender).flush())
}
}
impl From<ChildStdin> for Sender {
fn from(stdin: ChildStdin) -> Sender {
unsafe { Sender::from_raw_fd(stdin.into_raw_fd()) }
}
}
impl FromRawFd for Sender {
unsafe fn from_raw_fd(fd: RawFd) -> Sender {
Sender {
inner: IoSource::new(File::from_raw_fd(fd)),
}
}
}
impl AsRawFd for Sender {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl IntoRawFd for Sender {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[derive(Debug)]
pub struct Receiver {
inner: IoSource<File>,
}
impl Receiver {
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
set_nonblocking(self.inner.as_raw_fd(), nonblocking)
}
pub fn try_io<F, T>(&self, f: F) -> io::Result<T>
where
F: FnOnce() -> io::Result<T>,
{
self.inner.do_io(|_| f())
}
}
impl event::Source for Receiver {
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.inner.register(registry, token, interests)
}
fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
self.inner.reregister(registry, token, interests)
}
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
self.inner.deregister(registry)
}
}
impl Read for Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read_vectored(bufs))
}
}
impl Read for &Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read_vectored(bufs))
}
}
impl From<ChildStdout> for Receiver {
fn from(stdout: ChildStdout) -> Receiver {
unsafe { Receiver::from_raw_fd(stdout.into_raw_fd()) }
}
}
impl From<ChildStderr> for Receiver {
fn from(stderr: ChildStderr) -> Receiver {
unsafe { Receiver::from_raw_fd(stderr.into_raw_fd()) }
}
}
impl FromRawFd for Receiver {
unsafe fn from_raw_fd(fd: RawFd) -> Receiver {
Receiver {
inner: IoSource::new(File::from_raw_fd(fd)),
}
}
}
impl AsRawFd for Receiver {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl IntoRawFd for Receiver {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(not(target_os = "illumos"))]
fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
let value = nonblocking as libc::c_int;
if unsafe { libc::ioctl(fd, libc::FIONBIO, &value) } == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(target_os = "illumos")]
fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if flags < 0 {
return Err(io::Error::last_os_error());
}
let nflags = if nonblocking {
flags | libc::O_NONBLOCK
} else {
flags & !libc::O_NONBLOCK
};
if flags != nflags {
if unsafe { libc::fcntl(fd, libc::F_SETFL, nflags) } < 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}