use crate::{ time::{Clock, 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 Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && 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() } #[inline] fn unwrapper(val: Result) -> T { if let Ok(v) = val { v } else { unreachable!("Your monotonic is not infallible") } } /// Dequeue a task from the TimerQueue #[inline] pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> where F: FnOnce(), { mono.clear_compare_flag(); if let Some(instant) = self.0.peek().map(|p| p.instant) { if instant < Self::unwrapper(Clock::try_now(mono)) { // task became ready let nr = unsafe { 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 < Self::unwrapper(Clock::try_now(mono)) { let nr = unsafe { self.0.pop_unchecked() }; Some((nr.task, nr.index)) } else { None } } } else { // The queue is empty, disable the interrupt. if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { 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)) } }