use core::cell::RefCell;
use cortex_m::{
interrupt::{self, Mutex},
peripheral::SYST,
};
struct Wakeup {
wakeup_at: u64,
waker: core::task::Waker,
}
fn schedule_wake_into_slots<const N: usize>(
slots: &mut [Option<Wakeup>; N],
at: u64,
waker: &core::task::Waker,
) -> bool {
let mut empty_idx: Option<usize> = None;
for (i, slot) in slots.iter().enumerate() {
if let Some(wakeup) = slot {
if wakeup.waker.will_wake(waker) && wakeup.wakeup_at == at {
return true;
}
} else if empty_idx.is_none() {
empty_idx = Some(i);
}
}
if let Some(i) = empty_idx {
slots[i] = Some(Wakeup {
wakeup_at: at,
waker: waker.clone(),
});
true
} else {
false
}
}
pub struct SystickDriver<const N: usize> {
wakeup_at: Mutex<RefCell<[Option<Wakeup>; N]>>,
timer: crate::Timer,
}
impl<const N: usize> SystickDriver<N> {
pub const fn new(systick_freq: u64, reload_value: u32) -> Self {
let timer = crate::Timer::new(embassy_time_driver::TICK_HZ, reload_value, systick_freq);
Self {
wakeup_at: Mutex::new(RefCell::new([const { None }; N])),
timer: timer,
}
}
pub const fn new_default(systick_freq: u64) -> Self {
let reload = (systick_freq / embassy_time_driver::TICK_HZ) - 1;
Self::new(systick_freq, reload as u32)
}
fn maybe_wake(&self) {
interrupt::free(|cs| {
let mutex_borrow = &self.wakeup_at.borrow(cs);
for slot in mutex_borrow.borrow_mut().iter_mut() {
let mut cleared = false;
if let Some(wakeup) = slot {
if self.timer.now() >= wakeup.wakeup_at {
wakeup.waker.wake_by_ref();
cleared = true;
}
}
if cleared {
*slot = None;
}
}
})
}
pub fn start(&self, syst: &mut SYST) {
self.timer.start(syst);
}
pub fn systick_interrupt(&self) {
self.timer.systick_handler();
self.maybe_wake();
}
}
impl<const N: usize> embassy_time_driver::Driver for SystickDriver<N> {
fn now(&self) -> u64 {
self.timer.now()
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
interrupt::free(|cs| {
let mutex_borrow = self.wakeup_at.borrow(cs);
let mut wakeups = mutex_borrow.borrow_mut();
if !schedule_wake_into_slots(&mut *wakeups, at, waker) {
panic!("No free wakeup slots");
}
})
}
}
#[cfg(feature = "embassy-defaults")]
embassy_time_driver::time_driver_impl!(static DRIVER: SystickDriver<4> = SystickDriver::new(8_000_000, 7_999));
#[cfg(feature = "embassy-defaults")]
#[cortex_m_rt::exception]
fn SysTick() {
DRIVER.systick_interrupt();
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::task::Wake;
struct TestWake;
impl Wake for TestWake {
fn wake(self: Arc<Self>) {}
}
fn make_waker() -> core::task::Waker {
Arc::new(TestWake).into()
}
fn make_distinct_waker() -> core::task::Waker {
Arc::new(TestWake).into()
}
fn count_occupied<const N: usize>(slots: &[Option<Wakeup>; N]) -> usize {
slots.iter().filter(|s| s.is_some()).count()
}
#[test]
fn test_basic_insert() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
let result = schedule_wake_into_slots(&mut slots, 100, &waker);
assert!(result);
assert_eq!(count_occupied(&slots), 1);
}
#[test]
fn test_duplicate_detection_same_waker_same_time() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
assert!(schedule_wake_into_slots(&mut slots, 100, &waker));
assert_eq!(count_occupied(&slots), 1);
assert!(schedule_wake_into_slots(&mut slots, 100, &waker));
assert_eq!(count_occupied(&slots), 1); }
#[test]
fn test_different_times_same_waker_creates_separate_slots() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
schedule_wake_into_slots(&mut slots, 100, &waker);
schedule_wake_into_slots(&mut slots, 200, &waker);
assert_eq!(count_occupied(&slots), 2);
}
#[test]
fn test_different_wakers_same_time_creates_separate_slots() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker1 = make_distinct_waker();
let waker2 = make_distinct_waker();
schedule_wake_into_slots(&mut slots, 100, &waker1);
schedule_wake_into_slots(&mut slots, 100, &waker2);
assert_eq!(count_occupied(&slots), 2);
}
#[test]
fn test_duplicate_detection_with_hole_before_existing() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
slots[1] = Some(Wakeup {
wakeup_at: 100,
waker: waker.clone(),
});
assert_eq!(count_occupied(&slots), 1);
let result = schedule_wake_into_slots(&mut slots, 100, &waker);
assert!(result); assert_eq!(count_occupied(&slots), 1);
assert!(slots[0].is_none());
}
#[test]
fn test_duplicate_detection_with_multiple_holes() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
slots[3] = Some(Wakeup {
wakeup_at: 100,
waker: waker.clone(),
});
let result = schedule_wake_into_slots(&mut slots, 100, &waker);
assert!(result);
assert_eq!(count_occupied(&slots), 1);
}
#[test]
fn test_fills_first_available_hole() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker1 = make_distinct_waker();
let waker2 = make_distinct_waker();
slots[1] = Some(Wakeup {
wakeup_at: 100,
waker: waker1.clone(),
});
let result = schedule_wake_into_slots(&mut slots, 100, &waker2);
assert!(result);
assert_eq!(count_occupied(&slots), 2);
assert!(slots[0].is_some()); }
#[test]
fn test_returns_false_when_full() {
let mut slots: [Option<Wakeup>; 2] = [const { None }; 2];
let waker1 = make_distinct_waker();
let waker2 = make_distinct_waker();
let waker3 = make_distinct_waker();
assert!(schedule_wake_into_slots(&mut slots, 100, &waker1));
assert!(schedule_wake_into_slots(&mut slots, 100, &waker2));
assert!(!schedule_wake_into_slots(&mut slots, 100, &waker3)); }
#[test]
fn test_repeated_polling_scenario() {
let mut slots: [Option<Wakeup>; 4] = [const { None }; 4];
let waker = make_waker();
for _ in 0..100 {
schedule_wake_into_slots(&mut slots, 1000, &waker);
}
assert_eq!(count_occupied(&slots), 1);
}
}