use super::Concurrent;
use crate::signal::Number;
use crate::system::{Disposition, Errno, Sigaction, Sigmask, SigmaskOp};
use crate::trap::SignalSystem;
use std::rc::Rc;
impl<S> SignalSystem for Rc<Concurrent<S>>
where
S: Sigmask + Sigaction,
{
fn get_disposition(&self, signal: Number) -> Result<Disposition, Errno> {
self.inner.get_sigaction(signal)
}
fn set_disposition(
&self,
signal: Number,
disposition: Disposition,
) -> impl Future<Output = Result<Disposition, Errno>> + use<S> {
let this = Rc::clone(self);
async move {
if disposition == Disposition::Catch {
this.update_sigmask_and_select_mask(SigmaskOp::Add, signal)
.await?;
}
let old_action = this.inner.sigaction(signal, disposition)?;
if disposition != Disposition::Catch {
this.update_sigmask_and_select_mask(SigmaskOp::Remove, signal)
.await?;
}
Ok(old_action)
}
}
}
impl<S> Concurrent<S>
where
S: Sigmask,
{
async fn update_sigmask_and_select_mask(
&self,
op: SigmaskOp,
signal: Number,
) -> Result<(), Errno> {
let mut old_mask = Vec::new();
self.inner
.sigmask(Some((op, &[signal])), Some(&mut old_mask))
.await?;
self.state
.borrow_mut()
.select_mask
.get_or_insert(old_mask)
.retain(|&s| s != signal);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::job::{ProcessResult, ProcessState};
use crate::system::SendSignal as _;
use crate::system::r#virtual::{SIGQUIT, SIGTERM, SIGUSR1, VirtualSystem};
use futures_util::FutureExt as _;
use std::num::NonZero;
#[test]
fn setting_disposition_from_default_to_catch() {
let inner = VirtualSystem::new();
let system = Rc::new(Concurrent::new(inner.clone()));
let result = system
.set_disposition(SIGTERM, Disposition::Catch)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(Disposition::Default));
assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Catch));
inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
assert_eq!(inner.current_process().state(), ProcessState::Running);
}
#[test]
fn setting_disposition_from_default_to_ignore() {
let inner = VirtualSystem::new();
let system = Rc::new(Concurrent::new(inner.clone()));
let result = system
.set_disposition(SIGTERM, Disposition::Ignore)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(Disposition::Default));
assert_eq!(system.get_disposition(SIGTERM), Ok(Disposition::Ignore));
inner.raise(SIGTERM).now_or_never().unwrap().unwrap();
assert_eq!(inner.current_process().state(), ProcessState::Running);
}
#[test]
fn setting_disposition_from_ignore_to_catch() {
let system = Rc::new(Concurrent::new(VirtualSystem::new()));
system
.set_disposition(SIGQUIT, Disposition::Ignore)
.now_or_never()
.unwrap()
.unwrap();
let result = system
.set_disposition(SIGQUIT, Disposition::Catch)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(Disposition::Ignore));
assert_eq!(system.get_disposition(SIGQUIT), Ok(Disposition::Catch));
}
#[test]
fn setting_disposition_from_catch_to_default() {
let inner = VirtualSystem::new();
let system = Rc::new(Concurrent::new(inner.clone()));
system
.set_disposition(SIGQUIT, Disposition::Catch)
.now_or_never()
.unwrap()
.unwrap();
system.raise(SIGQUIT).now_or_never().unwrap().unwrap();
let result = system
.set_disposition(SIGQUIT, Disposition::Default)
.now_or_never();
assert_eq!(result, None);
assert_eq!(
inner.current_process().state(),
ProcessState::Halted(ProcessResult::Signaled {
signal: SIGQUIT,
core_dump: true
})
);
}
#[test]
fn first_update_sigmask_and_select_mask_updates_blocking_mask() {
let inner = VirtualSystem::new();
_ = inner
.current_process_mut()
.block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
let system = Rc::new(Concurrent::new(inner.clone()));
let result = system
.update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
.now_or_never()
.unwrap();
assert_eq!(result, Ok(()));
let blocked_signals = inner
.current_process()
.blocked_signals()
.iter()
.copied()
.collect::<Vec<_>>();
assert_eq!(blocked_signals, [SIGQUIT, SIGTERM, SIGUSR1]);
}
#[test]
fn first_update_sigmask_and_select_mask_sets_select_mask() {
let inner = VirtualSystem::new();
_ = inner
.current_process_mut()
.block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
let system = Rc::new(Concurrent::new(inner.clone()));
system
.update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(
system.state.borrow().select_mask.as_deref(),
Some([SIGQUIT, SIGUSR1].as_slice())
);
}
#[ignore = "current VirtualSystem::sigmask silently ignores invalid signals"]
#[test]
fn first_update_sigmask_and_select_mask_leaves_select_mask_unchanged_on_error() {
let system = Rc::new(Concurrent::new(VirtualSystem::new()));
let invalid_signal = Number::from_raw_unchecked(NonZero::new(-1).unwrap());
let result = system
.update_sigmask_and_select_mask(SigmaskOp::Add, invalid_signal)
.now_or_never()
.unwrap();
assert_eq!(result, Err(Errno::EINVAL));
assert_eq!(system.state.borrow().select_mask.as_deref(), None);
}
#[test]
fn second_update_sigmask_and_select_mask_updates_select_mask() {
let inner = VirtualSystem::new();
_ = inner
.current_process_mut()
.block_signals(SigmaskOp::Set, &[SIGQUIT, SIGTERM, SIGUSR1]);
let system = Rc::new(Concurrent::new(inner.clone()));
system
.update_sigmask_and_select_mask(SigmaskOp::Add, SIGTERM)
.now_or_never()
.unwrap()
.unwrap();
system
.update_sigmask_and_select_mask(SigmaskOp::Remove, SIGQUIT)
.now_or_never()
.unwrap()
.unwrap();
assert_eq!(
system.state.borrow().select_mask.as_deref(),
Some([SIGUSR1].as_slice())
);
}
}