use alloc::{borrow::Cow, sync::Arc};
use core::{
mem,
sync::atomic::{AtomicBool, Ordering},
};
use axpoll::{IoEvents, Pollable};
use axpoll_set::PollSet;
use starry_signal::{SignalInfo, SignalSet};
use zerocopy::{Immutable, IntoBytes};
use crate::{
StarryError, StarryResult,
file::{FileLike, IoDst, IoSrc},
sync::IrqMutex,
task::{
current_user_task,
future::{block_on_user, poll_io},
},
};
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: sig_info.pid(),
ssi_uid: sig_info.uid(),
ssi_fd: -1,
ssi_tid: 0,
ssi_band: 0,
ssi_overrun: sig_info.timer_overrun(),
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: IrqMutex<SignalSet>,
non_blocking: AtomicBool,
poll_rx: PollSet,
}
impl Signalfd {
pub fn new(mask: SignalSet) -> Arc<Self> {
Arc::new(Self {
mask: IrqMutex::new(mask),
non_blocking: AtomicBool::new(false),
poll_rx: PollSet::new(),
})
}
pub fn update_mask(&self, mask: SignalSet) {
{
*self.mask.lock() = mask;
}
unsafe { self.poll_rx.wake(IoEvents::IN) };
}
fn mask(&self) -> SignalSet {
*self.mask.lock()
}
fn has_pending_signals(&self) -> bool {
let mask = self.mask();
let curr = current_user_task();
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_user_task();
let signal = curr.as_thread().signal();
signal.dequeue_signal(&mask)
}
}
impl FileLike for Signalfd {
fn read(&self, dst: &mut IoDst) -> StarryResult<usize> {
if dst.remaining_mut() < SIGNALFD_SIGINFO_SIZE {
return Err(StarryError::InvalidInput);
}
let task = current_user_task();
block_on_user(
&task,
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() {
unsafe { self.poll_rx.wake(IoEvents::IN) };
}
Ok(SIGNALFD_SIGINFO_SIZE)
} else {
Err(crate::StarryError::WouldBlock)
}
}),
)
.into_result()?
}
fn write(&self, _src: &mut IoSrc) -> StarryResult<usize> {
Err(StarryError::BadFileDescriptor)
}
fn nonblocking(&self) -> bool {
self.non_blocking.load(Ordering::Acquire)
}
fn set_nonblocking(&self, non_blocking: bool) -> StarryResult {
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
}
unsafe fn register_shared(
&self,
sink: &mut dyn axpoll::SharedRegistrationSink,
events: IoEvents,
) {
if events.contains(IoEvents::IN) {
unsafe {
sink.register_shared(&self.poll_rx, IoEvents::IN);
sink.register_shared(
current_user_task().as_thread().signalfd_poll_source(),
IoEvents::IN,
);
}
}
}
unsafe fn register_exclusive(
&self,
sink: &mut dyn axpoll::ExclusiveRegistrationSink,
events: IoEvents,
) {
if events.contains(IoEvents::IN) {
unsafe {
sink.register_exclusive(&self.poll_rx, IoEvents::IN);
sink.register_exclusive(
current_user_task().as_thread().signalfd_poll_source(),
IoEvents::IN,
);
}
}
}
}