use std::{
io,
os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
ptr,
time::Duration,
};
#[derive(Debug)]
pub struct ExitWatch {
pidfd: OwnedFd,
}
impl ExitWatch {
pub fn send_signal(&self, signal: i32) -> io::Result<()> {
cvt!(unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
self.pidfd.as_raw_fd(),
signal,
ptr::null_mut::<libc::siginfo_t>(),
0,
)
});
Ok(())
}
}
#[derive(Debug)]
pub struct Poller {
epoll: OwnedFd,
events: Vec<libc::epoll_event>,
}
impl Poller {
pub fn new() -> io::Result<Self> {
let fd = cvt!(unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) });
Ok(Self {
epoll: unsafe { OwnedFd::from_raw_fd(fd) },
events: vec![libc::epoll_event { events: 0, u64: 0 }; 64],
})
}
pub fn watch_read(&self, fd: BorrowedFd<'_>, token: u64) -> io::Result<()> {
self.add(fd, token)
}
pub fn unwatch_read(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
self.delete(fd)
}
pub fn watch_exit(&self, pid: libc::pid_t, token: u64) -> io::Result<ExitWatch> {
let fd = cvt!(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) });
let pidfd = unsafe { OwnedFd::from_raw_fd(fd as RawFd) };
self.add(pidfd.as_fd(), token)?;
Ok(ExitWatch { pidfd })
}
pub fn unwatch_exit(&self, watch: &ExitWatch) -> io::Result<()> {
self.delete(watch.pidfd.as_fd())
}
fn add(&self, fd: BorrowedFd<'_>, token: u64) -> io::Result<()> {
let mut event = libc::epoll_event {
events: libc::EPOLLIN as u32,
u64: token,
};
self.ctl(libc::EPOLL_CTL_ADD, fd, &mut event)
}
fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
let mut event = libc::epoll_event { events: 0, u64: 0 };
self.ctl(libc::EPOLL_CTL_DEL, fd, &mut event)
}
fn ctl(&self, op: i32, fd: BorrowedFd<'_>, event: &mut libc::epoll_event) -> io::Result<()> {
cvt!(unsafe { libc::epoll_ctl(self.epoll.as_raw_fd(), op, fd.as_raw_fd(), event) });
Ok(())
}
pub fn wait(&mut self, tokens: &mut Vec<u64>, timeout: Option<Duration>) -> io::Result<usize> {
let millis = match timeout {
None => -1,
Some(d) if d.is_zero() => 0,
Some(d) => {
let millis = d.as_nanos().div_ceil(1_000_000);
millis.clamp(1, i32::MAX as u128) as i32
}
};
let n = cvt!(unsafe {
libc::epoll_wait(
self.epoll.as_raw_fd(),
self.events.as_mut_ptr(),
self.events.len() as i32,
millis,
)
});
tokens.clear();
tokens.extend(self.events[..n as usize].iter().map(|ev| ev.u64));
Ok(n as usize)
}
}