async_timer/oneshot/
posix.rs

1//! Posix based timer
2
3#[cfg(feature = "no_std")]
4core::compile_error!("no_std is not supported for posix implementation");
5
6use core::future::Future;
7use core::pin::Pin;
8use core::{mem, ptr, time, task};
9
10use super::state::TimerState;
11use crate::alloc::boxed::Box;
12
13mod ffi {
14    use super::*;
15
16    #[allow(non_camel_case_types)]
17    pub type timer_t = usize;
18
19    #[inline(always)]
20    unsafe fn get_value(info: *mut libc::siginfo_t) -> *const TimerState {
21        let value = (*info).si_value();
22
23        value.sival_ptr as *const TimerState
24    }
25    pub unsafe extern "C" fn timer_handler(_sig: libc::c_int, si: *mut libc::siginfo_t, _uc: *mut libc::c_void) {
26        let state = get_value(si);
27
28        (*state).wake();
29    }
30
31    #[repr(C)]
32    pub struct itimerspec {
33        pub it_interval: libc::timespec,
34        pub it_value: libc::timespec,
35    }
36
37    extern "C" {
38        pub fn timer_create(clockid: libc::clockid_t, sevp: *mut libc::sigevent, timerid: *mut timer_t) -> libc::c_int;
39        pub fn timer_settime(timerid: timer_t, flags: libc::c_int, new_value: *const itimerspec, old_value: *mut itimerspec) -> libc::c_int;
40        pub fn timer_delete(timerid: timer_t);
41    }
42}
43
44const TIMER_SIG: libc::c_int = 40;
45
46fn init() {
47    let mut sa_mask = mem::MaybeUninit::<libc::sigset_t>::uninit();
48    unsafe {
49        libc::sigemptyset(sa_mask.as_mut_ptr());
50    }
51
52    let timer_sig = libc::sigaction {
53        sa_flags: libc::SA_SIGINFO,
54        sa_sigaction: ffi::timer_handler as usize,
55        sa_mask: unsafe { sa_mask.assume_init() },
56        #[cfg(any(target_os = "linux", target_os = "android"))]
57        sa_restorer: None,
58    };
59
60    unsafe {
61        os_assert!(libc::sigaction(TIMER_SIG, &timer_sig, ptr::null_mut()) != -1);
62    }
63}
64
65fn time_create(state: *mut TimerState) -> ffi::timer_t {
66    let mut event: libc::sigevent = unsafe { mem::zeroed() };
67
68    event.sigev_value = libc::sigval {
69        sival_ptr: state as *mut _,
70    };
71    event.sigev_signo = TIMER_SIG;
72    //NOTE: Timer handler is invoked by signal handler
73    //      Therefore all limitations are applied to your waker.
74    //      To be safe we could use thread, but in this case
75    //      we cannot really hope for it to be optimal...
76    event.sigev_notify = libc::SIGEV_SIGNAL;
77
78    let mut res = mem::MaybeUninit::<ffi::timer_t>::uninit();
79
80    unsafe {
81        os_assert!(ffi::timer_create(libc::CLOCK_MONOTONIC, &mut event, res.as_mut_ptr()) == 0);
82        res.assume_init()
83    }
84}
85
86fn set_timer_value(fd: ffi::timer_t, timeout: time::Duration) {
87    let it_value = libc::timespec {
88        tv_sec: timeout.as_secs() as libc::time_t,
89        #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
90        tv_nsec: timeout.subsec_nanos() as libc::suseconds_t,
91        #[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
92        tv_nsec: timeout.subsec_nanos() as libc::c_long,
93    };
94
95    let new_value = ffi::itimerspec {
96        it_interval: unsafe { mem::zeroed() },
97        it_value,
98    };
99
100    unsafe {
101        os_assert!(ffi::timer_settime(fd, 0, &new_value, ptr::null_mut()) == 0);
102    }
103}
104
105enum State {
106    Init(time::Duration),
107    Running(ffi::timer_t, Box<TimerState>),
108}
109
110///Posix Timer
111///
112///Currently implemented only for `Linux` and `Android` as BSD systems
113///proved to be a bit  problematic
114pub struct PosixTimer {
115    state: State,
116}
117
118impl super::Oneshot for PosixTimer {
119    fn new(timeout: time::Duration) -> Self {
120        use crate::std::sync::Once;
121        static RUNTIME: Once = Once::new();
122
123        debug_assert!(!(timeout.as_secs() == 0 && timeout.subsec_nanos() == 0), "Zero timeout makes no sense");
124
125        RUNTIME.call_once(init);
126
127        Self {
128            state: State::Init(timeout),
129        }
130    }
131
132    fn is_ticking(&self) -> bool {
133        match &self.state {
134            State::Init(_) => false,
135            State::Running(_, ref state) => !state.is_done(),
136        }
137    }
138
139    fn is_expired(&self) -> bool {
140        match &self.state {
141            State::Init(_) => false,
142            State::Running(_, ref state) => state.is_done(),
143        }
144    }
145
146    fn cancel(&mut self) {
147        match self.state {
148            State::Init(_) => (),
149            State::Running(fd, _) => unsafe {
150                ffi::timer_settime(fd, 0, &mut mem::zeroed(), ptr::null_mut());
151            }
152        }
153    }
154
155    fn restart(&mut self, new_value: time::Duration, waker: &task::Waker) {
156        debug_assert!(!(new_value.as_secs() == 0 && new_value.subsec_nanos() == 0), "Zero timeout makes no sense");
157
158        match &mut self.state {
159            State::Init(ref mut timeout) => {
160                *timeout = new_value;
161            },
162            State::Running(fd, ref mut state) => {
163                state.register(waker);
164                set_timer_value(*fd, new_value);
165            }
166        }
167    }
168}
169
170impl Future for PosixTimer {
171    type Output = ();
172
173    fn poll(mut self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
174        self.state = match &self.state {
175            State::Init(ref timeout) => {
176                let state = Box::into_raw(Box::new(TimerState::new()));
177                let fd = time_create(state);
178
179                let state = unsafe { Box::from_raw(state) };
180                state.register(ctx.waker());
181
182                set_timer_value(fd, *timeout);
183
184                State::Running(fd, state)
185            },
186            State::Running(_, ref state) => match state.is_done() {
187                false => return task::Poll::Pending,
188                true => return task::Poll::Ready(()),
189            }
190        };
191
192        task::Poll::Pending
193    }
194}
195
196impl Drop for PosixTimer {
197    fn drop(&mut self) {
198        match self.state {
199            State::Init(_) => (),
200            State::Running(fd, _) => unsafe {
201                ffi::timer_delete(fd);
202            }
203        }
204    }
205}