rasi_default/
time.rs

1//! The implementation of [`Timer`] syscall.
2//!
3//! You can register [`MioTimer`] to the global registry using [`register_mio_timer`], or use it alone.
4
5use std::{
6    task::{Poll, Waker},
7    time::Instant,
8};
9
10use mio::Token;
11use rasi_syscall::{register_global_timer, Handle, Timer};
12
13use crate::{reactor::global_reactor, TokenSequence};
14
15pub(crate) struct TimerHandle {
16    deadline: Instant,
17    token: Token,
18}
19
20impl TimerHandle {
21    pub(crate) fn new(token: Token, deadline: Instant) -> Handle {
22        Handle::new(TimerHandle { token, deadline })
23    }
24}
25
26/// The type that implement the syscall [`Timer`].
27#[derive(Debug, Default)]
28pub struct MioTimer {}
29
30impl Timer for MioTimer {
31    fn deadline(
32        &self,
33        waker: Waker,
34        deadline: std::time::Instant,
35    ) -> std::io::Result<Option<rasi_syscall::Handle>> {
36        let token = Token::next();
37
38        Ok(global_reactor()
39            .deadline(token, waker, deadline)
40            .map(|_| TimerHandle::new(token, deadline)))
41    }
42
43    fn timeout_wait(
44        &self,
45        waker: std::task::Waker,
46        handle: &rasi_syscall::Handle,
47    ) -> std::task::Poll<()> {
48        let time_handle = handle
49            .downcast::<TimerHandle>()
50            .expect("Expect TimeHandle.");
51
52        match global_reactor().deadline(time_handle.token, waker, time_handle.deadline) {
53            Some(_) => Poll::Pending,
54            None => Poll::Ready(()),
55        }
56    }
57}
58
59/// This function using [`register_global_timer`] to register the [`MioTimer`] to global registry.
60///
61/// So you may not call this function twice, otherwise will cause a panic. [`read more`](`register_global_timer`)
62pub fn register_mio_timer() {
63    register_global_timer(MioTimer::default())
64}
65
66#[cfg(test)]
67mod tests {
68    use std::sync::OnceLock;
69
70    use rasi_spec::timer::run_timer_spec;
71
72    use super::MioTimer;
73
74    static INIT: OnceLock<Box<dyn rasi_syscall::Timer>> = OnceLock::new();
75
76    fn get_syscall() -> &'static dyn rasi_syscall::Timer {
77        INIT.get_or_init(|| Box::new(MioTimer::default())).as_ref()
78    }
79
80    #[futures_test::test]
81    async fn test_timeout() {
82        run_timer_spec(get_syscall()).await;
83    }
84}