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