Skip to main content

kernel/
timer.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use 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    /// Schedules the timer to fire at the specified deadline.
79    ///
80    /// # Safety
81    ///
82    /// The caller must ensure that:
83    /// - `callback` is a valid FFI callback that does not cause undefined behavior when executed.
84    /// - `arg` is either a valid pointer or a null pointer, and is safe to pass to `callback`.
85    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        // SAFETY: cpp_timer_set is safe as self and deadline are valid pointers.
92        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    /// Schedules the timer to fire once at the specified deadline (in nanoseconds).
99    ///
100    /// # Safety
101    ///
102    /// Same as [`Self::set_deadline`].
103    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        // SAFETY: The caller guarantees the safety of callback and arg.
111        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        // SAFETY: cpp_timer_cancel is safe as self is a valid pointer.
118        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
127// SAFETY: A Timer can be sent across threads as its underlying C++ object is thread-safe
128// (protected by TimerLock internally).
129unsafe 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/// Kernel timer tests.
139#[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        // SAFETY: arg is a valid pointer to AtomicBool.
153        let fired = unsafe { &*(arg as *const AtomicBool) };
154        fired.store(true, Ordering::Release);
155    }
156
157    /// Verifies that a scheduled oneshot timer fires and executes its callback.
158    #[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        // Schedule timer to fire in 1ms (1,000,000 ns) from now.
165        let now = unsafe { cpp_current_mono_time() };
166        // SAFETY: timer_cb and arg are valid.
167        unsafe {
168            timer.as_mut().set_oneshot(now + 1_000_000, timer_cb, arg);
169        }
170
171        // Poll up to 100ms (100 iterations of 1ms sleep) for the timer to fire.
172        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)); // 1ms
179        }
180
181        unittest::expect_true!(success);
182    }
183
184    /// Verifies that a scheduled timer can be successfully cancelled.
185    #[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        // Schedule timer to fire in 10 seconds (in the future).
192        let now = unsafe { cpp_current_mono_time() };
193        // SAFETY: timer_cb and arg are valid.
194        unsafe {
195            timer.as_mut().set_oneshot(now + 10_000_000_000, timer_cb, arg);
196        }
197
198        // Cancel it immediately.
199        let cancelled = timer.as_mut().cancel();
200        // Cancel should return true.
201        unittest::expect_true!(cancelled);
202
203        // Sleep for a short duration to ensure no callback is running.
204        let _ = crate::thread::sleep_relative(DurationMono(1_000_000)); // 1ms
205
206        // Verify that it did not fire.
207        unittest::expect_false!(fired.load(Ordering::Acquire));
208    }
209}