Skip to main content

starnix_core/time/
timers.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::signals::{SignalEvent, SignalEventNotify, SignalEventValue};
6use crate::task::CurrentTask;
7use crate::time::interval_timer::{IntervalTimer, IntervalTimerHandle};
8use crate::time::{Timeline, TimerWakeup};
9use starnix_logging::log_warn;
10use starnix_sync::{LockDepMutex, TimerTableStateLock};
11use starnix_uapi::errors::Errno;
12use starnix_uapi::signals::SIGALRM;
13use starnix_uapi::{TIMER_ABSTIME, error, itimerspec, uapi};
14use std::collections::HashMap;
15
16static_assertions::const_assert!(
17    std::mem::size_of::<uapi::__kernel_timer_t>()
18        == std::mem::size_of::<uapi::arch32::__kernel_timer_t>()
19);
20pub type TimerId = uapi::__kernel_timer_t;
21
22static_assertions::const_assert!(
23    std::mem::size_of::<uapi::__kernel_clockid_t>()
24        == std::mem::size_of::<uapi::arch32::__kernel_clockid_t>()
25);
26pub type ClockId = uapi::__kernel_clockid_t;
27
28// Table for POSIX timers from timer_create() that deliver timers via signals (not new-style
29// timerfd's).
30#[derive(Debug, Default)]
31pub struct TimerTable {
32    state: LockDepMutex<TimerTableMutableState, TimerTableStateLock>,
33}
34
35#[derive(Debug)]
36struct TimerTableMutableState {
37    /// The `TimerId` at which allocation should begin searching for an unused ID.
38    next_timer_id: TimerId,
39    timers: HashMap<TimerId, IntervalTimerHandle>,
40    itimer_real: IntervalTimerHandle,
41}
42
43impl Default for TimerTableMutableState {
44    fn default() -> Self {
45        let signal_event =
46            SignalEvent::new(SignalEventValue(0), SIGALRM, SignalEventNotify::Signal);
47        let itimer_real =
48            IntervalTimer::new(0, Timeline::RealTime, TimerWakeup::Regular, signal_event)
49                .expect("Failed to create itimer_real");
50        TimerTableMutableState {
51            itimer_real,
52            timers: Default::default(),
53            next_timer_id: Default::default(),
54        }
55    }
56}
57
58impl TimerTable {
59    /// Creates a new per-process interval timer.
60    ///
61    /// The new timer is initially disarmed.
62    pub fn create(
63        &self,
64        timeline: Timeline,
65        wakeup_type: TimerWakeup,
66        signal_event: Option<SignalEvent>,
67    ) -> Result<TimerId, Errno> {
68        let mut state = self.state.lock();
69
70        // Find a vacant timer id.
71        let end = state.next_timer_id;
72        let timer_id = loop {
73            let timer_id = state.next_timer_id;
74            state.next_timer_id += 1;
75
76            if state.next_timer_id == TimerId::MAX {
77                state.next_timer_id = 0;
78            } else if state.next_timer_id == end {
79                // After searching the entire timer map, there is no vacant timer id.
80                // Fails the call and implies the program could try it again later.
81                return error!(EAGAIN);
82            }
83
84            if !state.timers.contains_key(&timer_id) {
85                break timer_id;
86            }
87        };
88
89        state.timers.insert(
90            timer_id,
91            IntervalTimer::new(
92                timer_id,
93                timeline,
94                wakeup_type,
95                signal_event.unwrap_or_else(|| {
96                    SignalEvent::new(
97                        SignalEventValue(timer_id as u64),
98                        SIGALRM,
99                        SignalEventNotify::Signal,
100                    )
101                }),
102            )?,
103        );
104
105        Ok(timer_id)
106    }
107
108    pub fn itimer_real(&self) -> IntervalTimerHandle {
109        self.state.lock().itimer_real.clone()
110    }
111
112    /// Disarms and deletes a timer.
113    pub fn delete(&self, current_task: &CurrentTask, id: TimerId) -> Result<(), Errno> {
114        let mut state = self.state.lock();
115        match state.timers.remove_entry(&id) {
116            Some(entry) => entry.1.disarm(current_task),
117            None => error!(EINVAL),
118        }
119    }
120
121    /// Fetches the time remaining until the next expiration of a timer, along with the interval
122    /// setting of the timer.
123    pub fn get_time(&self, id: TimerId) -> Result<itimerspec, Errno> {
124        Ok(self.get_timer(id)?.time_remaining().into())
125    }
126
127    /// Returns the overrun count for the last timer expiration.
128    pub fn get_overrun(&self, id: TimerId) -> Result<i32, Errno> {
129        Ok(self.get_timer(id)?.overrun_last())
130    }
131
132    /// Arms (start) or disarms (stop) the timer identifierd by `id`. The `new_value` arg is a
133    /// pointer to an `itimerspec` structure that specifies the new initial value and the new
134    /// interval for the timer.
135    pub fn set_time(
136        &self,
137        current_task: &CurrentTask,
138        id: TimerId,
139        flags: i32,
140        new_value: itimerspec,
141    ) -> Result<itimerspec, Errno> {
142        let itimer = self.get_timer(id)?;
143        let old_value: itimerspec = itimer.time_remaining().into();
144        if new_value.it_value.tv_sec != 0 || new_value.it_value.tv_nsec != 0 {
145            let is_absolute = flags == TIMER_ABSTIME as i32;
146            itimer.arm(current_task, new_value, is_absolute)?;
147        } else {
148            itimer.disarm(current_task)?;
149        }
150
151        Ok(old_value)
152    }
153
154    pub fn get_timer(&self, id: TimerId) -> Result<IntervalTimerHandle, Errno> {
155        match self.state.lock().timers.get(&id) {
156            Some(itimer) => Ok(itimer.clone()),
157            None => error!(EINVAL),
158        }
159    }
160
161    /// Disarms and deletes all POSIX timers, preserving interval timers.
162    pub fn reset_for_exec(&self, current_task: &CurrentTask) {
163        // Drop the table lock before disarming timers to avoid holding the lock during
164        // asynchronous timer cancellation. Note that `next_timer_id` is intentionally not reset,
165        // matching Linux behavior where timer ID allocation continues sequentially across execve.
166        let timers = std::mem::take(&mut self.state.lock().timers);
167        for (_, timer) in timers {
168            if let Err(err) = timer.disarm(current_task) {
169                log_warn!("Failed to disarm POSIX timer on exec: {err:?}");
170            }
171        }
172    }
173}