feat(ipc): saved replies: SaveReply/ReplySaved (8 per thread, released on thread death, unforgeable); terminal READ saved until Enter (line.rs editor, reader queue), now pinged

This commit is contained in:
2026-10-28 02:09:34 +01:00
parent 3d69c10079
commit d04dcfffa1
19 changed files with 588 additions and 94 deletions

View File

@@ -13,7 +13,7 @@ use types::{
};
use super::super::{Delivery, DeliveryKind, SyscallOutcome, SyscallResult};
use super::Outgoing;
use super::{Outgoing, ThreadRef};
use crate::Kernel;
impl<P: Platform> Kernel<P> {
@@ -226,11 +226,17 @@ impl<P: Platform> Kernel<P> {
.ipc_mut(tid)
.and_then(|s| s.reply_to.take())
.ok_or(KernelError::NoReply)?;
self.answer(pid, caller, message);
Ok(())
}
/// Wakes `caller` with `message` from `pid`, if it is still waiting.
pub(super) fn answer(&mut self, pid: ProcessId, caller: ThreadRef, message: Message) {
let waiting = self.is_live(caller)
&& self.is_blocked_as(caller.tid, |r| *r == BlockReason::AwaitingReply);
if !waiting {
// The caller died; the reply has nowhere to go.
return Ok(());
return;
}
let to = self.thread_process(caller.tid);
// A reply may always carry a capability the server can delegate.
@@ -242,7 +248,6 @@ impl<P: Platform> Kernel<P> {
cap,
};
self.wake(caller.tid, SyscallResult::Received(delivery));
Ok(())
}
pub(crate) fn reply_recv(

View File

@@ -4,6 +4,7 @@ mod endpoint;
mod events;
mod irq;
mod notification;
mod saved;
use domain::{
error::KernelError,
@@ -44,13 +45,19 @@ pub struct IpcState {
pub(crate) outgoing: Option<Outgoing>,
/// The caller this thread owes a reply to.
pub(crate) reply_to: Option<ThreadRef>,
/// Callers it kept to answer later (`SaveReply`).
pub(crate) saved: [Option<ThreadRef>; SAVED],
pub(crate) bound: Option<NotificationId>,
}
/// Callers one thread can keep waiting.
pub(crate) const SAVED: usize = types::bounded::SavedReply::MAX as usize + 1;
impl IpcState {
pub const EMPTY: Self = Self {
outgoing: None,
reply_to: None,
saved: [None; SAVED],
bound: None,
};
}

View File

@@ -0,0 +1,52 @@
//! Replies kept for later (docs/ipc.md): a server that cannot answer a call
//! yet (it waits on the user, or on another server) keeps the caller and
//! receives others meanwhile. The kept caller is named only within the
//! thread that kept it, so it can be neither passed on nor forged, and it
//! is released with an error when that thread dies.
use domain::{error::KernelError, message::Message};
use platform::Platform;
use types::{
bounded::SavedReply,
id::{ProcessId, ThreadId},
};
use crate::Kernel;
impl<P: Platform> Kernel<P> {
/// Moves the caller `tid` owes a reply to into its first free slot.
pub(crate) fn save_reply(&mut self, tid: ThreadId) -> Result<SavedReply, KernelError> {
let s = self.ipc_mut(tid).ok_or(KernelError::NoReply)?;
let caller = s.reply_to.ok_or(KernelError::NoReply)?;
let (index, free) = s
.saved
.iter_mut()
.enumerate()
.find(|(_, c)| c.is_none())
.ok_or(KernelError::NoFreeSlot)?;
let reply = u8::try_from(index)
.ok()
.and_then(SavedReply::new)
.ok_or(KernelError::NoFreeSlot)?;
*free = Some(caller);
s.reply_to = None;
Ok(reply)
}
/// Answers the caller kept as `reply`; it is forgotten either way.
pub(crate) fn reply_saved(
&mut self,
tid: ThreadId,
pid: ProcessId,
reply: SavedReply,
message: Message,
) -> Result<(), KernelError> {
let caller = self
.ipc_mut(tid)
.and_then(|s| s.saved.get_mut(usize::from(reply.value())))
.and_then(Option::take)
.ok_or(KernelError::NoReply)?;
self.answer(pid, caller, message);
Ok(())
}
}

View File

@@ -138,6 +138,14 @@ impl<P: Platform> Kernel<P> {
Syscall::ReplyRecv { endpoint, message } => {
self.reply_recv(tid, pid, endpoint, message)
}
Syscall::SaveReply => complete(
self.save_reply(tid)
.map(|r| SyscallResult::Value(usize::from(r.value()))),
),
Syscall::ReplySaved { reply, message } => complete(
self.reply_saved(tid, pid, reply, message)
.map(|()| SyscallResult::Ok),
),
Syscall::CreateNotification => {
complete(self.create_notification_cap(pid).map(SyscallResult::Slot))
}