use core::sync::atomic::{AtomicBool, Ordering};
use ax_errno::{AxError, AxResult};
use ax_hal::uspace::UserContext;
use ax_task::{TaskInner, current};
use starry_process::Pid;
use starry_signal::{SignalInfo, SignalOSAction, SignalSet};
use super::{AsThread, Thread, do_exit, get_process_data, get_process_group, get_task};
pub fn check_signals(
thr: &Thread,
uctx: &mut UserContext,
restore_blocked: Option<SignalSet>,
) -> bool {
let Some((sig, os_action)) = thr.signal.check_signals(uctx, restore_blocked) else {
return false;
};
let signo = sig.signo();
match os_action {
SignalOSAction::Terminate => {
do_exit(signo as i32, true);
}
SignalOSAction::CoreDump => {
do_exit(128 + signo as i32, true);
}
SignalOSAction::Stop => {
do_exit(1, true);
}
SignalOSAction::Continue => {
}
SignalOSAction::Handler => {
}
}
true
}
static BLOCK_NEXT_SIGNAL_CHECK: AtomicBool = AtomicBool::new(false);
pub fn block_next_signal() {
BLOCK_NEXT_SIGNAL_CHECK.store(true, Ordering::SeqCst);
}
pub fn unblock_next_signal() -> bool {
BLOCK_NEXT_SIGNAL_CHECK.swap(false, Ordering::SeqCst)
}
pub fn with_blocked_signals<R>(
blocked: Option<SignalSet>,
f: impl FnOnce() -> AxResult<R>,
) -> AxResult<R> {
let curr = current();
let sig = &curr.as_thread().signal;
let old_blocked = blocked.map(|set| sig.set_blocked(set));
f().inspect(|_| {
if let Some(old) = old_blocked {
sig.set_blocked(old);
}
})
}
pub(super) fn send_signal_thread_inner(task: &TaskInner, thr: &Thread, sig: SignalInfo) {
if thr.signal.send_signal(sig) {
task.interrupt();
}
}
pub fn send_signal_to_thread(tgid: Option<Pid>, tid: Pid, sig: Option<SignalInfo>) -> AxResult<()> {
let task = get_task(tid)?;
let thread = task.try_as_thread().ok_or(AxError::OperationNotPermitted)?;
if tgid.is_some_and(|tgid| thread.proc_data.proc.pid() != tgid) {
return Err(AxError::NoSuchProcess);
}
if let Some(sig) = sig {
info!("Send signal {:?} to thread {}", sig.signo(), tid);
send_signal_thread_inner(&task, thread, sig);
}
Ok(())
}
pub fn send_signal_to_process(pid: Pid, sig: Option<SignalInfo>) -> AxResult<()> {
let proc_data = get_process_data(pid)?;
if let Some(sig) = sig {
let signo = sig.signo();
info!("Send signal {signo:?} to process {pid}");
if let Some(tid) = proc_data.signal.send_signal(sig)
&& let Ok(task) = get_task(tid)
{
task.interrupt();
}
}
Ok(())
}
pub fn send_signal_to_process_group(pgid: Pid, sig: Option<SignalInfo>) -> AxResult<()> {
let pg = get_process_group(pgid)?;
if let Some(sig) = sig {
info!("Send signal {:?} to process group {}", sig.signo(), pgid);
for proc in pg.processes() {
send_signal_to_process(proc.pid(), Some(sig.clone()))?;
}
}
Ok(())
}
pub fn raise_signal_fatal(sig: SignalInfo) -> AxResult<()> {
let curr = current();
let proc_data = &curr.as_thread().proc_data;
let signo = sig.signo();
info!("Send fatal signal {signo:?} to the current process");
if let Some(tid) = proc_data.signal.send_signal(sig)
&& let Ok(task) = get_task(tid)
{
task.interrupt();
} else {
do_exit(signo as i32, true);
}
Ok(())
}