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