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, SYSCALL_INSN_LEN, Thread, do_exit, get_process_data, get_process_group, get_task,
};
pub struct SyscallRestartInfo {
pub saved_a0: usize,
pub saved_sysno: usize,
}
pub fn check_signals(
thr: &Thread,
uctx: &mut UserContext,
restore_blocked: Option<SignalSet>,
restart_info: Option<&SyscallRestartInfo>,
) -> bool {
let Some((sig, os_action)) =
thr.signal
.check_signals_with(uctx, restore_blocked, |uctx, _sig, restartable| {
if let Some(info) = restart_info
&& (uctx.retval() as isize) == -(ax_errno::LinuxError::EINTR.code() as isize)
&& restartable
{
let new_ip = uctx.ip() - SYSCALL_INSN_LEN;
uctx.set_ip(new_ip);
uctx.set_arg0(info.saved_a0);
#[cfg(target_arch = "x86_64")]
uctx.set_sysno(info.saved_sysno);
#[cfg(not(target_arch = "x86_64"))]
let _ = info.saved_sysno;
}
})
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::NoFurtherAction => {}
}
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(())
}