use alloc::{borrow::Cow, sync::Arc};
use core::{
mem,
sync::atomic::{AtomicBool, Ordering},
task::Context,
};
use ax_errno::{AxError, AxResult};
use ax_task::{
current,
future::{block_on, poll_io},
};
use axpoll::{IoEvents, PollSet, Pollable};
use spin::RwLock;
use starry_signal::{SignalInfo, SignalSet};
use zerocopy::{Immutable, IntoBytes};
use crate::{
file::{FileLike, IoDst, IoSrc},
task::AsThread,
};
const SIGNALFD_SIGINFO_SIZE: usize = 128;
#[repr(C)]
#[derive(Immutable, IntoBytes)]
struct SignalfdSiginfo {
ssi_signo: u32, ssi_errno: i32, ssi_code: i32, ssi_pid: u32, ssi_uid: u32, ssi_fd: i32, ssi_tid: u32, ssi_band: u32, ssi_overrun: u32, ssi_trapno: u32, ssi_status: i32, ssi_int: i32, ssi_ptr: u64, ssi_utime: u64, ssi_stime: u64, ssi_addr: u64, ssi_addr_lsb: u16, _pad: [u8; 46], }
const _: [(); SIGNALFD_SIGINFO_SIZE] = [(); mem::size_of::<SignalfdSiginfo>()];
impl SignalfdSiginfo {
fn from_signal_info(sig_info: &SignalInfo) -> Self {
let errno = sig_info.errno();
SignalfdSiginfo {
ssi_signo: sig_info.signo() as u32,
ssi_errno: errno,
ssi_code: sig_info.code(),
ssi_pid: 0,
ssi_uid: 0,
ssi_fd: -1,
ssi_tid: 0,
ssi_band: 0,
ssi_overrun: 0,
ssi_trapno: 0,
ssi_status: 0,
ssi_int: 0,
ssi_ptr: 0,
ssi_utime: 0,
ssi_stime: 0,
ssi_addr: 0,
ssi_addr_lsb: 0,
_pad: [0u8; 46],
}
}
}
pub struct Signalfd {
mask: RwLock<SignalSet>,
non_blocking: AtomicBool,
poll_rx: PollSet,
}
impl Signalfd {
pub fn new(mask: SignalSet) -> Arc<Self> {
Arc::new(Self {
mask: RwLock::new(mask),
non_blocking: AtomicBool::new(false),
poll_rx: PollSet::new(),
})
}
pub fn update_mask(&self, mask: SignalSet) {
*self.mask.write() = mask;
self.poll_rx.wake();
}
fn mask(&self) -> SignalSet {
*self.mask.read()
}
fn has_pending_signals(&self) -> bool {
let mask = self.mask();
let curr = current();
let signal = &curr.as_thread().signal;
let pending = signal.pending();
!(pending & mask).is_empty()
}
fn dequeue_signal(&self) -> Option<SignalInfo> {
let mask = self.mask();
let curr = current();
let signal = &curr.as_thread().signal;
signal.dequeue_signal(&mask)
}
}
impl FileLike for Signalfd {
fn read(&self, dst: &mut IoDst) -> AxResult<usize> {
if dst.remaining_mut() < SIGNALFD_SIGINFO_SIZE {
return Err(AxError::InvalidInput);
}
block_on(poll_io(self, IoEvents::IN, self.nonblocking(), || {
if let Some(sig_info) = self.dequeue_signal() {
let sfd_info = SignalfdSiginfo::from_signal_info(&sig_info);
let bytes = sfd_info.as_bytes();
dst.write(bytes)?;
if self.has_pending_signals() {
self.poll_rx.wake();
}
Ok(SIGNALFD_SIGINFO_SIZE)
} else {
Err(AxError::WouldBlock)
}
}))
}
fn write(&self, _src: &mut IoSrc) -> AxResult<usize> {
Err(AxError::BadFileDescriptor)
}
fn nonblocking(&self) -> bool {
self.non_blocking.load(Ordering::Acquire)
}
fn set_nonblocking(&self, non_blocking: bool) -> AxResult {
self.non_blocking.store(non_blocking, Ordering::Release);
Ok(())
}
fn path(&self) -> Cow<'_, str> {
"anon_inode:[signalfd]".into()
}
}
impl Pollable for Signalfd {
fn poll(&self) -> IoEvents {
let mut events = IoEvents::empty();
events.set(IoEvents::IN, self.has_pending_signals());
events
}
fn register(&self, context: &mut Context<'_>, events: IoEvents) {
if events.contains(IoEvents::IN) {
self.poll_rx.register(context.waker());
}
}
}