use crate::{EpollFlags, Fd};
pub use nix::sys::signal::{SigSet, Signal};
use nix::sys::{
signal::SigmaskHow,
signal::pthread_sigmask,
signalfd::{SfdFlags, SignalFd},
};
use std::{io::Error, os::fd::AsFd};
pub struct AsyncSignal {
fd: Fd<SignalFd>,
sigset: SigSet,
}
impl AsyncSignal {
pub fn new(sigset: SigSet) -> Result<Self, Error> {
if sigset == SigSet::empty() {
return Err(Error::other("empty sigset"));
}
let mut oldset = SigSet::empty();
pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(&mut oldset))?;
for s in &sigset {
if oldset.contains(s) {
return Err(Error::other("signal is blocked"));
}
}
let signalfd = SignalFd::with_flags(&sigset, SfdFlags::SFD_CLOEXEC)?;
pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(&sigset), None)?;
Ok(Self {
fd: Fd::new(signalfd, EpollFlags::EPOLLIN)?,
sigset,
})
}
pub async fn receive(&mut self) -> Result<nix::libc::signalfd_siginfo, Error> {
self.fd
.with(|fd, _events| {
let mut buf = std::mem::MaybeUninit::<nix::libc::signalfd_siginfo>::uninit();
nix::unistd::read(fd.as_fd(), unsafe {
std::slice::from_raw_parts_mut(
buf.as_mut_ptr().cast::<u8>(),
std::mem::size_of_val(&buf),
)
})?;
Ok(unsafe { buf.assume_init() })
})
.await
}
}
impl Drop for AsyncSignal {
fn drop(&mut self) {
let _ = pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(&self.sigset), None);
}
}