Files
RedBear-OS/redox-rt/src/signal.rs
T
2024-10-11 06:44:54 +03:00

832 lines
26 KiB
Rust

use core::{
cell::{Cell, UnsafeCell},
ffi::{c_int, c_void},
mem::MaybeUninit,
ptr::NonNull,
sync::atomic::{AtomicU8, AtomicUsize, Ordering},
};
use syscall::{
data::AtomicU64, Error, NonatomicUsize, RawAction, Result, RtSigInfo, SenderInfo,
SetSighandlerData, SigProcControl, Sigcontrol, SigcontrolFlags, TimeSpec, EAGAIN, EINTR,
EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT,
SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH, SIGXCPU,
SIGXFSZ,
};
use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb};
#[cfg(target_arch = "x86_64")]
static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
pub fn sighandler_function() -> usize {
// TODO: HWCAP?
__relibc_internal_sigentry as usize
}
/// ucontext_t representation
#[repr(C)]
pub struct SigStack {
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
))]
_pad: [usize; 1], // pad from 7*8 to 64
#[cfg(target_arch = "x86")]
_pad: [usize; 3], // pad from 9*4 to 12*4
pub link: *mut SigStack,
pub old_stack: PosixStackt,
pub old_mask: u64,
pub(crate) sival: usize,
pub(crate) sig_code: u32,
pub(crate) sig_num: u32,
// x86_64: 864 bytes
// i686: 512 bytes
// aarch64: 272 bytes (SIMD TODO)
// riscv64: 520 bytes (vector extensions TODO)
pub regs: ArchIntRegs,
}
#[repr(C)]
pub struct PosixStackt {
pub sp: *mut (),
pub flags: i32,
pub size: usize,
}
pub const SS_ONSTACK: usize = 1;
pub const SS_DISABLE: usize = 2;
impl From<Sigaltstack> for PosixStackt {
fn from(value: Sigaltstack) -> Self {
match value {
Sigaltstack::Disabled => PosixStackt {
sp: core::ptr::null_mut(),
size: 0,
flags: SS_DISABLE.try_into().unwrap(),
},
Sigaltstack::Enabled {
onstack,
base,
size,
} => PosixStackt {
sp: base.cast(),
size,
flags: if onstack {
SS_ONSTACK.try_into().unwrap()
} else {
0
},
},
}
}
}
#[repr(C)]
// TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining
// it here. Alternatively, check at compile time that the structs are equivalent.
pub struct SiginfoAbi {
pub si_signo: i32,
pub si_errno: i32,
pub si_code: i32,
pub si_pid: i32, // pid_t
pub si_uid: i32, // uid_t
pub si_addr: *mut (), // *mut c_void
pub si_status: i32,
pub si_value: usize, // sigval
}
#[inline(always)]
unsafe fn inner(stack: &mut SigStack) {
let os = &Tcb::current().unwrap().os_specific;
let stack_ptr = NonNull::from(&mut *stack);
stack.link = core::mem::replace(&mut (*os.arch.get()).last_sigstack, Some(stack_ptr))
.map_or_else(core::ptr::null_mut, |x| x.as_ptr());
let signals_were_disabled = (*os.arch.get()).disable_signals_depth > 0;
let targeted_thread_not_process = stack.sig_num >= 64;
stack.sig_num %= 64;
// asm counts from 0
stack.sig_num += 1;
let (sender_pid, sender_uid) = {
let area = &mut *os.arch.get();
// Undefined if the signal was not realtime
stack.sival = area.tmp_rt_inf.arg;
stack.old_stack = arch_pre(stack, area);
if (stack.sig_num - 1) / 32 == 1 && !targeted_thread_not_process {
stack.sig_code = area.tmp_rt_inf.code as u32;
(area.tmp_rt_inf.pid, area.tmp_rt_inf.uid)
} else {
stack.sig_code = 0; // TODO: SI_USER constant?
// TODO: Handle SIGCHLD. Maybe that should always be queued though?
let inf = SenderInfo::from_raw(area.tmp_id_inf);
(inf.pid, inf.ruid)
}
};
let sigaction = {
let guard = SIGACTIONS_LOCK.lock();
let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num as usize - 1]);
if action.flags.contains(SigactionFlags::RESETHAND) {
drop(guard);
sigaction(
stack.sig_num as u8,
Some(&Sigaction {
kind: SigactionKind::Default,
mask: 0,
flags: SigactionFlags::empty(),
}),
None,
);
}
action
};
let shall_restart = sigaction.flags.contains(SigactionFlags::RESTART);
let handler = match sigaction.kind {
SigactionKind::Ignore => {
panic!("ctl {:x?} signal {}", os.control, stack.sig_num)
}
SigactionKind::Default => {
syscall::exit(stack.sig_num as usize);
unreachable!();
}
SigactionKind::Handled { handler } => handler,
};
// Set sigmask to sa_mask and unmark the signal as pending.
let prev_sigallow = get_allowset_raw(&os.control.word);
let mut sigallow_inside = !sigaction.mask & prev_sigallow;
if !sigaction.flags.contains(SigactionFlags::NODEFER) {
sigallow_inside &= !sig_bit(stack.sig_num);
}
let _pending_when_sa_mask = set_allowset_raw(&os.control.word, prev_sigallow, sigallow_inside);
// TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first?
// Re-enable signals again.
let control_flags = &os.control.control_flags;
control_flags.store(
control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(),
Ordering::Release,
);
core::sync::atomic::compiler_fence(Ordering::Acquire);
stack.old_mask = prev_sigallow;
// Call handler, either sa_handler or sa_siginfo depending on flag.
if sigaction.flags.contains(SigactionFlags::SIGINFO)
&& let Some(sigaction) = handler.sigaction
{
let info = SiginfoAbi {
si_signo: stack.sig_num as c_int,
si_addr: core::ptr::null_mut(),
si_code: stack.sig_code as i32,
si_errno: 0,
si_pid: sender_pid as i32,
si_status: 0,
si_uid: sender_uid as i32,
si_value: stack.sival,
};
sigaction(
stack.sig_num as c_int,
core::ptr::addr_of!(info).cast(),
stack as *mut SigStack as *mut (),
);
} else if let Some(handler) = handler.handler {
handler(stack.sig_num as c_int);
}
// Disable signals while we modify the sigmask again
control_flags.store(
control_flags.load(Ordering::Relaxed) | SigcontrolFlags::INHIBIT_DELIVERY.bits(),
Ordering::Release,
);
core::sync::atomic::compiler_fence(Ordering::Acquire);
// Update allowset again.
let new_mask = stack.old_mask;
let old_mask = get_allowset_raw(&os.control.word);
let _pending_when_restored_mask = set_allowset_raw(&os.control.word, old_mask, new_mask);
// TODO: If resetting the sigmask caused signals to be unblocked, then should they be delivered
// here? And would it be possible to tail-call-optimize that?
(*os.arch.get()).last_sig_was_restart = shall_restart;
// TODO: Support setting uc_link to jump back to a different context?
(*os.arch.get()).last_sigstack = NonNull::new(stack.link);
// TODO: Support restoring uc_stack?
// And re-enable them again
if !signals_were_disabled {
core::sync::atomic::compiler_fence(Ordering::Release);
control_flags.store(
control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(),
Ordering::Relaxed,
);
}
}
#[cfg(not(target_arch = "x86"))]
pub(crate) unsafe extern "C" fn inner_c(stack: usize) {
inner(&mut *(stack as *mut SigStack))
}
#[cfg(target_arch = "x86")]
pub(crate) unsafe extern "fastcall" fn inner_fastcall(stack: usize) {
inner(&mut *(stack as *mut SigStack))
}
pub fn get_sigmask() -> Result<u64> {
let mut mask = 0;
modify_sigmask(Some(&mut mask), Option::<fn(u64) -> u64>::None)?;
Ok(mask)
}
pub fn set_sigmask(new: Option<u64>, old: Option<&mut u64>) -> Result<()> {
modify_sigmask(old, new.map(move |newmask| move |_| newmask))
}
pub fn or_sigmask(new: Option<u64>, old: Option<&mut u64>) -> Result<()> {
// Parsing nightmare... :)
modify_sigmask(
old,
new.map(move |newmask| move |oldmask| oldmask | newmask),
)
}
pub fn andn_sigmask(new: Option<u64>, old: Option<&mut u64>) -> Result<()> {
modify_sigmask(
old,
new.map(move |newmask| move |oldmask| oldmask & !newmask),
)
}
fn get_allowset_raw(words: &[AtomicU64; 2]) -> u64 {
(words[0].load(Ordering::Relaxed) >> 32) | ((words[1].load(Ordering::Relaxed) >> 32) << 32)
}
/// Sets mask from old to new, returning what was pending at the time.
fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 {
// This assumes *only this thread* can change the allowset. If this rule is broken, the use of
// fetch_add will corrupt the words entirely. fetch_add is very efficient on x86, being
// generated as LOCK XADD which is the fastest RMW instruction AFAIK.
let prev_w0 = words[0].fetch_add(
((new & 0xffff_ffff) << 32).wrapping_sub((old & 0xffff_ffff) << 32),
Ordering::Relaxed,
) & 0xffff_ffff;
let prev_w1 = words[1].fetch_add(
((new >> 32) << 32).wrapping_sub((old >> 32) << 32),
Ordering::Relaxed,
) & 0xffff_ffff;
prev_w0 | (prev_w1 << 32)
}
fn modify_sigmask(old: Option<&mut u64>, op: Option<impl FnOnce(u64) -> u64>) -> Result<()> {
let _guard = tmp_disable_signals();
let ctl = current_sigctl();
let prev = get_allowset_raw(&ctl.word);
if let Some(old) = old {
*old = !prev;
}
let Some(op) = op else {
return Ok(());
};
let next = !op(!prev);
let pending = set_allowset_raw(&ctl.word, prev, next);
// POSIX requires that at least one pending unblocked signal be delivered before
// pthread_sigmask returns, if there is one.
if pending != 0 {
unsafe {
manually_enter_trampoline();
}
}
Ok(())
}
#[derive(Clone, Copy, Default)]
pub enum SigactionKind {
#[default]
Default,
Ignore,
Handled {
handler: SignalHandler,
},
}
#[derive(Clone, Copy, Default)]
pub struct Sigaction {
pub kind: SigactionKind,
pub mask: u64,
pub flags: SigactionFlags,
}
impl Sigaction {
fn ip(&self) -> usize {
unsafe {
match self.kind {
SigactionKind::Handled { handler } => {
if self.flags.contains(SigactionFlags::SIGINFO) {
handler.sigaction.map_or(0, |a| a as usize)
} else {
handler.handler.map_or(0, |a| a as usize)
}
}
_ => 0,
}
}
}
}
const MASK_DONTCARE: u64 = !0;
fn convert_old(action: &RawAction) -> Sigaction {
let old_first = action.first.load(Ordering::Relaxed);
let old_mask = action.user_data.load(Ordering::Relaxed);
let handler = (old_first & !(u64::from(STORED_FLAGS) << 32)) as usize;
let flags = SigactionFlags::from_bits_retain(((old_first >> 32) as u32) & STORED_FLAGS);
let kind = if handler == default_handler as usize {
SigactionKind::Default
} else if flags.contains(SigactionFlags::IGNORED) {
SigactionKind::Ignore
} else {
SigactionKind::Handled {
handler: unsafe { core::mem::transmute(handler as usize) },
}
};
Sigaction {
mask: old_mask,
flags,
kind,
}
}
pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction>) -> Result<()> {
if matches!(usize::from(signal), 0 | 32 | SIGKILL | SIGSTOP | 65..) {
return Err(Error::new(EINVAL));
}
let _sigguard = tmp_disable_signals();
let ctl = current_sigctl();
let _guard = SIGACTIONS_LOCK.lock();
let action = &PROC_CONTROL_STRUCT.actions[usize::from(signal) - 1];
if let Some(old) = old {
*old = convert_old(action);
}
let Some(new) = new else {
return Ok(());
};
let explicit_handler = new.ip();
let (mask, flags, handler) = match (usize::from(signal), new.kind) {
(_, SigactionKind::Ignore) | (SIGURG | SIGWINCH, SigactionKind::Default) => {
// TODO: POSIX specifies that pending signals shall be discarded if set to SIG_IGN by
// sigaction.
// TODO: handle tmp_disable_signals
(
MASK_DONTCARE,
SigactionFlags::IGNORED,
if matches!(new.kind, SigactionKind::Default) {
default_handler as usize
} else {
0
},
)
}
// TODO: Handle pending signals before these flags are set.
(SIGTSTP | SIGTTOU | SIGTTIN, SigactionKind::Default) => (
MASK_DONTCARE,
SigactionFlags::SIG_SPECIFIC,
default_handler as usize,
),
(SIGCHLD, SigactionKind::Default) => {
let nocldstop_bit = new.flags & SigactionFlags::SIG_SPECIFIC;
(
MASK_DONTCARE,
SigactionFlags::IGNORED | nocldstop_bit,
default_handler as usize,
)
}
(_, SigactionKind::Default) => (new.mask, new.flags, default_handler as usize),
(_, SigactionKind::Handled { .. }) => (new.mask, new.flags, explicit_handler),
};
let new_first = (handler as u64) | (u64::from(flags.bits() & STORED_FLAGS) << 32);
action.first.store(new_first, Ordering::Relaxed);
action.user_data.store(mask, Ordering::Relaxed);
Ok(())
}
fn current_sigctl() -> &'static Sigcontrol {
&unsafe { Tcb::current() }.unwrap().os_specific.control
}
pub struct TmpDisableSignalsGuard {
_inner: (),
}
pub fn tmp_disable_signals() -> TmpDisableSignalsGuard {
unsafe {
let ctl = &current_sigctl().control_flags;
ctl.store(
ctl.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
Ordering::Release,
);
core::sync::atomic::compiler_fence(Ordering::Acquire);
// TODO: fence?
(*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth += 1;
}
TmpDisableSignalsGuard { _inner: () }
}
impl Drop for TmpDisableSignalsGuard {
fn drop(&mut self) {
unsafe {
let depth =
&mut (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth;
*depth -= 1;
if *depth == 0 {
let ctl = &current_sigctl().control_flags;
ctl.store(
ctl.load(Ordering::Relaxed) & !syscall::flag::INHIBIT_DELIVERY.bits(),
Ordering::Release,
);
core::sync::atomic::compiler_fence(Ordering::Acquire);
}
}
}
}
bitflags::bitflags! {
// Some flags are ignored by the rt, but they match relibc's 1:1 to simplify conversion.
#[derive(Clone, Copy, Default)]
pub struct SigactionFlags: u32 {
const NOCLDWAIT = 2;
const RESTORER = 4;
const SIGINFO = 0x0200_0000;
const ONSTACK = 0x0400_0000;
const RESTART = 0x0800_0000;
const NODEFER = 0x1000_0000;
const RESETHAND = 0x2000_0000;
const SIG_SPECIFIC = 0x4000_0000;
const IGNORED = 0x8000_0000;
}
}
const STORED_FLAGS: u32 = 0xfe00_0000;
fn default_handler(sig: c_int) {
syscall::exit(sig as usize);
}
#[derive(Clone, Copy)]
pub union SignalHandler {
pub handler: Option<extern "C" fn(c_int)>,
pub sigaction: Option<unsafe extern "C" fn(c_int, *const (), *mut ())>,
}
static SIGACTIONS_LOCK: Mutex<()> = Mutex::new(());
pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl {
pending: AtomicU64::new(0),
actions: [const {
RawAction {
first: AtomicU64::new(0),
user_data: AtomicU64::new(0),
}
}; 64],
sender_infos: [const { AtomicU64::new(0) }; 32],
};
fn combine_allowset([lo, hi]: [u64; 2]) -> u64 {
(lo >> 32) | ((hi >> 32) << 32)
}
const fn sig_bit(sig: u32) -> u64 {
//assert_ne!(sig, 32);
//assert_ne!(sig, 0);
1 << (sig - 1)
}
pub fn setup_sighandler(tcb: &RtTcb) {
{
let _guard = SIGACTIONS_LOCK.lock();
for (sig_idx, action) in PROC_CONTROL_STRUCT.actions.iter().enumerate() {
let sig = sig_idx + 1;
let bits = if matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU) {
SigactionFlags::SIG_SPECIFIC
} else if matches!(sig, SIGCHLD | SIGURG | SIGWINCH) {
SigactionFlags::IGNORED
} else {
SigactionFlags::empty()
};
action.first.store(
(u64::from(bits.bits()) << 32) | default_handler as u64,
Ordering::Relaxed,
);
}
}
let arch = unsafe { &mut *tcb.arch.get() };
{
// The asm decides whether to use the altstack, based on whether the saved stack pointer
// was already on that stack. Thus, setting the altstack to the entire address space, is
// equivalent to not using any altstack at all (the default).
arch.altstack_top = usize::MAX;
arch.altstack_bottom = 0;
// TODO
#[cfg(any(target_arch = "x86", target_arch = "aarch64", target_arch = "riscv64"))]
{
arch.pctl = core::ptr::addr_of!(PROC_CONTROL_STRUCT) as usize;
}
}
#[cfg(target_arch = "x86_64")]
{
let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx;
CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed);
SUPPORTS_AVX.store(u8::from(cpuid_eax1_ecx & 1 << 28 != 0), Ordering::Relaxed);
}
let data = current_setsighandler_struct();
let fd = FdGuard::new(
syscall::dup(**tcb.thread_fd(), b"sighandler").expect("failed to open sighandler fd"),
);
let _ = syscall::write(*fd, &data).expect("failed to write to sighandler fd");
// TODO: Inherited set of ignored signals
set_sigmask(Some(0), None);
}
pub type RtSigarea = RtTcb; // TODO
pub fn current_setsighandler_struct() -> SetSighandlerData {
SetSighandlerData {
user_handler: sighandler_function(),
excp_handler: 0, // TODO
thread_control_addr: core::ptr::addr_of!(
unsafe { Tcb::current() }.unwrap().os_specific.control
) as usize,
proc_control_addr: &PROC_CONTROL_STRUCT as *const SigProcControl as usize,
}
}
#[derive(Clone, Copy, Default, PartialEq)]
pub enum Sigaltstack {
#[default]
Disabled,
Enabled {
onstack: bool,
base: *mut (),
size: usize,
},
}
pub(crate) fn get_sigaltstack(tcb: &SigArea, sp: usize) -> Sigaltstack {
if tcb.altstack_bottom == 0 && tcb.altstack_top == usize::MAX {
Sigaltstack::Disabled
} else {
Sigaltstack::Enabled {
base: tcb.altstack_bottom as *mut (),
size: tcb.altstack_top - tcb.altstack_bottom,
onstack: (tcb.altstack_bottom..tcb.altstack_top).contains(&sp),
}
}
}
pub unsafe fn sigaltstack(
new: Option<&Sigaltstack>,
old_out: Option<&mut Sigaltstack>,
) -> Result<()> {
let _g = tmp_disable_signals();
let tcb = &mut *Tcb::current().unwrap().os_specific.arch.get();
let old = get_sigaltstack(tcb, crate::arch::current_sp());
if matches!(old, Sigaltstack::Enabled { onstack: true, .. }) && new != Some(&old) {
return Err(Error::new(EPERM));
}
if let Some(old_out) = old_out {
*old_out = old;
}
if let Some(new) = new {
match *new {
Sigaltstack::Disabled => {
tcb.altstack_bottom = 0;
tcb.altstack_top = usize::MAX;
}
Sigaltstack::Enabled { onstack: true, .. } => return Err(Error::new(EINVAL)),
Sigaltstack::Enabled {
base,
size,
onstack: false,
} => {
if size < MIN_SIGALTSTACK_SIZE {
return Err(Error::new(ENOMEM));
}
tcb.altstack_bottom = base as usize;
tcb.altstack_top = base as usize + size;
}
}
}
Ok(())
}
pub const MIN_SIGALTSTACK_SIZE: usize = 2048;
pub fn currently_pending_blocked() -> u64 {
let control = &unsafe { Tcb::current().unwrap() }.os_specific.control;
let w0 = control.word[0].load(Ordering::Relaxed);
let w1 = control.word[1].load(Ordering::Relaxed);
let allow = (w0 >> 32) | ((w1 >> 32) << 32);
let thread_pending = (w0 & 0xffff_ffff) | ((w1 >> 32) & 0xffff_ffff);
let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Relaxed);
core::sync::atomic::fence(Ordering::Acquire); // TODO: Correct ordering?
(thread_pending | proc_pending) & !allow
}
pub enum Unreachable {}
pub fn await_signal_async(inner_allowset: u64) -> Result<Unreachable> {
let _guard = tmp_disable_signals();
let control = &unsafe { Tcb::current().unwrap() }.os_specific.control;
let old_allowset = get_allowset_raw(&control.word);
set_allowset_raw(&control.word, old_allowset, inner_allowset);
let res = syscall::nanosleep(
&TimeSpec {
tv_sec: i64::MAX,
tv_nsec: 0,
},
&mut TimeSpec::default(),
);
set_allowset_raw(&control.word, inner_allowset, old_allowset);
if res == Err(Error::new(EINTR)) {
unsafe {
manually_enter_trampoline();
}
}
res?;
unreachable!()
}
// TODO: deadline-based API
pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Result<SiginfoAbi> {
let _guard = tmp_disable_signals();
let control = &unsafe { Tcb::current().unwrap() }.os_specific.control;
let old_allowset = get_allowset_raw(&control.word);
let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire);
let thread_pending = set_allowset_raw(&control.word, old_allowset, inner_allowset);
// Check if there are already signals matching the requested set, before waiting.
if let Some(info) = try_claim_multiple(proc_pending, thread_pending, inner_allowset, control) {
// TODO: RAII
set_allowset_raw(&control.word, inner_allowset, old_allowset);
return Ok(info);
}
let res = match timeout {
Some(t) => syscall::nanosleep(&t, &mut TimeSpec::default()),
None => syscall::nanosleep(
&TimeSpec {
tv_sec: i64::MAX,
tv_nsec: 0,
},
&mut TimeSpec::default(),
),
};
let thread_pending = set_allowset_raw(&control.word, inner_allowset, old_allowset);
let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire);
if let Err(error) = res
&& error.errno != EINTR
{
return Err(error);
}
// Then check if there were any signals left after waiting.
try_claim_multiple(proc_pending, thread_pending, inner_allowset, control)
// Normally ETIMEDOUT but not for sigtimedwait.
.ok_or(Error::new(EAGAIN))
}
fn try_claim_multiple(
mut proc_pending: u64,
mut thread_pending: u64,
allowset: u64,
control: &Sigcontrol,
) -> Option<SiginfoAbi> {
while (proc_pending | thread_pending) & allowset != 0 {
let sig_idx = ((proc_pending | thread_pending) & allowset).trailing_zeros();
if thread_pending & allowset & (1 << sig_idx) != 0
&& let Some(res) = try_claim_single(sig_idx, Some(control))
{
return Some(res);
}
thread_pending &= !(1 << sig_idx);
if proc_pending & allowset & (1 << sig_idx) != 0
&& let Some(res) = try_claim_single(sig_idx, None)
{
return Some(res);
}
proc_pending &= !(1 << sig_idx);
}
None
}
fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option<SiginfoAbi> {
let sig_group = sig_idx / 32;
if sig_group == 1 && thread_control.is_none() {
// Queued (realtime) signal
let mut ret = MaybeUninit::<RtSigInfo>::uninit();
let rt_inf = unsafe {
syscall::syscall2(
syscall::SYS_SIGDEQUEUE,
ret.as_mut_ptr() as usize,
sig_idx as usize - 32,
)
.ok()?;
ret.assume_init()
};
Some(SiginfoAbi {
si_signo: sig_idx as i32 + 1,
si_errno: 0,
si_code: rt_inf.code,
si_pid: rt_inf.pid as i32,
si_uid: rt_inf.uid as i32,
si_status: 0,
si_value: rt_inf.arg,
si_addr: core::ptr::null_mut(),
})
} else {
// Idempotent (standard or thread realtime) signal
let info = SenderInfo::from_raw(match thread_control {
Some(ctl) => {
// Only this thread can clear pending bits, so this will always succeed.
let info = ctl.sender_infos[sig_idx as usize].load(Ordering::Acquire);
// TODO: Ordering
ctl.word[sig_group as usize].fetch_and(!(1 << (sig_idx % 32)), Ordering::Release);
info
}
None => {
let info =
PROC_CONTROL_STRUCT.sender_infos[sig_idx as usize].load(Ordering::Acquire);
if PROC_CONTROL_STRUCT
.pending
.fetch_and(!(1 << sig_idx), Ordering::Release)
& (1 << sig_idx)
== 0
{
// already claimed
return None;
}
info
}
});
Some(SiginfoAbi {
si_signo: sig_idx as i32 + 1,
si_errno: 0,
si_code: 0, // TODO: SI_USER const?
si_pid: info.pid as i32,
si_uid: info.ruid as i32,
si_status: 0,
si_value: 0, // undefined
si_addr: core::ptr::null_mut(),
})
}
}