Skip to main content

starnix_core/vfs/
timer.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fs::fuchsia::{BootZxTimer, MonotonicZxTimer};
6use crate::power::OnWakeOps;
7use crate::task::{
8    CurrentTask, EventHandler, Kernel, SignalHandler, SignalHandlerInner, WaitCanceler, Waiter,
9};
10use crate::time::{GenericDuration, HrTimer, TargetTime, Timeline, TimerWakeup};
11use crate::vfs::buffers::{InputBuffer, OutputBuffer};
12use crate::vfs::{
13    Anon, FileHandle, FileObject, FileObjectState, FileOps, fileops_impl_nonseekable,
14    fileops_impl_noop_sync,
15};
16use futures::channel::mpsc;
17use starnix_logging::{log_debug, log_warn};
18use starnix_sync::{LockDepMutex, TimerFileInfoLock};
19use starnix_types::time::{duration_from_timespec, timespec_from_duration, timespec_is_zero};
20use starnix_uapi::errors::Errno;
21use starnix_uapi::open_flags::OpenFlags;
22use starnix_uapi::vfs::FdEvents;
23use starnix_uapi::{TFD_TIMER_ABSTIME, TFD_TIMER_CANCEL_ON_SET, error, itimerspec};
24use std::sync::{Arc, Weak};
25use zerocopy::IntoBytes;
26use zx::HandleRef;
27
28pub trait TimerOps: Send + Sync + 'static {
29    /// Starts the timer with the specified `deadline`.
30    ///
31    /// This method should start the timer and schedule it to trigger at the specified `deadline`.
32    /// The timer should be cancelled if it is already running.
33    fn start(
34        &self,
35        current_task: &CurrentTask,
36        source: Option<Weak<dyn OnWakeOps>>,
37        deadline: TargetTime,
38    ) -> Result<(), Errno>;
39
40    /// Stops the timer.
41    ///
42    /// This method should stop the timer and prevent it from triggering.
43    fn stop(&self, kernel: &Arc<Kernel>) -> Result<(), Errno>;
44
45    /// Returns a reference to the underlying Zircon handle.
46    fn as_handle_ref(&self) -> HandleRef<'_>;
47
48    /// For TimerOps that support monitoring timeline changes (e.g. timers on the
49    /// UTC timeline), this returns a an object that counts the number of timeline
50    /// changes since last reset.
51    ///
52    /// The caller must reset this value to restart the counting.
53    fn get_timeline_change_observer(
54        &self,
55        current_task: &CurrentTask,
56    ) -> Option<TimelineChangeObserver>;
57}
58
59/// Used to observe timeline changes from within TimerOps.
60#[derive(Debug)]
61pub struct TimelineChangeObserver {
62    // Stores the number of timeline changes observed since initial watch, or
63    // timeline reset.
64    timeline_change_counter: zx::Counter,
65    // Used to indicate timeline change interest.
66    timeline_change_registration: mpsc::UnboundedSender<bool>,
67}
68
69impl Drop for TimelineChangeObserver {
70    // Ensure that a lingering registration does not get retained.
71    fn drop(&mut self) {
72        self.set_timeline_change_interest(false);
73    }
74}
75
76impl TimelineChangeObserver {
77    pub fn new(
78        timeline_change_counter: zx::Counter,
79        timeline_change_registration: mpsc::UnboundedSender<bool>,
80    ) -> Self {
81        Self { timeline_change_counter, timeline_change_registration }
82    }
83
84    /// Resets the change counter, and returns the observed value.
85    ///
86    /// The reset is done in such a way that any "sets" that come while "reset" is running does not
87    /// lose a "set".
88    ///
89    /// What is expected here is that at the end either (1) COUNTER_POSITIVE is cleared, or (2) if
90    /// it happens not to be cleared due to a race, that Starnix has a way to probe the
91    /// COUNTER_POSITIVE and react *again* if it remains asserted (without being strobed to zero)
92    /// after this call returns.
93    ///
94    /// (2) happens to be true today due to how file ops work, and will likely continue to be so.
95    /// But it's a tad bit disconcerting that the correct operation of this counter depends on two
96    /// bits of code that are somewhat far away from each other. A safer alternative would be
97    /// a "counter swap" operation that would write a value *and* return the old value atomically,
98    /// but that does not exist today.
99    pub fn reset_timeline_change_counter(&self) -> i64 {
100        let counter = &self.timeline_change_counter;
101        let value = counter.read().expect("it is possible to read the counter");
102        if value != 0 {
103            // We do not write a zero here, but write a number that neutralizes the effect of
104            // `value`. Writing a zero would lose a concurrent "add" from the producer side if
105            // the add came between the `read` and `write zero`.
106            //
107            // This approach will, for the same reason, not necessarily strobe the
108            // COUNTER_POSITIVE signal, but the way Starnix handles async waits will ensure
109            // that the signal value will not be missed.
110            counter.add(-value).expect("it is possible to set the counter to zero");
111        }
112        return value;
113    }
114
115    pub fn get_timeline_change_counter_ref(&self) -> &zx::Counter {
116        &self.timeline_change_counter
117    }
118
119    pub fn set_timeline_change_interest(&mut self, is_interested: bool) {
120        // Unbounded send on an async channel is OK.
121        self.timeline_change_registration.unbounded_send(is_interested).expect("can send");
122    }
123}
124
125/// Deadline interval information for this [TimerFile].
126///
127/// When the file is read, the deadline is recomputed based on the current time and the set
128/// interval. If the interval is 0, `self.timer` is cancelled after the file is read.
129#[derive(Debug)]
130pub struct TimerFileInfo {
131    /// The timeout, expressed as a target value in the chosen timeline.
132    deadline: TargetTime,
133    /// The period for interval timer repeat. `0` for timers that do not repeat.
134    interval: zx::MonotonicDuration,
135    /// Incremented when a timeline change occurs. We must set this to zero manually
136    /// if we want to get repeated signaling. This timer is not shared with any
137    /// other `TimerFileInfo`s, so the signaling protocol should not depend on
138    /// how many timers are active.
139    timeline_change_observer: Option<TimelineChangeObserver>,
140    /// If set, this timer must return ECANCELED when read. (The read unblocks
141    /// when there are UTC timeline changes.)
142    cancel_on_set: bool,
143}
144
145impl TimerFileInfo {
146    pub fn new(next_deadline: TargetTime, interval_period: zx::MonotonicDuration) -> Self {
147        Self {
148            deadline: next_deadline,
149            interval: interval_period,
150            timeline_change_observer: None,
151            cancel_on_set: false,
152        }
153    }
154
155    pub fn reset_timeline_change_counter(&self) -> i64 {
156        if let Some(observer) = self.timeline_change_observer.as_ref() {
157            return observer.reset_timeline_change_counter();
158        } else {
159            0
160        }
161    }
162
163    pub fn set_deadline(&mut self, new_deadline: TargetTime) -> &mut Self {
164        self.deadline = new_deadline;
165        self
166    }
167
168    pub fn set_interval(&mut self, new_interval: zx::MonotonicDuration) -> &mut Self {
169        self.interval = new_interval;
170        self
171    }
172
173    /// Set the counter used for tracking changes to the underlying timeline. This counter gets
174    /// incremented on each timeline change by Starnix from within `HrTimerManager`.
175    pub fn set_timeline_change_observer(
176        &mut self,
177        observer: Option<TimelineChangeObserver>,
178    ) -> &mut Self {
179        self.timeline_change_observer = observer;
180        self
181    }
182
183    /// Mark the timer as "cancel on set". Such a timer will report ECANCELED on read if armed with
184    /// a zero valued realtime timeline, but will report data available for read when used in
185    /// epoll.
186    pub fn set_cancel_on_set(&mut self, value: bool) -> &mut Self {
187        self.cancel_on_set = value;
188        self.timeline_change_observer
189            .as_mut()
190            .map(|observer| observer.set_timeline_change_interest(value));
191        self
192    }
193}
194
195/// A `TimerFile` represents a file created by `timerfd_create`.
196///
197/// Clients can read the number of times the timer has triggered from the file. The file supports
198/// blocking reads, waiting for the timer to trigger.
199pub struct TimerFile {
200    /// The timer that is used to wait for blocking reads.
201    timer: Arc<dyn TimerOps>,
202
203    /// The type of clock this file was created with.
204    timeline: Timeline,
205
206    /// Whether this timer can wake up the system.
207    wakeup_type: TimerWakeup,
208
209    /// Details about the timeline, deadline and cancel behavior requested from this
210    /// [TimerFile].
211    timer_file_info: Arc<LockDepMutex<TimerFileInfo, TimerFileInfoLock>>,
212}
213
214impl TimerFile {
215    /// Creates a new anonymous `TimerFile` in `kernel`.
216    ///
217    /// Returns an error if the `zx::Timer` could not be created.
218    pub fn new_file(
219        current_task: &CurrentTask,
220        wakeup_type: TimerWakeup,
221        timeline: Timeline,
222        flags: OpenFlags,
223    ) -> Result<FileHandle, Errno> {
224        let timer: Arc<dyn TimerOps> = match (wakeup_type, timeline) {
225            (TimerWakeup::Regular, Timeline::Monotonic) => Arc::new(MonotonicZxTimer::new()),
226            (TimerWakeup::Regular, Timeline::BootInstant) => Arc::new(BootZxTimer::new()),
227            (TimerWakeup::Regular, Timeline::RealTime)
228            | (TimerWakeup::Alarm, Timeline::BootInstant | Timeline::RealTime) => {
229                Arc::new(HrTimer::new())
230            }
231            (TimerWakeup::Alarm, Timeline::Monotonic) => {
232                unreachable!("monotonic times cannot be alarm deadlines")
233            }
234        };
235
236        let mut timer_file_info =
237            TimerFileInfo::new(timeline.zero_time(), zx::MonotonicDuration::default());
238
239        if timeline.is_realtime() {
240            // Realtime timers must also track changes of the UTC timeline, so we configure
241            // that here. In theory we could only do this when timerfd_settime is called.
242            // However, it turns out that the appropriate wait_asyncs can be requested before
243            // the timer is started with proper flags, such as when the timer is used in
244            // `epoll`. This means we have to create this setup at timer creation.
245            timer_file_info
246                .set_timeline_change_observer(timer.get_timeline_change_observer(current_task));
247        }
248
249        Ok(Anon::new_private_file(
250            current_task,
251            Box::new(TimerFile {
252                timer,
253                timeline,
254                wakeup_type,
255                timer_file_info: Arc::new(timer_file_info.into()),
256            }),
257            flags,
258            "[timerfd]",
259        ))
260    }
261
262    pub fn wakeup_type(&self) -> TimerWakeup {
263        self.wakeup_type
264    }
265
266    /// Returns the current `itimerspec` for the file.
267    ///
268    /// The returned `itimerspec.it_value` contains the amount of time remaining until the
269    /// next timer trigger.
270    pub fn current_timer_spec(&self) -> itimerspec {
271        let (deadline, interval) = {
272            let guard = self.timer_file_info.lock();
273            (guard.deadline, guard.interval)
274        };
275        let now = self.timeline.now();
276        let remaining_time = if interval == zx::MonotonicDuration::default() && deadline <= now {
277            timespec_from_duration(zx::MonotonicDuration::default())
278        } else {
279            timespec_from_duration(
280                *deadline.delta(&now).expect("deadline and now come from same timeline"),
281            )
282        };
283
284        itimerspec { it_interval: timespec_from_duration(interval), it_value: remaining_time }
285    }
286
287    /// Sets the `itimerspec` for the timer, which will either update the associated `zx::Timer`'s
288    /// scheduled trigger or cancel the timer.
289    ///
290    /// Returns the previous `itimerspec` on success.
291    pub fn set_timer_spec(
292        &self,
293        current_task: &CurrentTask,
294        file_object: &FileObject,
295        timer_spec: itimerspec,
296        flags: u32,
297    ) -> Result<itimerspec, Errno> {
298        let mut tfi = self.timer_file_info.lock();
299        // On each timer "set", we need to figure out if we want to set this flag again, since each
300        // timer can be used in both "cancel or set" or "regular" flavors over its lifetim.  Reset
301        // it here first unconditionally, then set again below, if the right combination of
302        // settings comes along.
303        tfi.set_cancel_on_set(false);
304        let old_itimerspec = tfi.deadline.itimerspec(tfi.interval);
305
306        if timespec_is_zero(timer_spec.it_value) {
307            // Sayeth timerfd_settime(2):
308            // Setting both fields of new_value.it_value to zero disarms the timer.
309            tfi.set_deadline(self.timeline.zero_time()).set_interval(zx::MonotonicDuration::ZERO);
310            self.timer.stop(current_task.kernel())?;
311
312            // Also sayeth timerfd_settime(2):
313            // TFD_TIMER_CANCEL_ON_SET
314            // If this flag is specified along with TFD_TIMER_ABSTIME and
315            // the clock for this timer is CLOCK_REALTIME or
316            // CLOCK_REALTIME_ALARM, then mark this timer as cancelable if
317            // the real-time clock undergoes a discontinuous change
318            // (settimeofday(2), clock_settime(2), or similar).  When such
319            // changes occur, a current or future read(2) from the file
320            // descriptor will fail with the error ECANCELED.
321            if (flags & TFD_TIMER_ABSTIME != 0)
322                && (flags & TFD_TIMER_CANCEL_ON_SET != 0)
323                && self.timeline.is_realtime()
324            {
325                // This timer is configured as "cancel on set", so mark it as such
326                // to allow wait_async to be configured properly. "Cancel on set"
327                // timers don't get scheduled anywhere, they just monitor timeline
328                // changes, so we don't need to start anything here.
329                tfi.set_cancel_on_set(true);
330            }
331        } else {
332            let new_deadline = if flags & TFD_TIMER_ABSTIME != 0 {
333                // If the time_spec represents an absolute time, then treat the
334                // `it_value` as the deadline..
335                self.timeline.target_from_timespec(timer_spec.it_value)?
336            } else {
337                // .. otherwise the deadline is computed relative to the current time.
338                self.timeline.now()
339                    + GenericDuration::from(duration_from_timespec::<zx::SyntheticTimeline>(
340                        timer_spec.it_value,
341                    )?)
342            };
343            let new_interval = duration_from_timespec(timer_spec.it_interval)?;
344
345            self.timer.start(current_task, Some(file_object.weak_handle.clone()), new_deadline)?;
346            tfi.set_deadline(new_deadline).set_interval(new_interval);
347        }
348
349        Ok(old_itimerspec)
350    }
351
352    /// Returns the `zx::Signals` to listen for given `events`. Used to wait on the `TimerOps`
353    /// associated with a `TimerFile`.
354    fn get_signals_from_events(events: FdEvents) -> zx::Signals {
355        if events.contains(FdEvents::POLLIN) {
356            zx::Signals::TIMER_SIGNALED
357        } else {
358            zx::Signals::NONE
359        }
360    }
361
362    fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
363        let mut events = FdEvents::empty();
364
365        if signals.contains(zx::Signals::TIMER_SIGNALED) {
366            events |= FdEvents::POLLIN;
367        }
368        events
369    }
370
371    /// Converts the events that can happen on a `zx::Counter` to
372    /// corresponding [FdEvents] on a timer. This is used when polling
373    /// for timeline changes on the timers.
374    fn get_counter_events_from_signals(signals: zx::Signals) -> FdEvents {
375        let mut events = FdEvents::empty();
376
377        if signals.contains(zx::Signals::COUNTER_POSITIVE) {
378            events |= FdEvents::POLLIN;
379        }
380        events
381    }
382}
383
384impl FileOps for TimerFile {
385    fileops_impl_nonseekable!();
386    fileops_impl_noop_sync!();
387
388    fn close(self: Box<Self>, _file: &FileObjectState, current_task: &CurrentTask) {
389        if let Err(e) = self.timer.stop(current_task.kernel()) {
390            log_warn!("Failed to stop the timer when closing the timerfd: {e:?}");
391        }
392    }
393
394    fn write(
395        &self,
396        file: &FileObject,
397        _current_task: &CurrentTask,
398        offset: usize,
399        _data: &mut dyn InputBuffer,
400    ) -> Result<usize, Errno> {
401        debug_assert!(offset == 0);
402        // The expected error seems to vary depending on the open flags..
403        if file.flags().contains(OpenFlags::NONBLOCK) { error!(EINVAL) } else { error!(ESPIPE) }
404    }
405
406    fn read(
407        &self,
408        file: &FileObject,
409        current_task: &CurrentTask,
410        offset: usize,
411        data: &mut dyn OutputBuffer,
412    ) -> Result<usize, Errno> {
413        debug_assert!(offset == 0);
414        file.blocking_op(current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, || {
415            let mut tfi = self.timer_file_info.lock();
416            let is_cancel_on_set = tfi.cancel_on_set;
417            // A "cancel-on-set" timer, this was prepared in `set_timer_spec`. It is handled
418            // specially.
419            if is_cancel_on_set {
420                if tfi.reset_timeline_change_counter() != 0 {
421                    // Timeline has changed, we communicate that by returning ECANCELED
422                    // to the reader. `data` is ignored.
423                    return error!(ECANCELED);
424                }
425                // The timer state has not changed, tell the caller to try again later.
426                return error!(EAGAIN);
427            }
428
429            if tfi.deadline.is_zero() {
430                return error!(EAGAIN);
431            }
432
433            let now = self.timeline.now();
434            log_debug!(
435                "read:\n\tnow={now:?}\n\ttfi={:?}\n\tnow_boot={:?}",
436                zx::MonotonicInstant::get(),
437                tfi.deadline
438            );
439            if tfi.deadline > now {
440                // Defensive re-arm: `MonotonicZxTimer` and `BootZxTimer` use
441                // `TimerSlackMode::Late` so they should not fire before `tfi.deadline`, but if
442                // `self.timer` ever asserts `ZX_TIMER_SIGNALED` prematurely (for example, if a
443                // realtime timer was emulated on the boot timeline before the UTC clock started),
444                // re-arm it for `tfi.deadline` to clear `ZX_TIMER_SIGNALED`. Otherwise,
445                // `blocking_op`'s edge-triggered wait (`ZX_WAIT_ASYNC_EDGE`) would never observe a
446                // `0 -> 1` transition and would sleep forever.
447                if self
448                    .timer
449                    .as_handle_ref()
450                    .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
451                    .is_ok()
452                {
453                    self.timer.start(current_task, Some(file.weak_handle.clone()), tfi.deadline)?;
454                }
455                // The next deadline has not yet passed.
456                return error!(EAGAIN);
457            }
458
459            let count: i64 = if tfi.interval > zx::MonotonicDuration::default() {
460                let elapsed_nanos =
461                    now.delta(&tfi.deadline).expect("timelines must match").into_nanos();
462                // The number of times the timer has triggered is written to `data`.
463                let num_intervals = elapsed_nanos / tfi.interval.into_nanos() + 1;
464                let new_deadline =
465                    tfi.deadline + GenericDuration::from(tfi.interval * num_intervals);
466
467                // The timer is set to clear the `ZX_TIMER_SIGNALED` signal until the next deadline
468                // is reached.
469                self.timer.start(current_task, Some(file.weak_handle.clone()), new_deadline)?;
470                tfi.set_deadline(new_deadline);
471
472                /*count=*/
473                num_intervals
474            } else {
475                tfi.set_deadline(self.timeline.zero_time())
476                    .set_interval(zx::MonotonicDuration::ZERO)
477                    .set_cancel_on_set(false);
478                // The timer is non-repeating, so cancel the timer to clear the `ZX_TIMER_SIGNALED`
479                // signal.
480                self.timer.stop(current_task.kernel())?;
481
482                /*count=*/
483                1
484            };
485
486            data.write(count.as_bytes())
487        })
488    }
489
490    fn wait_async(
491        &self,
492        _file: &FileObject,
493        _current_task: &CurrentTask,
494        waiter: &Waiter,
495        events: FdEvents,
496        event_handler: EventHandler,
497    ) -> Option<WaitCanceler> {
498        let signal_handler = SignalHandler {
499            inner: SignalHandlerInner::ZxHandle(TimerFile::get_events_from_signals),
500            event_handler: event_handler.clone(),
501            err_code: None,
502        };
503        let canceler = waiter
504            .wake_on_zircon_signals(
505                &self.timer.as_handle_ref(),
506                TimerFile::get_signals_from_events(events),
507                signal_handler,
508            )
509            .expect("TODO return error");
510        //
511        let cancel_timeline_change = {
512            // For timers that support timeline change notifications, set up an additional wake
513            // option on the counter that tallies the occurrences of timeline changes.
514            //
515            // Note that such a counter will not get notified unless the timer is also configured
516            // to receive such notifications.
517            if !self.timeline.is_realtime() {
518                None
519            } else {
520                let guard = self.timer_file_info.lock();
521                guard.timeline_change_observer.as_ref().map(|obs| {
522                    let handler = SignalHandler {
523                        inner: SignalHandlerInner::ZxHandle(
524                            TimerFile::get_counter_events_from_signals,
525                        ),
526                        event_handler,
527                        err_code: None,
528                    };
529                    waiter
530                        .wake_on_zircon_signals(
531                            obs.get_timeline_change_counter_ref(),
532                            zx::Signals::COUNTER_POSITIVE,
533                            handler,
534                        )
535                        .expect("TODO return error")
536                })
537            }
538        };
539        let mut cancel = WaitCanceler::new_port(canceler);
540        if let Some(cancel_timeline_change) = cancel_timeline_change {
541            let cancel_timeline_change = WaitCanceler::new_port(cancel_timeline_change);
542            cancel = cancel.merge(cancel_timeline_change);
543        }
544
545        Some(cancel)
546    }
547
548    fn query_events(
549        &self,
550        file: &FileObject,
551        current_task: &CurrentTask,
552    ) -> Result<FdEvents, Errno> {
553        let guard = self.timer_file_info.lock();
554        let counter_signals = guard
555            .timeline_change_observer
556            .as_ref()
557            .map(|observer| {
558                observer
559                    .get_timeline_change_counter_ref()
560                    .wait_one(zx::Signals::COUNTER_POSITIVE, zx::Instant::ZERO)
561                    .to_result()
562            })
563            // It seems that translating errors into empty signal sets is OK.
564            .unwrap_or_else(|| Ok(zx::Signals::empty()))
565            .unwrap_or_else(|_| zx::Signals::empty());
566        let events_from_counter = TimerFile::get_counter_events_from_signals(counter_signals);
567
568        let mut timer_signals = match self
569            .timer
570            .as_handle_ref()
571            .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
572            .to_result()
573        {
574            Err(zx::Status::TIMED_OUT) => zx::Signals::empty(),
575            res => res.unwrap(),
576        };
577        // Defensive re-arm: if `self.timer` asserted `ZX_TIMER_SIGNALED` before `guard.deadline`
578        // has passed, re-arm the timer for `guard.deadline` and suppress `POLLIN` so `epoll` /
579        // `poll` callers do not wake up before a subsequent `read()` would succeed. If `start()`
580        // fails, log a warning and keep reporting readiness rather than failing `query_events()`.
581        if timer_signals.contains(zx::Signals::TIMER_SIGNALED)
582            && !guard.deadline.is_zero()
583            && guard.deadline > self.timeline.now()
584        {
585            match self.timer.start(current_task, Some(file.weak_handle.clone()), guard.deadline) {
586                Ok(()) => timer_signals.remove(zx::Signals::TIMER_SIGNALED),
587                Err(e) => log_warn!("timerfd: failed to re-arm prematurely signaled timer: {e:?}"),
588            }
589        }
590        let events_from_timer = TimerFile::get_events_from_signals(timer_signals);
591        Ok(events_from_timer | events_from_counter)
592    }
593}
594
595#[cfg(test)]
596mod tests {
597    use super::*;
598    use crate::testing::spawn_kernel_and_run;
599    use crate::vfs::buffers::VecOutputBuffer;
600    use crate::vfs::epoll::EpollFileObject;
601    use starnix_uapi::timespec;
602    use starnix_uapi::vfs::EpollEvent;
603
604    #[::fuchsia::test]
605    async fn test_zx_timers_use_late_slack() {
606        let mono = MonotonicZxTimer::new();
607        let mono_dup = mono
608            .as_handle_ref()
609            .duplicate_handle(zx::Rights::SAME_RIGHTS)
610            .expect("duplicate handle");
611        let mono_info = zx::MonotonicTimer::from(mono_dup).info().expect("timer info");
612        assert_eq!(mono_info.options, zx::sys::ZX_TIMER_SLACK_LATE);
613
614        let boot = BootZxTimer::new();
615        let boot_dup = boot
616            .as_handle_ref()
617            .duplicate_handle(zx::Rights::SAME_RIGHTS)
618            .expect("duplicate handle");
619        let boot_info = zx::BootTimer::from(boot_dup).info().expect("timer info");
620        assert_eq!(boot_info.options, zx::sys::ZX_TIMER_SLACK_LATE);
621    }
622
623    #[::fuchsia::test]
624    async fn test_timerfd_blocking_read_when_signaled_before_deadline() {
625        spawn_kernel_and_run(async |current_task| {
626            let timer_file_handle = TimerFile::new_file(
627                &current_task,
628                TimerWakeup::Regular,
629                Timeline::Monotonic,
630                OpenFlags::RDONLY,
631            )
632            .unwrap();
633            let timer_file = timer_file_handle.downcast_file::<TimerFile>().unwrap();
634
635            // First, deterministically verify with a far-future deadline that `query_events()`
636            // ignores a premature `ZX_TIMER_SIGNALED` and re-arms the underlying timer to clear
637            // the signal.
638            timer_file
639                .timer
640                .start(&current_task, None, TargetTime::Monotonic(zx::MonotonicInstant::ZERO))
641                .unwrap();
642            assert!(
643                timer_file
644                    .timer
645                    .as_handle_ref()
646                    .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
647                    .is_ok()
648            );
649            timer_file.timer_file_info.lock().set_deadline(TargetTime::Monotonic(
650                zx::MonotonicInstant::after(zx::MonotonicDuration::from_hours(1)),
651            ));
652            assert_eq!(timer_file_handle.query_events(&current_task).unwrap(), FdEvents::empty());
653            assert_eq!(
654                timer_file
655                    .timer
656                    .as_handle_ref()
657                    .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::ZERO)
658                    .to_result(),
659                Err(zx::Status::TIMED_OUT)
660            );
661
662            // Next, test a blocking `read()` when `ZX_TIMER_SIGNALED` is already asserted before
663            // `tfi.deadline`.
664            //
665            // Note on timing and flake risk:
666            // - False-failure risk: None. We run `read()` directly on `current_task` without an
667            //   artificial `recv_timeout` (relying on the test runner timeout if `read()` hangs),
668            //   and `TimerFile::read` cannot return `Ok` until `Timeline::Monotonic.now() >=
669            //   future_deadline`.
670            // - False-pass risk: If the thread is preempted for >= 50ms between computing
671            //   `future_deadline` and the `tfi.deadline > now` check inside `TimerFile::read`,
672            //   `now >= future_deadline` will already hold on the first `blocking_op` iteration,
673            //   so `read()` will succeed immediately without exercising the re-arm branch.
674            timer_file
675                .timer
676                .start(&current_task, None, TargetTime::Monotonic(zx::MonotonicInstant::ZERO))
677                .unwrap();
678            assert!(
679                timer_file
680                    .timer
681                    .as_handle_ref()
682                    .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
683                    .is_ok()
684            );
685            let future_deadline = TargetTime::Monotonic(zx::MonotonicInstant::after(
686                zx::MonotonicDuration::from_millis(50),
687            ));
688            timer_file.timer_file_info.lock().set_deadline(future_deadline);
689
690            let mut buf = VecOutputBuffer::new(8);
691            let bytes_read = timer_file_handle.read(&current_task, &mut buf).unwrap();
692            assert_eq!(bytes_read, 8);
693            assert_eq!(i64::from_ne_bytes(buf.data().try_into().unwrap()), 1);
694            assert!(Timeline::Monotonic.now() >= future_deadline);
695        })
696        .await;
697    }
698
699    #[::fuchsia::test]
700    async fn test_timerfd_epoll_and_blocking_read_with_timerslack() {
701        spawn_kernel_and_run(async |current_task| {
702            const DEADLINE_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(50);
703
704            let epoll_handle = EpollFileObject::new_file(&current_task);
705            let epoll_file = epoll_handle.downcast_file::<EpollFileObject>().unwrap();
706
707            let timer_file_handle = TimerFile::new_file(
708                &current_task,
709                TimerWakeup::Regular,
710                Timeline::Monotonic,
711                OpenFlags::RDONLY | OpenFlags::NONBLOCK,
712            )
713            .unwrap();
714
715            epoll_file
716                .add(
717                    &current_task,
718                    &timer_file_handle,
719                    &epoll_handle,
720                    EpollEvent::new(FdEvents::POLLIN, 3),
721                )
722                .unwrap();
723
724            // Set 50ms timerslack so `ZX_TIMER_SLACK_CENTER` would coalesce `[t - 50ms, t + 50ms]`.
725            current_task.write().set_timerslack_ns(DEADLINE_DURATION.into_nanos() as u64);
726
727            // Create an earlier timer at +20ms so Zircon's per-CPU timer queue has a timer inside
728            // `[deadline - slack, deadline)`.
729            //
730            // Note on timing and flake risk:
731            // - False-failure risk: None. `now` is sampled before `set_timer_spec` sets
732            //   `tfi.deadline = Timeline::Monotonic.now() + 50ms >= now + 50ms`, and the kernel
733            //   with `ZX_TIMER_SLACK_LATE` never fires the timer before `tfi.deadline`.
734            // - False-pass risk: Zircon timer queues are per-CPU, so if the thread migrates CPUs
735            //   or is descheduled for >= 20ms between `early_timer.set` and `set_timer_spec`,
736            //   `early_timer` would not coalesce with `timer_file` even if `ZX_TIMER_SLACK_CENTER`
737            //   were used. (`test_zx_timers_use_late_slack` above deterministically verifies
738            //   `ZX_TIMER_SLACK_LATE` via `ZX_INFO_TIMER` without relying on timer coalescing.)
739            let early_timer = zx::MonotonicTimer::create();
740            let now = zx::MonotonicInstant::get();
741            early_timer
742                .set(now + zx::MonotonicDuration::from_millis(20), zx::MonotonicDuration::ZERO)
743                .unwrap();
744
745            let timer_file = timer_file_handle.downcast_file::<TimerFile>().unwrap();
746            let spec = itimerspec {
747                it_interval: timespec { tv_sec: 0, tv_nsec: 0 },
748                it_value: timespec_from_duration(DEADLINE_DURATION),
749            };
750            timer_file.set_timer_spec(&current_task, &timer_file_handle, spec, 0).unwrap();
751
752            // Verify that the raw `ZX_TIMER_SIGNALED` signal does not assert before the 50ms
753            // deadline (independent of `query_events()` filtering).
754            timer_file
755                .timer
756                .as_handle_ref()
757                .wait_one(zx::Signals::TIMER_SIGNALED, zx::MonotonicInstant::INFINITE)
758                .to_result()
759                .unwrap();
760            let signaled_after = zx::MonotonicInstant::get() - now;
761            assert!(
762                signaled_after >= DEADLINE_DURATION,
763                "ZX_TIMER_SIGNALED asserted early after {signaled_after:?}"
764            );
765
766            let events = epoll_file.wait(&current_task, 1, zx::MonotonicInstant::INFINITE).unwrap();
767            assert_eq!(events.len(), 1);
768
769            let mut buf = VecOutputBuffer::new(8);
770            let bytes_read = timer_file_handle.read(&current_task, &mut buf).unwrap();
771            let elapsed = zx::MonotonicInstant::get() - now;
772            assert_eq!(bytes_read, 8);
773            assert!(elapsed >= DEADLINE_DURATION, "timerfd expired early after {elapsed:?}");
774        })
775        .await;
776    }
777}