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:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
52
crates/kernel/src/syscall/ipc/saved.rs
Normal file
52
crates/kernel/src/syscall/ipc/saved.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user