crate::ix!();
#[derive(Debug, Getters, Builder)]
#[getset(get = "pub(crate)")]
pub struct TokenPipe {
#[builder(default = "[-1, -1]")]
fds: [i32; 2],
}
impl Drop for TokenPipe {
fn drop(&mut self) {
trace!(fds = ?self.fds, "TokenPipe::drop");
self.close();
}
}
impl TokenPipe {
pub fn new(fds: [i32; 2]) -> Self {
trace!(fds = ?fds, "TokenPipe::new");
Self { fds }
}
pub fn make() -> Option<Self> {
trace!("TokenPipe::make → start");
#[cfg(not(windows))]
{
let mut fds = [-1; 2];
if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
error!(errno = last_errno(), "pipe(2) failed");
return None;
}
info!(fds = ?fds, "pipe(2) created");
return Some(Self { fds });
}
#[cfg(windows)]
{
let mut fds = [-1; 2];
const BUFSZ: libc::c_int = 4096;
const O_BINARY: libc::c_int = 0x8000; if unsafe { libc::_pipe(fds.as_mut_ptr(), BUFSZ, O_BINARY) } != 0 {
error!(errno = last_errno(), "_pipe failed");
return None;
}
info!(fds = ?fds, "_pipe created");
return Some(Self { fds });
}
}
pub fn take_read_end(&mut self) -> TokenPipeEnd {
trace!(fd = self.fds[0], "TokenPipe::take_read_end");
let fd = self.fds[0];
self.fds[0] = -1;
TokenPipeEnd::new(Some(fd))
}
pub fn take_write_end(&mut self) -> TokenPipeEnd {
trace!(fd = self.fds[1], "TokenPipe::take_write_end");
let fd = self.fds[1];
self.fds[1] = -1;
TokenPipeEnd::new(Some(fd))
}
pub fn close(&mut self) {
trace!(fds = ?self.fds, "TokenPipe::close");
for fd in &mut self.fds {
if *fd != -1 {
#[cfg(windows)]
unsafe { libc::_close(*fd); }
#[cfg(not(windows))]
unsafe { libc::close(*fd); }
*fd = -1;
}
}
}
pub fn new_from_other(mut other: Self) -> Self {
trace!(fds = ?other.fds, "TokenPipe::new_from_other");
let mut fds = [-1; 2];
for (d, s) in fds.iter_mut().zip(other.fds.iter_mut()) {
*d = *s;
*s = -1;
}
Self { fds }
}
pub fn assign_from(&mut self, mut other: Self) -> &mut Self {
trace!(self_fds = ?self.fds, other_fds = ?other.fds, "TokenPipe::assign_from");
self.close();
for (d, s) in self.fds.iter_mut().zip(other.fds.iter_mut()) {
*d = *s;
*s = -1;
}
self
}
}
#[cfg(all(test, not(windows)))]
mod tokenpipe_pipe_behavior {
use super::*;
#[traced_test]
fn endpoints_are_moved_only_once() {
let mut pipe = TokenPipe::make().expect("pipe creation");
let _r1 = pipe.take_read_end();
let _w1 = pipe.take_write_end();
let mut r2 = pipe.take_read_end();
let mut w2 = pipe.take_write_end();
assert_eq!(r2.token_read(), TokenPipeEndStatus::TS_ERR as i32);
assert_eq!(w2.token_write(1), TokenPipeEndStatus::TS_ERR as i32);
}
#[traced_test]
fn assign_from_transfers_fd() {
let mut pipe = TokenPipe::make().expect("pipe creation");
let mut writer1 = pipe.take_write_end(); let mut writer2 = TokenPipeEnd::new(None);
writer2.assign_from(writer1);
let mut reader = pipe.take_read_end();
assert_eq!(writer2.token_write(0xAB), 0);
assert_eq!(reader.token_read(), 0xAB);
}
#[traced_test]
fn close_then_take_returns_closed_endpoints() {
let mut pipe = TokenPipe::make().expect("pipe creation");
pipe.close();
let mut r = pipe.take_read_end();
let mut w = pipe.take_write_end();
assert_eq!(r.token_read(), TokenPipeEndStatus::TS_ERR as i32);
assert_eq!(w.token_write(77), TokenPipeEndStatus::TS_ERR as i32);
}
}