diff --git a/src/process/thread_group/signal.rs b/src/process/thread_group/signal.rs index 5b684d6..c518e17 100644 --- a/src/process/thread_group/signal.rs +++ b/src/process/thread_group/signal.rs @@ -1,16 +1,15 @@ +use crate::{memory::uaccess::UserCopyable, sched::current::current_task}; +use bitflags::bitflags; use core::{ alloc::Layout, fmt::Display, mem::transmute, ops::{Index, IndexMut}, + task::{Poll, ready}, }; - -use bitflags::bitflags; use ksigaction::{KSignalAction, UserspaceSigAction}; use libkernel::memory::{address::UA, region::UserMemoryRegion}; -use crate::memory::uaccess::UserCopyable; - pub mod kill; pub mod ksigaction; pub mod sigaction; @@ -256,3 +255,62 @@ impl SignalActionState { } } } + +pub trait Interruptable> { + /// Mark this operation as interruptable. + /// + /// When a signal is delivered to this process/task while it is `Sleeping`, + /// it may be woken up if there are no running tasks to deliver the signal + /// to. If a task is running an `interruptable()` future, then the + /// underlying future's execution will be short-circuted by the delivery of + /// a signal. If the kernel is running a non-`interruptable()` future, then + /// the signal delivery is deferred until either an `interruptable()` + /// operation is executed or the system call has finished. + /// + /// `.await`ing a `interruptable()`-wrapped future returns a + /// [InterruptResult]. + fn interruptable(self) -> InterruptableFut; +} + +/// A wrapper for a long-running future, allowing it to be interrupted by a +/// signal. +pub struct InterruptableFut> { + sub_fut: F, +} + +impl> Interruptable for F { + fn interruptable(self) -> InterruptableFut { + // TODO: Set the task state to a new variant `Interruptable`. This + // allows the `deliver_signal` code to wake up a task to deliver a + // signal to where it will be actioned. + InterruptableFut { sub_fut: self } + } +} + +impl> Future for InterruptableFut { + type Output = InterruptResult; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + if current_task().peek_signal().is_some() { + Poll::Ready(InterruptResult::Interrupted) + } else { + unsafe { + let val = ready!(self.map_unchecked_mut(|f| &mut f.sub_fut).poll(cx)); + Poll::Ready(InterruptResult::Uninterrupted(val)) + } + } + } +} + +/// The result of running an interruptable operation within the kernel. +pub enum InterruptResult { + /// The operation was interrupted due to the delivery of the specified + /// signal. The system call would normally short-circuit and return -EINTR + /// at this point. + Interrupted, + /// The underlying future completed without interruption. + Uninterrupted(T), +}