Skip to main content

hermit/scheduler/
timer_interrupts.rs

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