mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-30 17:11:47 -05:00
process: signals: add interruptable()
Add a new struct, `InterruptableFut` which allows signal short-circuiting logic. If a future within the kernel's syscall logic is wrapped in a `InterruptableFut`, then a wakeup with any pending signals causes the underlying future to be dropped and it's operation cancelled. Provide a `InterruptResult` enum to allow the caller to know whether the operation was interrupted and allows them to take appropriate action. Typically exiting with `-EINTR`. Finally, provide a blanket implementation for all futures, allowing then to call `.interruptable()` to easily wrap any future.
This commit is contained in:
@@ -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<T, F: Future<Output = T>> {
|
||||
/// 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<T, F>;
|
||||
}
|
||||
|
||||
/// A wrapper for a long-running future, allowing it to be interrupted by a
|
||||
/// signal.
|
||||
pub struct InterruptableFut<T, F: Future<Output = T>> {
|
||||
sub_fut: F,
|
||||
}
|
||||
|
||||
impl<T, F: Future<Output = T>> Interruptable<T, F> for F {
|
||||
fn interruptable(self) -> InterruptableFut<T, F> {
|
||||
// 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<T, F: Future<Output = T>> Future for InterruptableFut<T, F> {
|
||||
type Output = InterruptResult<T>;
|
||||
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> Poll<Self::Output> {
|
||||
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<T> {
|
||||
/// 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),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user