use crate::error::SysError;
use crate::shim::{self, SelectFd};
use rustix::io::{Errno, retry_on_intr};
use rustix::pipe;
use std::io::{Error, Write};
use std::os::fd::{AsFd, OwnedFd};
use std::sync::{Arc, Mutex};
#[derive(PartialEq)]
enum WriterMode {
Open,
Closed,
}
pub struct InterruptibleWriter<Fd: AsFd> {
mode: Mutex<WriterMode>,
fd: Fd,
pipe_rd: OwnedFd,
pipe_wr: OwnedFd,
}
impl<Fd: AsFd> InterruptibleWriter<Fd> {
pub fn open(fd: Fd) -> Result<Self, SysError> {
let (pipe_rd, pipe_wr) = match retry_on_intr(|| pipe::pipe()) {
Ok(fds) => fds,
Err(err) => return Err(SysError("pipe()", err)),
};
shim::fcntl_nonblock(&fd, true).map_err(|err| SysError("fcntl(fd)", err))?;
shim::fcntl_nonblock(&pipe_rd, true).map_err(|err| SysError("fcntl(pipe)", err))?;
shim::fcntl_nonblock(&pipe_wr, true).map_err(|err| SysError("fcntl(pipe)", err))?;
Ok(InterruptibleWriter {
mode: Mutex::new(WriterMode::Open),
fd,
pipe_rd,
pipe_wr,
})
}
pub fn close(&self) -> Result<(), SysError> {
{
let mut locked_mode = self.mode.lock().unwrap();
if *locked_mode == WriterMode::Closed {
return Ok(());
}
*locked_mode = WriterMode::Closed;
}
if let Err(err) = shim::write(&self.pipe_wr, &[0u8]) {
if err != Errno::AGAIN {
return Err(SysError("write(pipe)", err));
}
}
Ok(())
}
pub fn blocking_writer(self: &Arc<Self>) -> ArcTimeoutWriter<Fd> {
ArcTimeoutWriter(Arc::clone(self))
}
fn write_imp(&self, buf: &[u8]) -> Result<usize, Error> {
loop {
{
let locked_mode = self.mode.lock().unwrap();
if *locked_mode == WriterMode::Closed {
return Ok(buf.len());
}
};
let mut pipe_fd = SelectFd {
fd: self.pipe_rd.as_fd(),
mask: SelectFd::READABLE,
};
let mut data_fd = SelectFd {
fd: self.fd.as_fd(),
mask: SelectFd::WRITEABLE,
};
shim::select(&mut [&mut pipe_fd, &mut data_fd], None)?;
if pipe_fd.mask != 0 {
_ = shim::read(&self.pipe_rd, &mut [0u8; 128]);
}
if data_fd.mask != 0 {
break;
}
}
match shim::write(&self.fd, buf) {
Ok(n) => Ok(n),
Err(err) => Err(Error::new(err.kind(), err)),
}
}
}
pub struct ArcTimeoutWriter<Fd: AsFd>(Arc<InterruptibleWriter<Fd>>);
impl<Fd: AsFd> Write for ArcTimeoutWriter<Fd> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.0.write_imp(buf)
}
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}