use crate::{time::Instant, Monotonic}; use core::cmp::Ordering; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; pub struct TimerQueue(pub BinaryHeap, N, Min>) where Mono: Monotonic, N: ArrayLength>, Task: Copy; impl TimerQueue where Mono: Monotonic, N: ArrayLength>, Task: Copy, { /// # Safety /// /// Writing to memory with a transmute in order to enable /// interrupts of the SysTick timer /// /// Enqueue a task without checking if it is full #[inline] pub unsafe fn enqueue_unchecked( &mut self, nr: NotReady, enable_interrupt: F1, pend_handler: F2, ) where F1: FnOnce(), F2: FnOnce(), { let mut is_empty = true; // Check if the top contains a non-empty element and if that element is // greater than nr let if_heap_max_greater_than_nr = self .0 .peek() .map(|head| { is_empty = false; nr.instant < head.instant }) .unwrap_or(true); if if_heap_max_greater_than_nr { if is_empty { // mem::transmute::<_, SYST>(()).enable_interrupt(); enable_interrupt(); } // Set SysTick pending // SCB::set_pendst(); pend_handler(); } self.0.push_unchecked(nr); } /// Check if the timer queue is empty. #[inline] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Dequeue a task from the TimerQueue #[inline] pub fn dequeue(&mut self, disable_interrupt: F) -> Option<(Task, u8)> where F: FnOnce(), { unsafe { Mono::clear_compare(); if let Some(instant) = self.0.peek().map(|p| p.instant) { if instant < Mono::now() { // instant < now // task became ready let nr = self.0.pop_unchecked(); Some((nr.task, nr.index)) } else { // TODO: Fix this hack... // Extract the compare time Mono::set_compare(*instant.duration_since_epoch().integer()); // Double check that the instant we set is really in the future, else // dequeue. If the monotonic is fast enough it can happen that from the // read of now to the set of the compare, the time can overflow. This is to // guard against this. if instant < Mono::now() { let nr = self.0.pop_unchecked(); Some((nr.task, nr.index)) } else { None } // Start counting down from the new reload // mem::transmute::<_, SYST>(()).clear_current(); } } else { // The queue is empty // mem::transmute::<_, SYST>(()).disable_interrupt(); disable_interrupt(); None } } } } pub struct NotReady where Task: Copy, Mono: Monotonic, { pub index: u8, pub instant: Instant, pub task: Task, } impl Eq for NotReady where Task: Copy, Mono: Monotonic, { } impl Ord for NotReady where Task: Copy, Mono: Monotonic, { fn cmp(&self, other: &Self) -> Ordering { self.instant.cmp(&other.instant) } } impl PartialEq for NotReady where Task: Copy, Mono: Monotonic, { fn eq(&self, other: &Self) -> bool { self.instant == other.instant } } impl PartialOrd for NotReady where Task: Copy, Mono: Monotonic, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) } }