feat(kernel): WatchDeath + SetTimer events as notification signals; adapter wakes at earliest deadline
This commit is contained in:
90
crates/kernel/src/syscall/ipc/events.rs
Normal file
90
crates/kernel/src/syscall/ipc/events.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
//! Events the kernel reports as notification signals: a watched process
|
||||
//! dying, a timer's deadline passing (see `domain::timer`).
|
||||
|
||||
use domain::{error::KernelError, resource::Resource, timer::Target};
|
||||
use platform::Platform;
|
||||
use types::{bounded::CapSlot, id::ProcessId, permission::Permission, time::Ticks};
|
||||
|
||||
use crate::Kernel;
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
/// Signalling a notification is the Write right on it.
|
||||
fn target(&self, pid: ProcessId, slot: CapSlot) -> Result<Target, KernelError> {
|
||||
let (notification, badge, generation) =
|
||||
self.notification_cap(pid, slot, Permission::Write)?;
|
||||
Ok(Target {
|
||||
notification,
|
||||
generation,
|
||||
badge,
|
||||
})
|
||||
}
|
||||
|
||||
/// Signals `target` unless its notification has died since.
|
||||
fn fire(&mut self, target: Target) {
|
||||
if self.notifications.generation(target.notification) == Some(target.generation) {
|
||||
self.signal_notification(target.notification, target.badge);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn watch_death(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
process: CapSlot,
|
||||
notification: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let watched = match self
|
||||
.check_cap(pid, process, Permission::Execute)?
|
||||
.resource()
|
||||
{
|
||||
Resource::Process(p) => *p,
|
||||
_ => return Err(KernelError::WrongObject),
|
||||
};
|
||||
let target = self.target(pid, notification)?;
|
||||
let slot = self
|
||||
.death_watchers
|
||||
.get_mut(watched.raw() as usize)
|
||||
.ok_or(KernelError::InvalidProcess(watched))?;
|
||||
*slot = Some(target);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A process is dying: tell its watcher, forget its own timer.
|
||||
pub(crate) fn report_death(&mut self, pid: ProcessId) {
|
||||
self.timers.clear(pid);
|
||||
if let Some(target) = self
|
||||
.death_watchers
|
||||
.get_mut(pid.raw() as usize)
|
||||
.and_then(Option::take)
|
||||
{
|
||||
self.fire(target);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_timer(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
notification: CapSlot,
|
||||
deadline: Ticks,
|
||||
) -> Result<(), KernelError> {
|
||||
let target = self.target(pid, notification)?;
|
||||
if deadline.raw() == 0 {
|
||||
self.timers.clear(pid);
|
||||
return Ok(());
|
||||
}
|
||||
self.timers.set(pid, deadline, target);
|
||||
self.fire_timers();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Signals every timer that is due and has this core woken for the next
|
||||
/// one. Call on every timer interrupt, whether or not a thread is running.
|
||||
pub fn fire_timers(&mut self) {
|
||||
let now = self.platform.current_ticks();
|
||||
while let Some(target) = self.timers.take_expired(now) {
|
||||
self.fire(target);
|
||||
}
|
||||
if let Some(next) = self.timers.earliest() {
|
||||
self.platform.wake_at(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Endpoint IPC, notifications and interrupt delivery (see docs/ipc.md).
|
||||
|
||||
mod endpoint;
|
||||
mod events;
|
||||
mod irq;
|
||||
mod notification;
|
||||
|
||||
|
||||
@@ -102,6 +102,20 @@ impl<P: Platform> Kernel<P> {
|
||||
self.region_address_cap(pid, region)
|
||||
.map(|pa| SyscallResult::Value(pa.raw())),
|
||||
),
|
||||
Syscall::WatchDeath {
|
||||
process,
|
||||
notification,
|
||||
} => complete(
|
||||
self.watch_death(pid, process, notification)
|
||||
.map(|()| SyscallResult::Ok),
|
||||
),
|
||||
Syscall::SetTimer {
|
||||
notification,
|
||||
deadline,
|
||||
} => complete(
|
||||
self.set_timer(pid, notification, deadline)
|
||||
.map(|()| SyscallResult::Ok),
|
||||
),
|
||||
Syscall::Delegate {
|
||||
slot,
|
||||
to_endpoint,
|
||||
|
||||
Reference in New Issue
Block a user