use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use core::{
array,
ops::{Index, IndexMut},
sync::atomic::{AtomicBool, Ordering},
};
use kspin::SpinNoIrq;
use crate::{
DefaultSignalAction, PendingSignals, SignalAction, SignalActionFlags, SignalDisposition,
SignalInfo, SignalSet, Signo, api::ThreadSignalManager,
};
#[derive(Clone)]
pub struct SignalActions(pub(crate) [SignalAction; 64]);
impl Default for SignalActions {
fn default() -> Self {
Self(array::from_fn(|_| SignalAction::default()))
}
}
impl Index<Signo> for SignalActions {
type Output = SignalAction;
fn index(&self, signo: Signo) -> &SignalAction {
&self.0[signo as usize - 1]
}
}
impl IndexMut<Signo> for SignalActions {
fn index_mut(&mut self, signo: Signo) -> &mut SignalAction {
&mut self.0[signo as usize - 1]
}
}
pub struct ProcessSignalManager {
pending: SpinNoIrq<PendingSignals>,
pub actions: Arc<SpinNoIrq<SignalActions>>,
pub(crate) default_restorer: usize,
pub(crate) children: SpinNoIrq<Vec<(u32, Weak<ThreadSignalManager>)>>,
pub(crate) possibly_has_signal: AtomicBool,
}
impl ProcessSignalManager {
pub fn new(actions: Arc<SpinNoIrq<SignalActions>>, default_restorer: usize) -> Self {
Self {
pending: SpinNoIrq::new(PendingSignals::default()),
actions,
default_restorer,
children: SpinNoIrq::new(Vec::new()),
possibly_has_signal: AtomicBool::new(false),
}
}
pub(crate) fn dequeue_signal(&self, mask: &SignalSet) -> Option<SignalInfo> {
let mut guard = self.pending.lock();
let result = guard.dequeue_signal(mask);
if guard.set.is_empty() {
self.possibly_has_signal.store(false, Ordering::Release);
}
result
}
pub fn signal_ignored(&self, signo: Signo) -> bool {
match &self.actions.lock()[signo].disposition {
SignalDisposition::Ignore => true,
SignalDisposition::Default => {
matches!(signo.default_action(), DefaultSignalAction::Ignore)
}
_ => false,
}
}
pub fn can_restart(&self, signo: Signo) -> bool {
self.actions.lock()[signo]
.flags
.contains(SignalActionFlags::RESTART)
}
#[must_use]
pub fn send_signal(&self, sig: SignalInfo) -> Option<u32> {
let signo = sig.signo();
if self.signal_ignored(signo) {
return None;
}
if self.pending.lock().put_signal(sig) {
self.possibly_has_signal.store(true, Ordering::Release);
}
let mut result = None;
self.children.lock().retain(|(tid, thread)| {
if let Some(thread) = thread.upgrade() {
if result.is_none() && !thread.signal_blocked(signo) {
result = Some(*tid);
}
true
} else {
false
}
});
result
}
pub fn pending(&self) -> SignalSet {
self.pending.lock().set
}
}