use std::borrow::Borrow;
use std::collections::hash_map::{HashMap, Iter};
use std::io::Error;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixStream;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use libc::{self, c_int};
use pipe;
use SigId;
#[derive(Debug)]
struct Waker {
pending: HashMap<c_int, AtomicBool>,
read: UnixStream,
write: UnixStream,
}
#[derive(Clone, Debug)]
pub struct Signals {
ids: Vec<SigId>,
waker: Arc<Waker>,
}
impl Signals {
pub fn new<I, S>(signals: I) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: Borrow<c_int>,
{
let (read, write) = UnixStream::pair()?;
let pending = signals
.into_iter()
.map(|sig| (*sig.borrow(), AtomicBool::new(false)))
.collect();
let waker = Arc::new(Waker {
pending,
read,
write,
});
let ids = waker
.pending
.keys()
.map(|sig| {
let sig = *sig;
let waker = Arc::clone(&waker);
let action = move || {
waker.pending[&sig].store(true, Ordering::SeqCst);
pipe::wake(waker.write.as_raw_fd());
};
unsafe { ::register(sig, action) }
})
.collect::<Result<_, _>>()?;
Ok(Self { ids, waker })
}
fn flush(&self, wait: bool) {
const SIZE: usize = 1024;
let mut buff = [0u8; SIZE];
unsafe {
libc::recv(
self.waker.read.as_raw_fd(),
buff.as_mut_ptr() as *mut libc::c_void,
SIZE,
if wait { 0 } else { libc::MSG_DONTWAIT },
);
}
}
pub fn pending(&self) -> Pending {
self.flush(false);
Pending(self.waker.pending.iter())
}
pub fn wait(&self) -> Pending {
self.flush(true);
Pending(self.waker.pending.iter())
}
pub fn forever(&self) -> Forever {
Forever {
signals: self,
iter: self.pending(),
}
}
}
impl Drop for Signals {
fn drop(&mut self) {
for id in &self.ids {
::unregister(*id);
}
}
}
impl<'a> IntoIterator for &'a Signals {
type Item = c_int;
type IntoIter = Forever<'a>;
fn into_iter(self) -> Forever<'a> {
self.forever()
}
}
pub struct Pending<'a>(Iter<'a, c_int, AtomicBool>);
impl<'a> Iterator for Pending<'a> {
type Item = c_int;
fn next(&mut self) -> Option<c_int> {
while let Some((sig, flag)) = self.0.next() {
if flag.swap(false, Ordering::SeqCst) {
return Some(*sig);
}
}
None
}
}
pub struct Forever<'a> {
signals: &'a Signals,
iter: Pending<'a>,
}
impl<'a> Iterator for Forever<'a> {
type Item = c_int;
fn next(&mut self) -> Option<c_int> {
loop {
if let Some(result) = self.iter.next() {
return Some(result);
}
self.iter = self.signals.wait();
}
}
}