Skip to main content

starnix_core/time/
interval_timer.rs

1// Copyright 2023 The Fuchsia Authors
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::power::OnWakeOps;
6use crate::signals::{SignalDetail, SignalEvent, SignalEventNotify, SignalInfo, send_signal};
7use crate::task::{CurrentTask, Kernel, ThreadGroup};
8use crate::time::utc::{estimate_boot_deadline_from_utc, utc_now};
9use crate::time::{
10    GenericDuration, HrTimer, HrTimerHandle, TargetTime, Timeline, TimerId, TimerWakeup,
11};
12use crate::vfs::timer::TimerOps;
13use assert_matches::assert_matches;
14use fuchsia_runtime::UtcInstant;
15use futures::channel::mpsc;
16use futures::stream::AbortHandle;
17use futures::{FutureExt, StreamExt, select};
18use starnix_logging::{log_debug, log_error, log_trace, log_warn, track_stub};
19use starnix_sync::{IntervalTimerState, LockDepMutex};
20use starnix_types::time::{duration_from_timespec, timespec_from_duration};
21use starnix_uapi::errors::Errno;
22use starnix_uapi::{SI_TIMER, itimerspec};
23use std::fmt::Debug;
24use std::ops::DerefMut;
25use std::pin::pin;
26use std::sync::{Arc, Weak};
27
28#[derive(Default)]
29pub struct TimerRemaining {
30    /// Remaining time until the next expiration.
31    pub remainder: zx::SyntheticDuration,
32    /// Interval for periodic timer.
33    pub interval: zx::SyntheticDuration,
34}
35
36impl From<TimerRemaining> for itimerspec {
37    fn from(value: TimerRemaining) -> Self {
38        Self {
39            it_interval: timespec_from_duration(value.interval),
40            it_value: timespec_from_duration(value.remainder),
41        }
42    }
43}
44
45#[derive(Debug)]
46pub struct IntervalTimer {
47    pub timer_id: TimerId,
48
49    /// HrTimer to trigger wakeup
50    hr_timer: Option<HrTimerHandle>,
51
52    timeline: Timeline,
53
54    pub signal_event: SignalEvent,
55
56    state: LockDepMutex<IntervalTimerMutableState, IntervalTimerState>,
57}
58pub type IntervalTimerHandle = Arc<IntervalTimer>;
59
60/// Emulates waiting on the UTC timeline for the interval timer.
61///
62/// Combines two functionalities offered by the Fuchsia runtime's timer
63/// and the wake alarm:
64/// * Fuchsia's runtime timer can wake process after a wait,
65/// * Wake alarm can wake the system after a period expires.
66///
67/// The UtcWaiter combines the two to ensure that once [UtcWaiter.wait()]
68/// returns, the correct amount of UTC (wall clock) time has expired.
69#[derive(Debug)]
70struct UtcWaiter {
71    // Used in `on_wake` below.
72    send: mpsc::UnboundedSender<()>,
73    // Call to obtain the current UtcInstant. Injected in tests.
74    utc_now_fn: fn() -> UtcInstant,
75}
76
77impl OnWakeOps for UtcWaiter {
78    // This fn is called when a wake alarm expires. [UtcWaiter] must be
79    // submitted to HrTimerManager for that to happen.
80    fn on_wake(&self, _: &CurrentTask, _: &zx::NullableHandle) {
81        self.on_wake_internal()
82    }
83}
84
85impl UtcWaiter {
86    /// Creates a new UtcWaiter.
87    ///
88    /// Await on `UtcWaiter::wait` to pass the time.
89    ///
90    /// # Returns
91    /// A pair of:
92    /// * `Self`: the waiter itself. Call `UtcWaiter::wait` on it.
93    /// * `UnboundedSender<_>`: a channel used for async notification from the
94    /// wake alarm subsystem.  Feed it into `UtcWaiter::wait`.
95    pub fn new() -> (Self, mpsc::UnboundedReceiver<()>) {
96        Self::new_internal(utc_now)
97    }
98
99    fn on_wake_internal(&self) {
100        self.send
101            .unbounded_send(())
102            // This is a common occurrence if the timer has been destroyed
103            // before we get to `unbounded_send` for it.
104            .inspect_err(|err| log_warn!("UtcWaiter::on_wake: {err:?}"))
105            .unwrap_or(());
106    }
107
108    fn new_internal(clock_fn: fn() -> UtcInstant) -> (Self, mpsc::UnboundedReceiver<()>) {
109        let (send, recv) = mpsc::unbounded();
110        // Returning recv instead of adding to Self avoids the need to take a Mutex.
111        (Self { send, utc_now_fn: clock_fn }, recv)
112    }
113
114    /// Awaits until `deadline` expires. Get `utc_signal` from a call to `UtcWaiter::new()`.
115    pub async fn wait(&self, deadline: UtcInstant, mut utc_signal: mpsc::UnboundedReceiver<()>) {
116        loop {
117            let mut utc_wait_fut = utc_signal.next().fuse();
118            let (deadline_boot, _) = estimate_boot_deadline_from_utc(deadline);
119            let mut boot_wait_fut = pin!(fuchsia_async::Timer::new(deadline_boot));
120            log_debug!(
121                "UtcWaiter::wait: waiting for: deadline_utc={:?}, deadline_boot={:?}",
122                deadline,
123                deadline_boot
124            );
125
126            // The UTC deadline can move. Initially, the UTC and the boot deadline
127            // are in sync. But if after starting the wait the UTC timeline changes,
128            // the actual UTC deadline may move to be sooner or later than the boot
129            // deadline, meaning that we can wake either earlier or later than
130            // the user requested.
131            //
132            // If we woke up correctly, we return. If we woke up too early, we
133            // recompute and continue waiting.
134            select! {
135                // While nominally the waits on the boot and UTC timelines will trigger at about
136                // the same wall clock instant absent timeline changes and wakes, the boot timer
137                // has a much finer resolution than the UTC timer. For example, some devices
138                // support at most 1 second resolution for UTC wakes, vs effectively millisecond
139                // resolution on boot timers or even finer.
140                //
141                // So if we have an opportunity to be more accurate by waking on boot timer, we
142                // should probably take it.
143                _ = boot_wait_fut => {
144                    log_debug!("UtcWaiter::wait: woken by boot deadline.");
145                },
146                // The UTC timer has an additional property that it is able to wake the device
147                // from sleep. As a tradeoff, it usually has orders of magnitude coarser resolution
148                // than the boot timer. So, we also wait on UTC to get the wake functionality.
149                _ = utc_wait_fut => {
150                    log_debug!("UtcWaiter::wait: woken by UTC deadline.");
151                },
152            }
153            let utc_now = (self.utc_now_fn)();
154            if deadline <= utc_now {
155                log_debug!(
156                    "UtcWaiter::wait: UTC deadline reached: now={:?}, deadline={:?}",
157                    utc_now,
158                    deadline
159                );
160                break;
161            } else {
162                log_debug!(
163                    "UtcWaiter::wait: UTC deadline NOT reached: now={:?}, deadline={:?}",
164                    utc_now,
165                    deadline
166                );
167            }
168        }
169    }
170}
171
172#[derive(Debug)]
173struct IntervalTimerMutableState {
174    /// Handle to abort the running timer task.
175    abort_handle: Option<AbortHandle>,
176    /// If the timer is armed (started).
177    armed: bool,
178    /// Time of the next expiration on the requested timeline.
179    target_time: TargetTime,
180    /// Interval for periodic timer.
181    interval: zx::SyntheticDuration,
182    /// Number of timer expirations that have occurred since the last time a signal was sent.
183    ///
184    /// Timer expiration is not counted as overrun under `SignalEventNotify::None`.
185    overrun_cur: i32,
186    /// Number of timer expirations that was on last delivered signal.
187    overrun_last: i32,
188}
189
190impl IntervalTimerMutableState {
191    fn disarm(&mut self) {
192        self.armed = false;
193        if let Some(abort_handle) = &self.abort_handle {
194            abort_handle.abort();
195        }
196        self.abort_handle = None;
197    }
198
199    fn on_setting_changed(&mut self) {
200        self.overrun_cur = 0;
201        self.overrun_last = 0;
202    }
203}
204
205impl IntervalTimer {
206    pub fn new(
207        timer_id: TimerId,
208        timeline: Timeline,
209        wakeup_type: TimerWakeup,
210        signal_event: SignalEvent,
211    ) -> Result<IntervalTimerHandle, Errno> {
212        // TODO(b/470129973): We may also need to add hr_timer for regular wakeups on the real time
213        // timeline, to track UTC timeline changes.
214        let hr_timer = match wakeup_type {
215            TimerWakeup::Regular => None,
216            TimerWakeup::Alarm => Some(HrTimer::new()),
217        };
218        Ok(Arc::new(Self {
219            timer_id,
220            hr_timer,
221            timeline,
222            signal_event,
223            state: IntervalTimerMutableState {
224                target_time: timeline.zero_time(),
225                abort_handle: Default::default(),
226                armed: Default::default(),
227                interval: Default::default(),
228                overrun_cur: Default::default(),
229                overrun_last: Default::default(),
230            }
231            .into(),
232        }))
233    }
234
235    fn signal_info(self: &IntervalTimerHandle) -> Option<SignalInfo> {
236        let signal_detail = SignalDetail::Timer { timer: self.clone() };
237        Some(SignalInfo::with_detail(self.signal_event.signo?, SI_TIMER, signal_detail))
238    }
239
240    async fn start_timer_loop(
241        self: &IntervalTimerHandle,
242        kernel: &Kernel,
243        timer_thread_group: Weak<ThreadGroup>,
244    ) {
245        loop {
246            let overtime = loop {
247                // We may have to issue multiple sleeps if the target time in the timer is
248                // updated while we are sleeping or if our estimation of the target time
249                // relative to the monotonic clock is off. Drop the guard before blocking so
250                // that the target time can be updated.
251                let target_time = { self.state.lock().target_time };
252                let now = self.timeline.now();
253                if now >= target_time {
254                    break now
255                        .delta(&target_time)
256                        .expect("timer timeline and target time are comparable");
257                }
258                let (utc_waiter, utc_signal) = UtcWaiter::new();
259                let utc_waiter = Arc::new(utc_waiter);
260                if let Some(hr_timer) = &self.hr_timer {
261                    assert_matches!(
262                        target_time,
263                        TargetTime::BootInstant(_) | TargetTime::RealTime(_),
264                        "monotonic times can't be alarm deadlines",
265                    );
266                    let weak_utc_waiter = Arc::downgrade(&utc_waiter);
267                    if let Err(e) = hr_timer.start(
268                        kernel.kthreads.system_task(),
269                        Some(weak_utc_waiter),
270                        target_time,
271                    ) {
272                        log_error!("Failed to start the HrTimer to trigger wakeup: {e}");
273                    }
274                }
275
276                match target_time {
277                    TargetTime::Monotonic(t) => fuchsia_async::Timer::new(t).await,
278                    TargetTime::BootInstant(t) => fuchsia_async::Timer::new(t).await,
279                    TargetTime::RealTime(t) => utc_waiter.wait(t, utc_signal).await,
280                }
281            };
282            if !self.state.lock().armed {
283                return;
284            }
285
286            // Timer expirations are counted as overruns except SIGEV_NONE.
287            if self.signal_event.notify != SignalEventNotify::None {
288                let mut guard = self.state.lock();
289                // If the `interval` is zero, the timer expires just once, at the time
290                // specified by `target_time`.
291                if guard.interval == zx::SyntheticDuration::ZERO {
292                    guard.overrun_cur = 1;
293                } else {
294                    let exp =
295                        i32::try_from(overtime.into_nanos() / guard.interval.into_nanos() + 1)
296                            .unwrap_or(i32::MAX);
297                    guard.overrun_cur = guard.overrun_cur.saturating_add(exp);
298                };
299            }
300
301            // Check on notify enum to determine the signal target.
302            if let Some(timer_thread_group) = timer_thread_group.upgrade() {
303                match self.signal_event.notify {
304                    SignalEventNotify::Signal => {
305                        if let Some(signal_info) = self.signal_info() {
306                            log_trace!(
307                                signal = signal_info.signal.number(),
308                                pid = timer_thread_group.leader;
309                                "sending signal for timer"
310                            );
311                            timer_thread_group.write().send_signal(signal_info);
312                        }
313                    }
314                    SignalEventNotify::None => {}
315                    SignalEventNotify::Thread { .. } => {
316                        track_stub!(TODO("https://fxbug.dev/322875029"), "SIGEV_THREAD timer");
317                    }
318                    SignalEventNotify::ThreadId(tid) => {
319                        // Check if the target thread exists in the thread group.
320                        timer_thread_group.read().get_task(tid).map(|target| {
321                            if let Some(signal_info) = self.signal_info() {
322                                log_trace!(
323                                    signal = signal_info.signal.number(),
324                                    tid;
325                                    "sending signal for timer"
326                                );
327                                send_signal(
328                                    kernel.kthreads.unlocked_for_async().deref_mut(),
329                                    &target,
330                                    signal_info,
331                                )
332                                .unwrap_or_else(|e| {
333                                    log_warn!("Failed to queue timer signal: {}", e)
334                                });
335                            }
336                        });
337                    }
338                }
339            }
340
341            // If the `interval` is zero, the timer expires just once, at the time
342            // specified by `target_time`.
343            let mut guard = self.state.lock();
344            if guard.interval != zx::SyntheticDuration::default() {
345                guard.target_time = self.timeline.now() + GenericDuration::from(guard.interval);
346            } else {
347                guard.disarm();
348                return;
349            }
350        }
351    }
352
353    pub fn on_signal_delivered(self: &IntervalTimerHandle) {
354        let mut guard = self.state.lock();
355        guard.overrun_last = guard.overrun_cur;
356        guard.overrun_cur = 0;
357    }
358
359    pub fn arm(
360        self: &IntervalTimerHandle,
361        current_task: &CurrentTask,
362        new_value: itimerspec,
363        is_absolute: bool,
364    ) -> Result<(), Errno> {
365        let mut guard = self.state.lock();
366
367        let target_time = if is_absolute {
368            self.timeline.target_from_timespec(new_value.it_value)?
369        } else {
370            self.timeline.now()
371                + GenericDuration::from(duration_from_timespec::<zx::SyntheticTimeline>(
372                    new_value.it_value,
373                )?)
374        };
375
376        // Stop the current running task.
377        guard.disarm();
378
379        let interval = duration_from_timespec(new_value.it_interval)?;
380        guard.interval = interval;
381        if let Some(hr_timer) = &self.hr_timer {
382            // It is important for power management that the hrtimer is marked as interval, as
383            // interval timers may prohibit container suspension.  Note that marking `is_interval`
384            // changes the hrtimer ID, which is only allowed if the hrtimer is not running.
385            *hr_timer.is_interval.lock() = guard.interval != zx::SyntheticDuration::default();
386        }
387
388        if target_time.is_zero() {
389            return Ok(());
390        }
391
392        guard.armed = true;
393        guard.target_time = target_time;
394        guard.on_setting_changed();
395
396        let kernel_ref = current_task.kernel().clone();
397        let self_ref = self.clone();
398        let thread_group = current_task.thread_group().weak_self.clone();
399        current_task.kernel().kthreads.spawn_future(
400            move || async move {
401                let _ = {
402                    // 1. Lock the state to update `abort_handle` when the timer is still armed.
403                    // 2. MutexGuard needs to be dropped before calling await on the future task.
404                    // Unfortunately, std::mem::drop is not working correctly on this:
405                    // (https://github.com/rust-lang/rust/issues/57478).
406                    let mut guard = self_ref.state.lock();
407                    if !guard.armed {
408                        return;
409                    }
410
411                    let (abortable_future, abort_handle) = futures::future::abortable(
412                        self_ref.start_timer_loop(&kernel_ref, thread_group),
413                    );
414                    guard.abort_handle = Some(abort_handle);
415                    abortable_future
416                }
417                .await;
418            },
419            "interval_timer_loop",
420        );
421
422        Ok(())
423    }
424
425    pub fn disarm(&self, current_task: &CurrentTask) -> Result<(), Errno> {
426        let mut guard = self.state.lock();
427        guard.disarm();
428        guard.on_setting_changed();
429        if let Some(hr_timer) = &self.hr_timer {
430            hr_timer.stop(current_task.kernel())?;
431        }
432        Ok(())
433    }
434
435    pub fn time_remaining(&self) -> TimerRemaining {
436        let guard = self.state.lock();
437        if !guard.armed {
438            return TimerRemaining::default();
439        }
440
441        TimerRemaining {
442            remainder: std::cmp::max(
443                zx::SyntheticDuration::ZERO,
444                *guard.target_time.delta(&self.timeline.now()).expect("timelines must match"),
445            ),
446            interval: guard.interval,
447        }
448    }
449
450    pub fn overrun_cur(&self) -> i32 {
451        self.state.lock().overrun_cur
452    }
453    pub fn overrun_last(&self) -> i32 {
454        self.state.lock().overrun_last
455    }
456}
457
458impl PartialEq for IntervalTimer {
459    fn eq(&self, other: &Self) -> bool {
460        std::ptr::addr_of!(self) == std::ptr::addr_of!(other)
461    }
462}
463impl Eq for IntervalTimer {}
464
465#[cfg(test)]
466mod tests {
467    use super::*;
468    use crate::time::utc::UtcClockOverrideGuard;
469    use assert_matches::assert_matches;
470    use fuchsia_async as fasync;
471    use fuchsia_runtime as fxr;
472    use std::task::Poll;
473
474    struct TestContext {
475        _initial_time_mono: zx::MonotonicInstant,
476        initial_time_utc: UtcInstant,
477        _utc_clock: fxr::UtcClock,
478        _guard: UtcClockOverrideGuard,
479    }
480
481    impl TestContext {
482        async fn new() -> Self {
483            // Make them the same initially.
484            let _initial_time_mono = zx::MonotonicInstant::from_nanos(1000);
485            let initial_time_utc = UtcInstant::from_nanos(_initial_time_mono.into_nanos());
486            fasync::TestExecutor::advance_to(_initial_time_mono.into()).await;
487
488            // Create and start the UTC clock.
489            let utc_clock =
490                fxr::UtcClock::create(zx::ClockOpts::empty(), Some(initial_time_utc)).unwrap();
491            let utc_clock_clone = utc_clock.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
492            let initial_time_boot = zx::BootInstant::from_nanos(_initial_time_mono.into_nanos());
493            utc_clock
494                .update(
495                    fxr::UtcClockUpdate::builder()
496                        .absolute_value(initial_time_boot, initial_time_utc)
497                        .build(),
498                )
499                .unwrap();
500
501            // Inject the clock into Starnix infra.
502            let _guard = UtcClockOverrideGuard::new(utc_clock_clone);
503
504            Self { _initial_time_mono, initial_time_utc, _utc_clock: utc_clock, _guard }
505        }
506    }
507
508    // If the UTC signal is received and the wait expired, we are done.
509    #[fuchsia::test(allow_stalls = false)]
510    async fn test_utc_waiter_on_utc_expired() {
511        let _context = TestContext::new().await;
512
513        let (waiter, utc_signal) = UtcWaiter::new();
514        // Expired deadline, and notification.
515        waiter.on_wake_internal();
516        let deadline_utc = _context.initial_time_utc - fxr::UtcDuration::from_nanos(10);
517        let wait_fut = pin!(waiter.wait(deadline_utc, utc_signal));
518        assert_matches!(
519            fasync::TestExecutor::poll_until_stalled(wait_fut).await,
520            Poll::Ready(_),
521            "UTC deadline should have expired"
522        );
523    }
524
525    // If the UTC signal is received, but the deadline is not reached, we
526    // must still pend.
527    #[fuchsia::test(allow_stalls = false)]
528    async fn test_utc_waiter_on_utc_still_pending() {
529        let context = TestContext::new().await;
530
531        let (waiter, utc_signal) =
532            UtcWaiter::new_internal(|| -> fxr::UtcInstant { fxr::UtcInstant::from_nanos(2000) });
533        // Notified, but not expired yet.
534        waiter.on_wake_internal();
535        let deadline_utc = context.initial_time_utc + fxr::UtcDuration::INFINITE;
536
537        let wait_fut = pin!(waiter.wait(deadline_utc, utc_signal));
538        assert_matches!(
539            fasync::TestExecutor::poll_until_stalled(wait_fut).await,
540            Poll::Pending,
541            "UTC deadline should not have expired"
542        );
543    }
544
545    // If we are woken by the timer, and UTC deadline has passed, we are done.
546    #[fuchsia::test(allow_stalls = false)]
547    async fn test_utc_waiter_on_boot_expires() {
548        let context = TestContext::new().await;
549
550        let (waiter, utc_signal) =
551            UtcWaiter::new_internal(|| -> fxr::UtcInstant { fxr::UtcInstant::from_nanos(5000) });
552        let deadline_utc = context.initial_time_utc + fxr::UtcDuration::from_nanos(4000);
553        let wait_fut = pin!(waiter.wait(deadline_utc, utc_signal));
554
555        fasync::TestExecutor::advance_to(zx::MonotonicInstant::from_nanos(10000).into()).await;
556        assert_matches!(
557            fasync::TestExecutor::poll_until_stalled(wait_fut).await,
558            Poll::Ready(_),
559            "UTC deadline should have expired, and we got notified via the timer wait"
560        );
561    }
562}