use std::{io, os::fd::AsRawFd};
use crate::log::dev_error;
use crate::system::signal::SignalAction;
use crate::system::{
poll::PollSet,
signal::{SignalHandler, SignalInfo, SignalNumber},
};
use signal_hook::consts::*;
pub(super) trait EventClosure: Sized {
type Break;
type Exit;
fn on_signal(&mut self, info: SignalInfo, dispatcher: &mut EventDispatcher<Self>);
}
macro_rules! define_signals {
($($signal:ident = $repr:literal,)*) => {
pub(super) const SIGNALS: &[SignalNumber] = &[$($signal,)*];
impl<T: EventClosure> EventDispatcher<T> {
pub(super) fn new() -> io::Result<Self> {
let mut dispatcher = Self {
signal_handlers: [$(SignalHandler::new($signal).map_err(|err| {
dev_error!(
"unable to set handler for {}",
super::signal_fmt($signal)
);
err
})?,)*],
poll_set: PollSet::new(),
callbacks: Vec::with_capacity(SIGNALS.len()),
status: Status::Continue,
};
$(
let handler = &dispatcher.signal_handlers[$repr].as_raw_fd();
dispatcher.set_read_callback(handler, |closure, dispatcher| {
let handler = &mut dispatcher.signal_handlers[$repr];
if let Ok(info) = handler.recv() {
closure.on_signal(info, dispatcher);
}
});
)*
Ok(dispatcher)
}
}
};
}
define_signals! {
SIGINT = 0,
SIGQUIT = 1,
SIGTSTP = 2,
SIGTERM = 3,
SIGHUP = 4,
SIGALRM = 5,
SIGPIPE = 6,
SIGUSR1 = 7,
SIGUSR2 = 8,
SIGCHLD = 9,
SIGCONT = 10,
SIGWINCH = 11,
}
enum Status<T: EventClosure> {
Continue,
Stop(StopReason<T>),
}
impl<T: EventClosure> Status<T> {
fn is_break(&self) -> bool {
matches!(self, Self::Stop(StopReason::Break(_)))
}
fn take_stop(&mut self) -> Option<StopReason<T>> {
let status = std::mem::replace(self, Self::Continue);
match status {
Status::Continue => None,
Status::Stop(reason) => Some(reason),
}
}
fn take_break(&mut self) -> Option<T::Break> {
match self.take_stop()? {
StopReason::Break(break_reason) => Some(break_reason),
reason @ StopReason::Exit(_) => {
*self = Self::Stop(reason);
None
}
}
}
fn take_exit(&mut self) -> Option<T::Exit> {
match self.take_stop()? {
reason @ StopReason::Break(_) => {
*self = Self::Stop(reason);
None
}
StopReason::Exit(exit_reason) => Some(exit_reason),
}
}
}
pub(super) enum StopReason<T: EventClosure> {
Break(T::Break),
Exit(T::Exit),
}
#[derive(PartialEq, Eq, Hash, Clone)]
struct EventId(usize);
pub(super) type Callback<T> = fn(&mut T, &mut EventDispatcher<T>);
pub(super) struct EventDispatcher<T: EventClosure> {
signal_handlers: [SignalHandler; SIGNALS.len()],
poll_set: PollSet<EventId>,
callbacks: Vec<Callback<T>>,
status: Status<T>,
}
impl<T: EventClosure> EventDispatcher<T> {
pub(super) fn set_read_callback<F: AsRawFd>(&mut self, fd: &F, callback: Callback<T>) {
let id = EventId(self.callbacks.len());
self.poll_set.add_fd_read(id, fd);
self.callbacks.push(callback);
}
pub(super) fn set_write_callback<F: AsRawFd>(&mut self, fd: &F, callback: Callback<T>) {
let id = EventId(self.callbacks.len());
self.poll_set.add_fd_write(id, fd);
self.callbacks.push(callback);
}
pub(super) fn set_break(&mut self, reason: T::Break) {
self.status = Status::Stop(StopReason::Break(reason));
}
pub(super) fn set_exit(&mut self, reason: T::Exit) {
self.status = Status::Stop(StopReason::Exit(reason));
}
pub(super) fn got_break(&self) -> bool {
self.status.is_break()
}
pub(super) fn event_loop(&mut self, state: &mut T) -> StopReason<T> {
loop {
if let Ok(ids) = self.poll_set.poll() {
for EventId(id) in ids {
self.callbacks[id](state, self);
if let Some(reason) = self.status.take_break() {
return StopReason::Break(reason);
}
}
if let Some(reason) = self.status.take_exit() {
return StopReason::Exit(reason);
}
} else {
if let Some(reason) = self.status.take_stop() {
return reason;
}
}
}
}
pub(super) fn unregister_handlers(self) {
for handler in self.signal_handlers {
handler.unregister();
}
}
pub(super) fn set_signal_action(&mut self, signal: SignalNumber, action: SignalAction) {
if let Some(i) = SIGNALS
.iter()
.enumerate()
.find_map(|(i, &sig)| (signal == sig).then_some(i))
{
self.signal_handlers[i].set_action(action);
}
}
}