Skip to main content

hermit/scheduler/
timer_interrupts.rs

1use core::mem;
2
3use crate::arch::kernel::core_local::core_scheduler;
4use crate::arch::kernel::set_oneshot_timer;
5#[cfg(feature = "net")]
6use crate::executor::network::wake_network_waker;
7
8/// A possible timer interrupt source (i.e. reason the timer interrupt was set
9/// up).
10#[derive(Debug, PartialEq, Eq)]
11pub enum Source {
12	Network,
13	Scheduler,
14	/// Preemptive time-slice timer (round-robin between equally prioritized,
15	/// ready tasks). Only armed with the `preemptive` feature and when there is
16	/// contention.
17	#[cfg(feature = "preemptive")]
18	Preemption,
19}
20
21/// A slot in the timer list. Each source is represented once. This is so that
22/// we can have multiple timers at the same time with only one hardware timer.
23#[derive(Debug)]
24pub struct Slot {
25	/// Timer source.
26	source: Source,
27	/// Point in time at which to wake up (in microsecond precision).
28	/// A value of [`u64::MAX`] means the timer is not set.
29	/// This is done to allow easy sorting by the wakeup time in ascending order.
30	wakeup_time: u64,
31}
32
33#[cfg(feature = "preemptive")]
34const NUMBER_OF_SLOTS: usize = 3;
35
36#[cfg(not(feature = "preemptive"))]
37const NUMBER_OF_SLOTS: usize = 2;
38
39// List of timers with one entry for every possible source.
40#[derive(Debug)]
41pub struct TimerList([Slot; NUMBER_OF_SLOTS]);
42
43impl TimerList {
44	pub fn new() -> Self {
45		Self([
46			Slot {
47				source: Source::Network,
48				wakeup_time: u64::MAX,
49			},
50			Slot {
51				source: Source::Scheduler,
52				wakeup_time: u64::MAX,
53			},
54			#[cfg(feature = "preemptive")]
55			Slot {
56				source: Source::Preemption,
57				wakeup_time: u64::MAX,
58			},
59		])
60	}
61
62	/// Mutably get the slot for a source.
63	pub fn slot_by_source_mut(&mut self, source: Source) -> &mut Slot {
64		// Cannot panic: There's more than one slot
65		self.0
66			.iter_mut()
67			.find(|slot| slot.source == source)
68			.unwrap()
69	}
70
71	// Find and get the next timer to fire (may return one that is not currently set if none are set).
72	pub fn next_timer(&self) -> &Slot {
73		// Cannot panic: There's more than 1 slot
74		self.0
75			.iter()
76			.min_by(|a, b| a.wakeup_time.cmp(&b.wakeup_time))
77			.unwrap()
78	}
79
80	/// Find and mutably get the next timer to fire (may return one that is not currently set if none are set).
81	pub fn next_timer_mut(&mut self) -> &mut Slot {
82		// Cannot panic: There's more than 1 slot
83		self.0
84			.iter_mut()
85			.min_by(|a, b| a.wakeup_time.cmp(&b.wakeup_time))
86			.unwrap()
87	}
88
89	/// Adjust all wakeup times by a specific offset.
90	pub fn adjust_by(&mut self, offset: u64) {
91		for timer in self.0.iter_mut() {
92			if timer.wakeup_time != u64::MAX {
93				timer.wakeup_time -= offset;
94			}
95		}
96	}
97}
98
99impl Default for TimerList {
100	fn default() -> Self {
101		Self::new()
102	}
103}
104
105/// Create a new timer, overriding any previous timer for the source.
106#[cfg(feature = "net")]
107#[inline]
108pub fn create_timer(source: Source, wakeup_micros: u64) {
109	create_timer_abs(
110		source,
111		// get_timer_ticks should always return a nonzero value
112		crate::arch::kernel::processor::get_timer_ticks() + wakeup_micros,
113	);
114}
115
116/// Crete a new timer, but with an absolute wakeup time.
117pub fn create_timer_abs(source: Source, wakeup_time: u64) {
118	let timers = &mut core_scheduler().timers;
119
120	// Cannot panic: Our timer list has an entry for every possible source
121	let previous_entry = timers.slot_by_source_mut(source);
122
123	// Overwrite the wakeup time
124	previous_entry.wakeup_time = previous_entry.wakeup_time.min(wakeup_time);
125
126	// If this timer is the one closest in the future, set the real timer to it
127	if timers.next_timer().wakeup_time == wakeup_time {
128		set_oneshot_timer(Some(wakeup_time));
129	}
130}
131
132/// Clears the timer slot for the currently active timer and sets the next timer or disables it if no timer is pending.
133pub fn clear_active_and_set_next() {
134	let timers = &mut core_scheduler().timers;
135
136	let lowest_timer = timers.next_timer_mut();
137	assert!(lowest_timer.wakeup_time != u64::MAX);
138
139	// Handle the timer interrupt
140	match lowest_timer.source {
141		#[cfg(feature = "net")]
142		Source::Network => wake_network_waker(),
143		_ => {} // no-op, we always poll after a timer interrupt
144	}
145
146	trace!("Cleared active timer {lowest_timer:?}");
147
148	let prev_wakeup_time = mem::replace(&mut lowest_timer.wakeup_time, u64::MAX);
149
150	// We may receive a timer interrupt earlier than expected
151	// This appears to only be the case in QEMU, it seems like timer ticks
152	// do not advance linearly there?
153	// Either way, this means that QEMU *thinks* the time has passed, so it
154	// probably has and knows better than we do.
155	// We can cheat a bit and adjust all timers slightly based on this
156	let timer_ticks = crate::arch::kernel::processor::get_timer_ticks();
157	if prev_wakeup_time > timer_ticks {
158		let offset = prev_wakeup_time - timer_ticks;
159		timers.adjust_by(offset);
160	}
161
162	let new_lowest_timer = timers.next_timer().wakeup_time;
163
164	if new_lowest_timer == u64::MAX {
165		set_oneshot_timer(None);
166	} else {
167		set_oneshot_timer(Some(new_lowest_timer));
168	}
169}