use crate::modules::input::Token;
use crate::{
RuntimeError,
constants::{INLINE_PAYLOAD, SIGNAL_POLL},
futures::{
net::step::settle,
signal::{
dispatch::{self, Watcher},
signal::{SignalKind, SignalReleasePolicy},
},
task::{
Nothing, Task,
sealed::{self, Park, Step},
},
},
modules::{int_check::IntCheck, park},
};
use std::{mem, time::Instant};
const _: () = assert!(mem::size_of::<Result<u32, RuntimeError>>() <= INLINE_PAYLOAD);
const _: () = assert!(mem::size_of::<Result<(), RuntimeError>>() <= INLINE_PAYLOAD);
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct SignalTask {
kind: SignalKind,
policy: SignalReleasePolicy,
seen: Option<u32>,
watch: Option<Watcher>,
}
impl SignalTask {
pub(crate) fn new(kind: SignalKind) -> Self {
Self {
kind,
policy: SignalReleasePolicy::default(),
seen: None,
watch: None,
}
}
pub fn release_policy(mut self, policy: SignalReleasePolicy) -> Self {
self.policy = policy;
self
}
fn advance(&mut self) -> Result<Step<Result<u32, RuntimeError>>, RuntimeError> {
let signo = self.kind.number();
if self.seen.is_none() {
self.watch = dispatch::catch(signo, self.policy)?;
self.seen = Some(dispatch::count(signo));
}
let seen = self.seen.unwrap_or_default();
let arrived = dispatch::count(signo);
if arrived != seen {
self.seen = Some(arrived);
return Ok(Step::Done(Ok(arrived.wrapping_sub(seen))));
}
let deadline = Instant::now() + SIGNAL_POLL;
Ok(Step::Park(Park {
ident: signo,
filter: libc::EVFILT_SIGNAL,
notes: 0,
deadline: Some(deadline),
}))
}
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct SendSignalTask {
pid: libc::pid_t,
kind: SignalKind,
}
impl SendSignalTask {
pub(crate) fn new(pid: libc::pid_t, kind: SignalKind) -> Self {
Self { pid, kind }
}
fn send(&self) -> Result<(), RuntimeError> {
if self.pid <= 0 {
return Err(RuntimeError::BadArgument);
}
let signo = self.kind.number();
dispatch::sendable(signo)?;
unsafe { libc::kill(self.pid, signo) }.check()?;
Ok(())
}
}
impl sealed::Sealed for SignalTask {}
impl sealed::Sealed for SendSignalTask {}
impl Task for SignalTask {
type Output = Result<u32, RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, reactor_id: i32, task_id: usize) -> Self::Output {
park::drive(self.clone(), reactor_id, task_id)
}
fn step(&mut self, _token: Token, _reactor_id: i32, _task_id: usize) -> Step<Self::Output> {
settle(self.advance())
}
}
impl Task for SendSignalTask {
type Output = Result<(), RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, _reactor_id: i32, _task_id: usize) -> Self::Output {
self.send()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_group_is_not_a_process() {
for pid in [0, -1, -42] {
assert_eq!(
SendSignalTask::new(pid, SignalKind::Terminate).send(),
Err(RuntimeError::BadArgument),
);
}
}
#[test]
fn a_non_signal_cannot_be_sent() {
let me = std::process::id() as libc::pid_t;
assert_eq!(
SendSignalTask::new(me, SignalKind::Other(0)).send(),
Err(RuntimeError::BadSignal),
);
}
}