182 lines
6.4 KiB
Rust
182 lines
6.4 KiB
Rust
//! 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"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn every_watcher_hears_and_there_is_room_for_a_few() {
|
|
let (mut k, init, proc_cap, first) = watching();
|
|
let second = k.create_notification_cap(init).unwrap();
|
|
k.watch_death(init, proc_cap, second).unwrap();
|
|
// The same notification again takes no new place.
|
|
k.watch_death(init, proc_cap, second).unwrap();
|
|
for _ in 2..domain::timer::MAX_WATCHERS {
|
|
let n = k.create_notification_cap(init).unwrap();
|
|
k.watch_death(init, proc_cap, n).unwrap();
|
|
}
|
|
let extra = k.create_notification_cap(init).unwrap();
|
|
assert!(k.watch_death(init, proc_cap, extra).is_err());
|
|
k.handle_syscall(Syscall::Kill { process: proc_cap });
|
|
assert_eq!(k.poll(init, first).unwrap(), 1);
|
|
assert_eq!(k.poll(init, second).unwrap(), 1);
|
|
}
|