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