1use core::{ptr, time, mem};
2use core::cell::Cell;
3use core::sync::atomic::{AtomicUsize, Ordering};
4use super::BoxFnPtr;
5
6extern crate alloc;
7use alloc::boxed::Box;
8
9mod ffi {
10 use core::mem;
11 pub use libc::c_void;
12 #[allow(non_camel_case_types)]
13 pub type timer_t = usize;
14
15 pub type Callback = unsafe extern "C" fn(libc::sigval);
16
17 pub unsafe extern "C" fn timer_callback(value: libc::sigval) {
18 if !value.sival_ptr.is_null() {
19 let cb: fn() -> () = mem::transmute(value.sival_ptr);
20
21 (cb)();
22 }
23 }
24
25 pub unsafe extern "C" fn timer_callback_unsafe(value: libc::sigval) {
26 if !value.sival_ptr.is_null() {
27 let cb: unsafe fn() -> () = mem::transmute(value.sival_ptr);
28
29 (cb)();
30 }
31 }
32
33 pub unsafe extern "C" fn timer_callback_generic<T: FnMut() -> ()>(value: libc::sigval) {
34 if !value.sival_ptr.is_null() {
35 let cb = &mut *(value.sival_ptr as *mut T);
36
37 (cb)();
38 }
39 }
40
41 #[repr(C)]
42 #[derive(PartialEq)]
43 pub struct timespec {
44 pub tv_sec: libc::time_t,
45 pub tv_nsec: libc::c_long,
46 }
47
48 #[repr(C)]
49 #[derive(PartialEq)]
50 pub struct itimerspec {
51 pub it_interval: timespec,
52 pub it_value: timespec,
53 }
54
55 pub const ZERO_TIMER_DURATION: itimerspec = itimerspec {
56 it_interval: timespec {
57 tv_sec: 0,
58 tv_nsec: 0
59 },
60 it_value: timespec {
61 tv_sec: 0,
62 tv_nsec: 0
63 },
64 };
65
66 extern "C" {
67 pub fn timer_settime(timerid: timer_t, flags: libc::c_int, new_value: *const itimerspec, old_value: *mut itimerspec) -> libc::c_int;
68 pub fn timer_gettime(timerid: timer_t, curr_value: *const itimerspec) -> libc::c_int;
69 pub fn timer_delete(timerid: timer_t);
70 }
71
72 #[link(name = "os-timer-posix-c", kind = "static")]
73 extern "C" {
74 pub fn posix_timer(clock: libc::c_int, cb: Callback, data: *mut libc::c_void) -> timer_t;
75 }
76}
77
78enum CallbackVariant {
79 Trivial(*mut ffi::c_void),
80 Boxed(Box<dyn FnMut()>),
81}
82
83pub struct Callback {
85 variant: CallbackVariant,
86 ffi_cb: ffi::Callback,
87}
88
89impl Callback {
90 pub unsafe fn raw(ffi_cb: ffi::Callback, data: *mut ffi::c_void) -> Self {
94 Self {
95 variant: CallbackVariant::Trivial(data),
96 ffi_cb,
97 }
98 }
99
100 pub fn plain(cb: fn()) -> Self {
102 Self {
103 variant: CallbackVariant::Trivial(cb as _),
104 ffi_cb: ffi::timer_callback,
105 }
106 }
107
108 pub fn unsafe_plain(cb: unsafe fn()) -> Self {
110 Self {
111 variant: CallbackVariant::Trivial(cb as _),
112 ffi_cb: ffi::timer_callback_unsafe,
113 }
114 }
115
116 pub fn closure<F: 'static + FnMut()>(cb: F) -> Self {
118 Self {
119 variant: CallbackVariant::Boxed(Box::new(cb)),
120 ffi_cb: ffi::timer_callback_generic::<F>,
121 }
122 }
123}
124
125pub struct Timer {
127 inner: AtomicUsize,
128 data: Cell<BoxFnPtr>,
129}
130
131impl Timer {
132 #[inline]
133 pub const unsafe fn uninit() -> Self {
137 Self {
138 inner: AtomicUsize::new(0),
139 data: Cell::new(BoxFnPtr::null()),
140 }
141 }
142
143 #[inline(always)]
144 fn get_inner(&self) -> usize {
145 let inner = self.inner.load(Ordering::Acquire);
146 debug_assert_ne!(inner, 0, "Timer has not been initialized");
147 inner
148 }
149
150 #[inline(always)]
151 pub fn is_init(&self) -> bool {
153 self.inner.load(Ordering::Acquire) != 0
154 }
155
156 #[must_use]
157 pub fn init(&self, cb: Callback) -> bool {
165 if self.is_init() {
166 return false;
167 }
168
169 let ffi_cb = cb.ffi_cb;
170 let (data, ffi_data) = match cb.variant {
171 CallbackVariant::Trivial(data) => (BoxFnPtr::null(), data),
172 CallbackVariant::Boxed(cb) => unsafe {
173 let raw = Box::into_raw(cb);
174 (BoxFnPtr(mem::transmute(raw)), raw as *mut ffi::c_void)
175 },
176 };
177
178 let handle = unsafe {
179 ffi::posix_timer(libc::CLOCK_MONOTONIC, ffi_cb, ffi_data)
180 };
181
182 match self.inner.compare_exchange(0, handle, Ordering::SeqCst, Ordering::Acquire) {
183 Ok(_) => match handle {
184 0 => false,
185 _ => {
186 self.data.set(data);
188 true
189 },
190 },
191 Err(_) => {
192 unsafe {
193 ffi::timer_delete(handle);
194 }
195 false
196 }
197 }
198 }
199
200 pub fn new(cb: Callback) -> Option<Self> {
204 let ffi_cb = cb.ffi_cb;
205 let (data, ffi_data) = match cb.variant {
206 CallbackVariant::Trivial(data) => (BoxFnPtr::null(), data),
207 CallbackVariant::Boxed(cb) => unsafe {
208 let raw = Box::into_raw(cb);
209 (BoxFnPtr(mem::transmute(raw)), raw as *mut ffi::c_void)
210 },
211 };
212
213 let handle = unsafe {
214 ffi::posix_timer(libc::CLOCK_MONOTONIC, ffi_cb, ffi_data)
215 };
216
217 if handle == 0 {
218 return None;
219 }
220
221 Some(Self {
222 inner: AtomicUsize::new(handle),
223 data: Cell::new(data),
224 })
225 }
226
227 pub fn schedule_interval(&self, timeout: time::Duration, interval: time::Duration) -> bool {
234 let it_value = ffi::timespec {
235 tv_sec: timeout.as_secs() as libc::time_t,
236 #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
237 tv_nsec: timeout.subsec_nanos() as libc::suseconds_t,
238 #[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
239 tv_nsec: timeout.subsec_nanos() as libc::c_long,
240 };
241
242 let it_interval = ffi::timespec {
243 tv_sec: interval.as_secs() as libc::time_t,
244 #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
245 tv_nsec: interval.subsec_nanos() as libc::suseconds_t,
246 #[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
247 tv_nsec: interval.subsec_nanos() as libc::c_long,
248 };
249
250 let new_value = ffi::itimerspec {
251 it_interval,
252 it_value,
253 };
254
255 unsafe {
256 ffi::timer_settime(self.get_inner(), 0, &new_value, ptr::null_mut()) == 0
257 }
258 }
259
260 #[inline]
261 pub fn is_scheduled(&self) -> bool {
266 let handle = self.get_inner();
267 let curr_value = unsafe {
268 let mut curr_value = mem::MaybeUninit::<ffi::itimerspec>::uninit();
269
270 if ffi::timer_gettime(handle, curr_value.as_mut_ptr()) != 0 {
271 return false;
272 }
273 curr_value.assume_init()
274 };
275
276 curr_value != ffi::ZERO_TIMER_DURATION
277 }
278
279 #[inline]
280 pub fn cancel(&self) {
282 if self.is_scheduled() {
283 unsafe {
284 ffi::timer_settime(self.get_inner(), 0, &mem::MaybeUninit::zeroed().assume_init(), ptr::null_mut());
285 }
286 }
287 }
288}
289
290impl Drop for Timer {
291 fn drop(&mut self) {
292 let handle = self.inner.load(Ordering::Relaxed);
293 if handle != 0 {
294 self.cancel();
295 unsafe {
296 ffi::timer_delete(handle)
297 }
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn init_plain_fn() {
308 let mut timer = unsafe {
309 Timer::uninit()
310 };
311
312 fn cb() {
313 }
314
315 let closure = || {
316 };
317
318 assert!(timer.init(Callback::plain(cb)));
319 let ptr = timer.inner.load(Ordering::Relaxed);
320 assert_ne!(ptr, 0);
321 assert!(timer.data.get_mut().is_null());
322
323 assert!(!timer.init(Callback::closure(closure)));
324 assert_ne!(ptr, 0);
325 assert_eq!(ptr, timer.inner.load(Ordering::Relaxed));
326 assert!(timer.data.get_mut().is_null());
327 }
328
329 #[test]
330 fn init_closure() {
331 let mut timer = unsafe {
332 Timer::uninit()
333 };
334
335 fn cb() {
336 }
337
338 let closure = || {
339 };
340
341 assert!(timer.init(Callback::closure(closure)));
342 let ptr = timer.inner.load(Ordering::Relaxed);
343 assert_ne!(ptr, 0);
344 assert!(!timer.data.get_mut().is_null());
345
346 assert!(!timer.init(Callback::plain(cb)));
347 assert_ne!(ptr, 0);
348 assert_eq!(ptr, timer.inner.load(Ordering::Relaxed));
349 assert!(!timer.data.get_mut().is_null());
350 }
351}