//! Stage 20: timerfd (timerfd_create / settime / gettime), checked by a //! musl program (user/timerfdtest.c): one-shot, interval counts adding up, //! non-blocking EAGAIN, poll/select/epoll wake-ups, and closing timerfds //! leaves no timer and no memory behind. #![no_std] #![no_main] #![feature(custom_test_frameworks)] #![test_runner(rustbelt::test_runner)] #![reexport_test_harness_main = "test_main"] extern crate alloc; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use bootloader::{BootInfo, entry_point}; use core::panic::PanicInfo; use rustbelt::fs::file::File; use rustbelt::process::{self, ExitStatus, SpawnArgs}; use rustbelt::{fs, memory, thread, timer}; entry_point!(main); fn main(boot_info: &'static BootInfo) -> ! { rustbelt::init(); rustbelt::init_memory(boot_info); rustbelt::init_smp(); fs::init(); rustbelt::init_storage(); test_main(); rustbelt::hlt_loop(); } /// Run /mnt/disk0/bin/`name` with `args`; its exit status and stdout. fn run(name: &str, args: &[&str]) -> (ExitStatus, String) { let (reader, writer) = fs::pipe::new(); let mut spawn = SpawnArgs::kernel(name); spawn.args.extend(args.iter().map(|a| String::from(*a))); spawn.stdio[1] = Arc::new(writer); let p = process::spawn_path(&alloc::format!("/mnt/disk0/bin/{}", name), spawn).expect("spawn failed"); let mut out = Vec::new(); let mut buf = [0u8; 256]; loop { match reader.read(&mut buf).unwrap() { 0 => break, n => out.extend_from_slice(&buf[..n]), } } (process::wait(&p), String::from_utf8_lossy(&out).into_owned()) } fn counts() -> (usize, usize, usize) { thread::sleep_ticks(3); (thread::thread_count(), process::count(), memory::frames_in_use()) } /// Everything in timerfdtest passes, twice; the second run leaves exactly /// what was there before (the first one starts timerd, a kernel thread). #[test_case] fn timerfd_program_passes() { let (status, out) = run("timerfdtest", &[]); assert_eq!(status, ExitStatus::Exited(0), "output: {}", out); assert!(out.contains("timerfdtest: 0 failed"), "output: {}", out); let (timers, before) = (timer::count(), counts()); let (status, out) = run("timerfdtest", &[]); assert_eq!(status, ExitStatus::Exited(0), "output: {}", out); assert_eq!(timer::count(), timers, "a closed timerfd left its timer behind"); assert_eq!(counts(), before, "timerfdtest leaked something"); } /// Kernel side: a timerfd's timer lives exactly as long as the file. #[test_case] fn timerfd_timer_goes_with_the_file() { let before = timer::count(); let fd = timer::timerfd_new(timer::CLOCK_MONOTONIC, true); assert_eq!(timer::count(), before + 1); fd.settime(false, 60_000, 60_000); let mut buf = [0u8; 8]; assert_eq!(fd.read(&mut buf[..4]), Err(rustbelt::errno::Errno::EINVAL)); thread::sleep_ticks(6); // ~330 ms: several periods assert_eq!(fd.read(&mut buf), Ok(8)); assert!(u64::from_le_bytes(buf) >= 2, "count {}", u64::from_le_bytes(buf)); drop(fd); assert_eq!(timer::count(), before); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { rustbelt::test_panic_handler(info) }