Skip to main content

capsules_core/virtualizers/
virtual_timer.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Provide multiple Timers on top of a single underlying Alarm.
6
7use core::cell::Cell;
8use core::cmp;
9
10use kernel::ErrorCode;
11use kernel::collections::list::{List, ListLink, ListNode};
12use kernel::hil::time::{self, Alarm, Ticks, Time, Timer};
13use kernel::utilities::cells::{NumericCellExt, OptionalCell};
14
15use crate::virtualizers::virtual_alarm::VirtualMuxAlarm;
16
17#[derive(Copy, Clone, Debug, PartialEq)]
18enum Mode {
19    Disabled,
20    OneShot,
21    Repeating,
22}
23
24/// An object to multiplex multiple "virtual" timers over a single underlying alarm. A
25/// `VirtualTimer` is a node in a linked list of timers that share the same underlying alarm.
26pub struct VirtualTimer<'a, A: Alarm<'a>> {
27    /// Underlying alarm which multiplexes all these virtual timers.
28    mux: &'a MuxTimer<'a, A>,
29    /// Reference time point when this timer was setup.
30    when: Cell<A::Ticks>,
31    /// Interval between events reported by this timer.
32    interval: Cell<A::Ticks>,
33    /// Current mode of this timer.
34    mode: Cell<Mode>,
35    /// Next timer in the list.
36    next: ListLink<'a, VirtualTimer<'a, A>>,
37    /// Timer client for this node in the list.
38    client: OptionalCell<&'a dyn time::TimerClient>,
39}
40
41impl<'a, A: Alarm<'a>> ListNode<'a, VirtualTimer<'a, A>> for VirtualTimer<'a, A> {
42    fn next(&self) -> &'a ListLink<'_, VirtualTimer<'a, A>> {
43        &self.next
44    }
45}
46
47impl<'a, A: Alarm<'a>> VirtualTimer<'a, A> {
48    /// After calling new, always call setup()
49    pub fn new(mux_timer: &'a MuxTimer<'a, A>) -> VirtualTimer<'a, A> {
50        let zero = A::Ticks::from(0);
51        VirtualTimer {
52            mux: mux_timer,
53            when: Cell::new(zero),
54            interval: Cell::new(zero),
55            mode: Cell::new(Mode::Disabled),
56            next: ListLink::empty(),
57            client: OptionalCell::empty(),
58        }
59    }
60
61    /// Call this method immediately after new() to link this to the mux, otherwise timers won't
62    /// fire
63    pub fn setup(&'a self) {
64        self.mux.timers.push_head(self);
65    }
66
67    // Start a new timer, configuring its mode and adjusting the underlying alarm if needed.
68    fn start_timer(&self, interval: A::Ticks, mode: Mode) -> A::Ticks {
69        if self.mode.get() == Mode::Disabled {
70            self.mux.enabled.increment();
71        }
72        self.mode.set(mode);
73
74        // We can't fire faster than the minimum dt of the alarm.
75        let real_interval: A::Ticks = A::Ticks::from(cmp::max(
76            interval.into_u32(),
77            self.mux.alarm.minimum_dt().into_u32(),
78        ));
79
80        let now = self.mux.alarm.now();
81        self.interval.set(real_interval);
82        self.when.set(now.wrapping_add(real_interval));
83        self.mux.calculate_alarm(now, real_interval);
84
85        real_interval
86    }
87}
88
89impl<'a, A: Alarm<'a>> Time for VirtualTimer<'a, A> {
90    type Frequency = A::Frequency;
91    type Ticks = A::Ticks;
92
93    fn now(&self) -> A::Ticks {
94        self.mux.alarm.now()
95    }
96}
97
98impl<'a, A: Alarm<'a>> Timer<'a> for VirtualTimer<'a, A> {
99    fn set_timer_client(&self, client: &'a dyn time::TimerClient) {
100        self.client.set(client);
101    }
102
103    fn cancel(&self) -> Result<(), ErrorCode> {
104        match self.mode.get() {
105            Mode::Disabled => Ok(()),
106            Mode::OneShot | Mode::Repeating => {
107                self.mode.set(Mode::Disabled);
108                self.mux.enabled.decrement();
109
110                // If there are not more enabled timers, disable the
111                // underlying alarm.
112                if self.mux.enabled.get() == 0 {
113                    let _ = self.mux.alarm.disarm();
114                }
115                Ok(())
116            }
117        }
118    }
119
120    fn interval(&self) -> Option<Self::Ticks> {
121        match self.mode.get() {
122            Mode::Disabled => None,
123            Mode::OneShot | Mode::Repeating => Some(self.interval.get()),
124        }
125    }
126
127    fn is_oneshot(&self) -> bool {
128        self.mode.get() == Mode::OneShot
129    }
130
131    fn is_repeating(&self) -> bool {
132        self.mode.get() == Mode::Repeating
133    }
134
135    fn is_enabled(&self) -> bool {
136        match self.mode.get() {
137            Mode::Disabled => false,
138            Mode::OneShot | Mode::Repeating => true,
139        }
140    }
141
142    fn oneshot(&self, interval: Self::Ticks) -> Self::Ticks {
143        self.start_timer(interval, Mode::OneShot)
144    }
145
146    fn repeating(&self, interval: Self::Ticks) -> Self::Ticks {
147        self.start_timer(interval, Mode::Repeating)
148    }
149
150    fn time_remaining(&self) -> Option<Self::Ticks> {
151        match self.mode.get() {
152            Mode::Disabled => None,
153            Mode::OneShot | Mode::Repeating => {
154                let when = self.when.get();
155                let now = self.mux.alarm.now();
156                Some(when.wrapping_sub(now))
157            }
158        }
159    }
160}
161
162impl<'a, A: Alarm<'a>> time::AlarmClient for VirtualTimer<'a, A> {
163    fn alarm(&self) {
164        match self.mode.get() {
165            Mode::Disabled => {} // Do nothing
166            Mode::OneShot => {
167                self.mode.set(Mode::Disabled);
168                self.client.map(|client| client.timer());
169            }
170            Mode::Repeating => {
171                // By setting the 'now' to be 'when', this ensures
172                // the the repeating timer fires at a fixed interval:
173                // it'll fire at when + (k * interval), for k=0...n.
174                let when = self.when.get();
175                let interval = self.interval.get();
176                self.when.set(when.wrapping_add(interval));
177                self.mux.calculate_alarm(when, interval);
178                self.client.map(|client| client.timer());
179            }
180        }
181    }
182}
183
184/// Structure to control a set of virtual timers multiplexed together on top of a single alarm.
185pub struct MuxTimer<'a, A: Alarm<'a>> {
186    /// Head of the linked list of virtual timers multiplexed together.
187    timers: List<'a, VirtualTimer<'a, A>>,
188    /// Number of virtual timers that are currently enabled.
189    enabled: Cell<usize>,
190    /// Underlying alarm, over which the virtual timers are multiplexed.
191    alarm: &'a VirtualMuxAlarm<'a, A>,
192}
193
194impl<'a, A: Alarm<'a>> MuxTimer<'a, A> {
195    pub const fn new(alarm: &'a VirtualMuxAlarm<'a, A>) -> MuxTimer<'a, A> {
196        MuxTimer {
197            timers: List::new(),
198            enabled: Cell::new(0),
199            alarm,
200        }
201    }
202
203    fn calculate_alarm(&self, now: A::Ticks, interval: A::Ticks) {
204        if self.enabled.get() == 1 {
205            // First alarm, to just set it
206            self.alarm.set_alarm(now, interval);
207        } else {
208            // If the current alarm doesn't fall within the range of
209            // [now, now + interval), this means this new alarm
210            // will fire sooner. This covers the case when the current
211            // alarm is in the past, because it must have already fired
212            // and the bottom half is pending. -pal
213            let cur_alarm = self.alarm.get_alarm();
214            let when = now.wrapping_add(interval);
215            if !cur_alarm.within_range(now, when) {
216                // This alarm is earlier: reset alarm to it
217                self.alarm.set_alarm(now, interval);
218            } else {
219                // current alarm will fire earlier, keep it
220            }
221        }
222    }
223}
224
225impl<'a, A: Alarm<'a>> time::AlarmClient for MuxTimer<'a, A> {
226    fn alarm(&self) {
227        // The "now" is when the alarm fired, not the current
228        // time; this is case there was some delay. This also
229        // ensures that all other timers are >= now.
230        let now = self.alarm.get_alarm();
231        // Check whether to fire each timer. At this level, alarms are one-shot,
232        // so a repeating timer will reset its `when` in the alarm() callback.
233        self.timers
234            .iter()
235            .filter(|cur| {
236                cur.is_enabled()
237                    && !now.within_range(
238                        cur.when.get().wrapping_sub(cur.interval.get()),
239                        cur.when.get(),
240                    )
241            })
242            .for_each(|cur| {
243                cur.alarm();
244            });
245
246        // Find the soonest alarm client (if any) and set the "next" underlying
247        // alarm based on it.  This needs to happen after firing all expired
248        // alarms since those may have reset new alarms.
249        let next = self
250            .timers
251            .iter()
252            .filter(|cur| cur.is_enabled())
253            .min_by_key(|cur| cur.when.get().wrapping_sub(now).into_u32());
254
255        // Set the alarm.
256        if let Some(valrm) = next {
257            self.alarm
258                .set_alarm(now, valrm.when.get().wrapping_sub(now));
259        } else {
260            let _ = self.alarm.disarm();
261        }
262    }
263}