1use core::marker::{PhantomData, PhantomPinned};
8use pin_init::pin_data;
9use unittest as _;
10use zr::OpaqueBytes;
11
12unsafe extern "C" {
13 fn cpp_timer_init(timer: *mut core::ffi::c_void, clock_id: u32);
14 fn cpp_timer_destroy(timer: *mut Timer);
15 fn cpp_timer_set(
16 timer: *mut Timer,
17 deadline: *const Deadline,
18 callback: Callback,
19 arg: *mut core::ffi::c_void,
20 );
21 fn cpp_timer_cancel(timer: *mut Timer) -> bool;
22}
23
24pub const ZX_CLOCK_MONOTONIC: u32 = 0;
25pub const ZX_CLOCK_BOOT: u32 = 1;
26
27#[repr(u32)]
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum SlackMode {
30 Center = 0,
31 Early = 1,
32 Late = 2,
33}
34
35#[repr(C)]
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct TimerSlack {
38 pub amount: i64,
39 pub mode: SlackMode,
40}
41
42impl TimerSlack {
43 pub const fn none() -> Self {
44 Self { amount: 0, mode: SlackMode::Center }
45 }
46}
47
48#[repr(C)]
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub struct Deadline {
51 pub when: i64,
52 pub slack: TimerSlack,
53}
54
55impl Deadline {
56 pub const fn no_slack(when: i64) -> Self {
57 Self { when, slack: TimerSlack::none() }
58 }
59}
60
61pub type Callback = unsafe extern "C" fn(timer: *mut Timer, now: i64, arg: *mut core::ffi::c_void);
62
63const TIMER_SIZE: usize = 72;
64
65#[pin_data(PinnedDrop)]
66#[repr(C)]
67#[repr(align(8))]
68pub struct Timer {
69 _opaque: OpaqueBytes<TIMER_SIZE>,
70 _marker: PhantomData<PhantomPinned>,
71}
72
73impl Timer {
74 pub fn init(clock_id: u32) -> impl pin_init::PinInit<Self, core::convert::Infallible> {
75 zr::pin_init_ffi!(cpp_timer_init, clock_id)
76 }
77
78 pub unsafe fn set_deadline(
86 self: core::pin::Pin<&mut Self>,
87 deadline: &Deadline,
88 callback: Callback,
89 arg: *mut core::ffi::c_void,
90 ) {
91 unsafe {
93 let me = self.get_unchecked_mut();
94 cpp_timer_set(me as *mut Timer, deadline as *const Deadline, callback, arg);
95 }
96 }
97
98 pub unsafe fn set_oneshot(
104 mut self: core::pin::Pin<&mut Self>,
105 deadline: i64,
106 callback: Callback,
107 arg: *mut core::ffi::c_void,
108 ) {
109 let dl = Deadline::no_slack(deadline);
110 unsafe {
112 self.as_mut().set_deadline(&dl, callback, arg);
113 }
114 }
115
116 pub fn cancel(self: core::pin::Pin<&mut Self>) -> bool {
117 unsafe {
119 let me = self.get_unchecked_mut();
120 cpp_timer_cancel(me as *mut Timer)
121 }
122 }
123}
124
125zr::unsafe_pinned_drop_ffi!(Timer, cpp_timer_destroy);
126
127unsafe impl Send for Timer {}
130
131zr::static_assert!(core::mem::size_of::<TimerSlack>() == 16);
132zr::static_assert!(core::mem::align_of::<TimerSlack>() == 8);
133zr::static_assert!(core::mem::size_of::<Deadline>() == 24);
134zr::static_assert!(core::mem::align_of::<Deadline>() == 8);
135zr::static_assert!(core::mem::size_of::<Timer>() == 72);
136zr::static_assert!(core::mem::align_of::<Timer>() == 8);
137
138#[cfg(ktest)]
140#[unittest::test_suite(name = "rust_timer")]
141mod tests {
142 use super::{Timer, ZX_CLOCK_MONOTONIC};
143 use core::sync::atomic::{AtomicBool, Ordering};
144 use pin_init::stack_pin_init;
145 use platform_rs::DurationMono;
146
147 unsafe extern "C" {
148 fn cpp_current_mono_time() -> i64;
149 }
150
151 unsafe extern "C" fn timer_cb(_timer: *mut Timer, _now: i64, arg: *mut core::ffi::c_void) {
152 let fired = unsafe { &*(arg as *const AtomicBool) };
154 fired.store(true, Ordering::Release);
155 }
156
157 #[test]
159 fn test_timer_oneshot() {
160 stack_pin_init!(let timer = Timer::init(ZX_CLOCK_MONOTONIC));
161 let fired = AtomicBool::new(false);
162 let arg = &fired as *const AtomicBool as *mut core::ffi::c_void;
163
164 let now = unsafe { cpp_current_mono_time() };
166 unsafe {
168 timer.as_mut().set_oneshot(now + 1_000_000, timer_cb, arg);
169 }
170
171 let mut success = false;
173 for _ in 0..100 {
174 if fired.load(Ordering::Acquire) {
175 success = true;
176 break;
177 }
178 let _ = crate::thread::sleep_relative(DurationMono(1_000_000)); }
180
181 unittest::expect_true!(success);
182 }
183
184 #[test]
186 fn test_timer_cancel() {
187 stack_pin_init!(let timer = Timer::init(ZX_CLOCK_MONOTONIC));
188 let fired = AtomicBool::new(false);
189 let arg = &fired as *const AtomicBool as *mut core::ffi::c_void;
190
191 let now = unsafe { cpp_current_mono_time() };
193 unsafe {
195 timer.as_mut().set_oneshot(now + 10_000_000_000, timer_cb, arg);
196 }
197
198 let cancelled = timer.as_mut().cancel();
200 unittest::expect_true!(cancelled);
202
203 let _ = crate::thread::sleep_relative(DurationMono(1_000_000)); unittest::expect_false!(fired.load(Ordering::Acquire));
208 }
209}