feat(kernel): WatchDeath + SetTimer events as notification signals; adapter wakes at earliest deadline
This commit is contained in:
163
crates/kernel/src/tests/events.rs
Normal file
163
crates/kernel/src/tests/events.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
//! Death watches and timers: the kernel's two events, both delivered as
|
||||
//! notification signals.
|
||||
|
||||
use super::helpers::CHILD_LIMITS;
|
||||
use super::*;
|
||||
use crate::{SyscallOutcome, SyscallResult};
|
||||
use domain::fault::Fault;
|
||||
use domain::resource::Resource;
|
||||
use domain::syscall::Syscall;
|
||||
use types::address::DataRef;
|
||||
use types::bounded::{CapSlot, Priority};
|
||||
use types::time::{Ticks, TimeSlice};
|
||||
|
||||
fn child(k: &mut Kernel<MockPlatform>) -> CapSlot {
|
||||
match k.handle_syscall(Syscall::CreateProcess {
|
||||
create_cap: CapSlot::new(0).unwrap(),
|
||||
image: DataRef::empty(),
|
||||
limits: CHILD_LIMITS,
|
||||
}) {
|
||||
SyscallOutcome::Complete(SyscallResult::Slot(s)) => s,
|
||||
_ => panic!("create process"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Init with a child it watches, and the notification it watches with.
|
||||
fn watching() -> (Kernel<MockPlatform>, ProcessId, CapSlot, CapSlot) {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let proc_cap = child(&mut k);
|
||||
let ntfn = k.create_notification_cap(init).unwrap();
|
||||
k.watch_death(init, proc_cap, ntfn).unwrap();
|
||||
(k, init, proc_cap, ntfn)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_watcher_hears_a_kill() {
|
||||
let (mut k, init, proc_cap, ntfn) = watching();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
k.handle_syscall(Syscall::Kill { process: proc_cap });
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_watcher_hears_an_exit_or_a_fault() {
|
||||
let (mut k, init, proc_cap, ntfn) = watching();
|
||||
let pid = k.process_of_cap_for_test(proc_cap);
|
||||
k.destroy_process(pid);
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
|
||||
|
||||
let proc_cap = child(&mut k);
|
||||
k.watch_death(init, proc_cap, ntfn).unwrap();
|
||||
let pid = k.process_of_cap_for_test(proc_cap);
|
||||
let tid = k.spawn_thread_for_test(pid, Priority::DEFAULT, TimeSlice::DEFAULT);
|
||||
k.handle_fault(tid, Fault::IllegalInstruction, 0);
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn watching_needs_a_process_cap_and_a_notification_to_signal() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let proc_cap = child(&mut k);
|
||||
let ntfn = k.create_notification_cap(init).unwrap();
|
||||
assert!(k.watch_death(init, ntfn, ntfn).is_err());
|
||||
assert!(k.watch_death(init, proc_cap, proc_cap).is_err());
|
||||
k.handle_syscall(Syscall::Kill { process: proc_cap });
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_dead_notification_is_never_signalled() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let watched = child(&mut k);
|
||||
let watcher = child(&mut k);
|
||||
let watcher_pid = k.process_of_cap_for_test(watcher);
|
||||
// The watcher's own notification, which dies with it.
|
||||
let ntfn = k.create_notification_cap(watcher_pid).unwrap();
|
||||
let theirs = watched_in(&mut k, watched, watcher_pid);
|
||||
k.watch_death(watcher_pid, theirs, ntfn).unwrap();
|
||||
k.destroy_process(watcher_pid);
|
||||
// A new notification may now reuse the id; the old watch must not hit it.
|
||||
let fresh = k.create_notification_cap(init).unwrap();
|
||||
k.handle_syscall(Syscall::Kill { process: watched });
|
||||
assert_eq!(k.poll(init, fresh).unwrap(), 0);
|
||||
}
|
||||
|
||||
/// A copy of init's `slot` in `to`'s table.
|
||||
fn watched_in(k: &mut Kernel<MockPlatform>, slot: CapSlot, to: ProcessId) -> CapSlot {
|
||||
let pid = k.process_of_cap_for_test(slot);
|
||||
k.grant_cap_for_test(
|
||||
to,
|
||||
Resource::Process(pid),
|
||||
types::permission::PermissionSet::empty().with(types::permission::Permission::Execute),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_timer_fires_at_its_deadline_and_not_before() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let ntfn = k.create_notification_cap(init).unwrap();
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(10));
|
||||
k.set_timer(init, ntfn, Ticks::from_raw(100)).unwrap();
|
||||
assert_eq!(k.platform_mut().wake_at, Some(Ticks::from_raw(100)));
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(99));
|
||||
k.fire_timers();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(100));
|
||||
k.fire_timers();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
|
||||
// Once only.
|
||||
k.fire_timers();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_past_deadline_fires_at_once_and_zero_cancels() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let ntfn = k.create_notification_cap(init).unwrap();
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(50));
|
||||
k.set_timer(init, ntfn, Ticks::from_raw(20)).unwrap();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 1);
|
||||
k.set_timer(init, ntfn, Ticks::from_raw(60)).unwrap();
|
||||
k.set_timer(init, ntfn, Ticks::from_raw(0)).unwrap();
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(70));
|
||||
k.fire_timers();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_timer_dies_with_its_process() {
|
||||
let (mut k, init) = kernel_with_process();
|
||||
let ntfn = k.create_notification_cap(init).unwrap();
|
||||
let proc_cap = child(&mut k);
|
||||
let pid = k.process_of_cap_for_test(proc_cap);
|
||||
let theirs = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Notification(notification_id(&k, init, ntfn)),
|
||||
types::permission::PermissionSet::empty().with(types::permission::Permission::Write),
|
||||
);
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(0));
|
||||
k.set_timer(pid, theirs, Ticks::from_raw(10)).unwrap();
|
||||
k.destroy_process(pid);
|
||||
k.platform_mut().set_ticks(Ticks::from_raw(20));
|
||||
k.fire_timers();
|
||||
assert_eq!(k.poll(init, ntfn).unwrap(), 0);
|
||||
}
|
||||
|
||||
fn notification_id(
|
||||
k: &Kernel<MockPlatform>,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
) -> types::id::NotificationId {
|
||||
match k
|
||||
.processes
|
||||
.get(pid)
|
||||
.unwrap()
|
||||
.capabilities()
|
||||
.get(slot)
|
||||
.unwrap()
|
||||
.resource()
|
||||
{
|
||||
Resource::Notification(id) => *id,
|
||||
_ => panic!("not a notification"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user