Cancel and reschedule working

Support cfgs in the imports

Account for extern tasks
This commit is contained in:
Emil Fresk 2021-03-14 21:27:21 +01:00
parent 1087f2ee64
commit 53c407017f
7 changed files with 716 additions and 35 deletions

View file

@ -32,7 +32,7 @@
#![deny(missing_docs)]
#![deny(rust_2018_compatibility)]
#![deny(rust_2018_idioms)]
#![deny(warnings)]
// #![deny(warnings)]
#![no_std]
use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC};
@ -43,6 +43,8 @@ pub use rtic_monotonic::{self, embedded_time as time, Monotonic};
#[doc(hidden)]
pub mod export;
#[doc(hidden)]
mod linked_list;
#[doc(hidden)]
mod tq;
/// Sets the given `interrupt` as pending

599
src/linked_list.rs Normal file
View file

@ -0,0 +1,599 @@
use core::fmt;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr;
pub use generic_array::ArrayLength;
use generic_array::GenericArray;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct LinkedIndex(u16);
impl LinkedIndex {
#[inline]
const unsafe fn new_unchecked(value: u16) -> Self {
LinkedIndex(value)
}
#[inline]
const fn none() -> Self {
LinkedIndex(u16::MAX)
}
#[inline]
const fn option(self) -> Option<u16> {
if self.0 == u16::MAX {
None
} else {
Some(self.0)
}
}
}
/// A node in the linked list.
pub struct Node<T> {
val: MaybeUninit<T>,
next: LinkedIndex,
}
/// Iterator for the linked list.
pub struct Iter<'a, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
list: &'a LinkedList<T, Kind, N>,
index: LinkedIndex,
}
impl<'a, T, Kind, N> Iterator for Iter<'a, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let index = self.index.option()?;
let node = self.list.node_at(index as usize);
self.index = node.next;
Some(self.list.read_data_in_node_at(index as usize))
}
}
/// Comes from [`LinkedList::find_mut`].
pub struct FindMut<'a, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
list: &'a mut LinkedList<T, Kind, N>,
is_head: bool,
prev_index: LinkedIndex,
index: LinkedIndex,
maybe_changed: bool,
}
impl<'a, T, Kind, N> FindMut<'a, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn pop_internal(&mut self) -> T {
if self.is_head {
// If it is the head element, we can do a normal pop
unsafe { self.list.pop_unchecked() }
} else {
// Somewhere in the list
// Re-point the previous index
self.list.node_at_mut(self.prev_index.0 as usize).next =
self.list.node_at_mut(self.index.0 as usize).next;
// Release the index into the free queue
self.list.node_at_mut(self.index.0 as usize).next = self.list.free;
self.list.free = self.index;
self.list.extract_data_in_node_at(self.index.0 as usize)
}
}
/// This will pop the element from the list.
///
/// Complexity is O(1).
#[inline]
pub fn pop(mut self) -> T {
self.pop_internal()
}
/// This will resort the element into the correct position in the list in needed.
/// Same as calling `drop`.
///
/// Complexity is worst-case O(N).
#[inline]
pub fn finish(self) {
drop(self)
}
}
impl<T, Kind, N> Drop for FindMut<'_, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn drop(&mut self) {
// Only resort the list if the element has changed
if self.maybe_changed {
let val = self.pop_internal();
unsafe { self.list.push_unchecked(val) };
}
}
}
impl<T, Kind, N> Deref for FindMut<'_, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.list.read_data_in_node_at(self.index.0 as usize)
}
}
impl<T, Kind, N> DerefMut for FindMut<'_, T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.maybe_changed = true;
self.list.read_mut_data_in_node_at(self.index.0 as usize)
}
}
impl<T, Kind, N> fmt::Debug for FindMut<'_, T, Kind, N>
where
T: PartialEq + PartialOrd + core::fmt::Debug,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FindMut")
.field("prev_index", &self.prev_index)
.field("index", &self.index)
.field(
"prev_value",
&self
.list
.read_data_in_node_at(self.prev_index.option().unwrap() as usize),
)
.field(
"value",
&self
.list
.read_data_in_node_at(self.index.option().unwrap() as usize),
)
.finish()
}
}
/// The linked list.
pub struct LinkedList<T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
list: MaybeUninit<GenericArray<Node<T>, N>>,
head: LinkedIndex,
free: LinkedIndex,
_kind: PhantomData<Kind>,
}
impl<T, Kind, N> LinkedList<T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn node_at(&self, index: usize) -> &Node<T> {
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
unsafe { &*(self.list.as_ptr() as *const Node<T>).add(index) }
}
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn node_at_mut(&mut self, index: usize) -> &mut Node<T> {
// Safety: The entire `self.list` is initialized in `new`, which makes this safe.
unsafe { &mut *(self.list.as_mut_ptr() as *mut Node<T>).add(index) }
}
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn write_data_in_node_at(&mut self, index: usize, data: T) {
unsafe {
self.node_at_mut(index).val.as_mut_ptr().write(data);
}
}
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn read_data_in_node_at(&self, index: usize) -> &T {
unsafe { &*self.node_at(index).val.as_ptr() }
}
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T {
unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() }
}
/// Internal helper to not do pointer arithmetic all over the place.
#[inline]
fn extract_data_in_node_at(&mut self, index: usize) -> T {
unsafe { self.node_at(index).val.as_ptr().read() }
}
/// Internal helper to not do pointer arithmetic all over the place.
/// Safety: This can overwrite existing allocated nodes if used improperly, meaning their
/// `Drop` methods won't run.
#[inline]
unsafe fn write_node_at(&mut self, index: usize, node: Node<T>) {
(self.list.as_mut_ptr() as *mut Node<T>)
.add(index)
.write(node)
}
/// Create a new linked list.
pub fn new() -> Self {
let mut list = LinkedList {
list: MaybeUninit::uninit(),
head: LinkedIndex::none(),
free: unsafe { LinkedIndex::new_unchecked(0) },
_kind: PhantomData,
};
let len = N::U16;
let mut free = 0;
// Initialize indexes
while free < len - 1 {
unsafe {
list.write_node_at(
free as usize,
Node {
val: MaybeUninit::uninit(),
next: LinkedIndex::new_unchecked(free + 1),
},
);
}
free += 1;
}
// Initialize final index
unsafe {
list.write_node_at(
free as usize,
Node {
val: MaybeUninit::uninit(),
next: LinkedIndex::none(),
},
);
}
list
}
/// Push unchecked
///
/// Complexity is O(N).
///
/// # Safety
///
/// Assumes that the list is not full.
pub unsafe fn push_unchecked(&mut self, value: T) {
let new = self.free.0;
// Store the data and update the next free spot
self.write_data_in_node_at(new as usize, value);
self.free = self.node_at(new as usize).next;
if let Some(head) = self.head.option() {
// Check if we need to replace head
if self
.read_data_in_node_at(head as usize)
.partial_cmp(self.read_data_in_node_at(new as usize))
!= Kind::ordering()
{
self.node_at_mut(new as usize).next = self.head;
self.head = LinkedIndex::new_unchecked(new);
} else {
// It's not head, search the list for the correct placement
let mut current = head;
while let Some(next) = self.node_at(current as usize).next.option() {
if self
.read_data_in_node_at(next as usize)
.partial_cmp(self.read_data_in_node_at(new as usize))
!= Kind::ordering()
{
break;
}
current = next;
}
self.node_at_mut(new as usize).next = self.node_at(current as usize).next;
self.node_at_mut(current as usize).next = LinkedIndex::new_unchecked(new);
}
} else {
self.node_at_mut(new as usize).next = self.head;
self.head = LinkedIndex::new_unchecked(new);
}
}
/// Pushes an element to the linked list and sorts it into place.
///
/// Complexity is O(N).
pub fn push(&mut self, value: T) -> Result<(), T> {
if !self.is_full() {
Ok(unsafe { self.push_unchecked(value) })
} else {
Err(value)
}
}
/// Get an iterator over the sorted list.
pub fn iter(&self) -> Iter<'_, T, Kind, N> {
Iter {
list: self,
index: self.head,
}
}
/// Find an element in the list.
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Kind, N>>
where
F: FnMut(&T) -> bool,
{
let head = self.head.option()?;
// Special-case, first element
if f(self.read_data_in_node_at(head as usize)) {
return Some(FindMut {
is_head: true,
prev_index: LinkedIndex::none(),
index: self.head,
list: self,
maybe_changed: false,
});
}
let mut current = head;
while let Some(next) = self.node_at(current as usize).next.option() {
if f(self.read_data_in_node_at(next as usize)) {
return Some(FindMut {
is_head: false,
prev_index: unsafe { LinkedIndex::new_unchecked(current) },
index: unsafe { LinkedIndex::new_unchecked(next) },
list: self,
maybe_changed: false,
});
}
current = next;
}
None
}
/// Peek at the first element.
pub fn peek(&self) -> Option<&T> {
self.head
.option()
.map(|head| self.read_data_in_node_at(head as usize))
}
/// Pop unchecked
///
/// # Safety
///
/// Assumes that the list is not empty.
pub unsafe fn pop_unchecked(&mut self) -> T {
let head = self.head.0;
let current = head;
self.head = self.node_at(head as usize).next;
self.node_at_mut(current as usize).next = self.free;
self.free = LinkedIndex::new_unchecked(current);
self.extract_data_in_node_at(current as usize)
}
/// Pops the first element in the list.
///
/// Complexity is O(1).
pub fn pop(&mut self) -> Result<T, ()> {
if !self.is_empty() {
Ok(unsafe { self.pop_unchecked() })
} else {
Err(())
}
}
/// Checks if the linked list is full.
#[inline]
pub fn is_full(&self) -> bool {
self.free.option().is_none()
}
/// Checks if the linked list is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.head.option().is_none()
}
}
impl<T, Kind, N> Drop for LinkedList<T, Kind, N>
where
T: PartialEq + PartialOrd,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn drop(&mut self) {
let mut index = self.head;
while let Some(i) = index.option() {
let node = self.node_at_mut(i as usize);
index = node.next;
unsafe {
ptr::drop_in_place(node.val.as_mut_ptr());
}
}
}
}
impl<T, Kind, N> fmt::Debug for LinkedList<T, Kind, N>
where
T: PartialEq + PartialOrd + core::fmt::Debug,
Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
/// Min sorted linked list.
pub struct Min;
/// Max sorted linked list.
pub struct Max;
/// Sealed traits and implementations for `linked_list`
pub mod kind {
use super::{Max, Min};
use core::cmp::Ordering;
/// The linked list kind: min first or max first
pub unsafe trait Kind {
#[doc(hidden)]
fn ordering() -> Option<Ordering>;
}
unsafe impl Kind for Min {
#[inline]
fn ordering() -> Option<Ordering> {
Some(Ordering::Less)
}
}
unsafe impl Kind for Max {
#[inline]
fn ordering() -> Option<Ordering> {
Some(Ordering::Greater)
}
}
}
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
use generic_array::typenum::consts::*;
#[test]
fn test_peek() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new();
ll.push(1).unwrap();
assert_eq!(ll.peek().unwrap(), &1);
ll.push(2).unwrap();
assert_eq!(ll.peek().unwrap(), &2);
ll.push(3).unwrap();
assert_eq!(ll.peek().unwrap(), &3);
let mut ll: LinkedList<u32, Min, U3> = LinkedList::new();
ll.push(2).unwrap();
assert_eq!(ll.peek().unwrap(), &2);
ll.push(1).unwrap();
assert_eq!(ll.peek().unwrap(), &1);
ll.push(3).unwrap();
assert_eq!(ll.peek().unwrap(), &1);
}
#[test]
fn test_full() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new();
ll.push(1).unwrap();
ll.push(2).unwrap();
ll.push(3).unwrap();
assert!(ll.is_full())
}
#[test]
fn test_empty() {
let ll: LinkedList<u32, Max, U3> = LinkedList::new();
assert!(ll.is_empty())
}
#[test]
fn test_rejected_push() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new();
ll.push(1).unwrap();
ll.push(2).unwrap();
ll.push(3).unwrap();
// This won't fit
let r = ll.push(4);
assert_eq!(r, Err(4));
}
#[test]
fn test_updating() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new();
ll.push(1).unwrap();
ll.push(2).unwrap();
ll.push(3).unwrap();
let mut find = ll.find_mut(|v| *v == 2).unwrap();
*find += 1000;
find.finish();
assert_eq!(ll.peek().unwrap(), &1002);
let mut find = ll.find_mut(|v| *v == 3).unwrap();
*find += 1000;
find.finish();
assert_eq!(ll.peek().unwrap(), &1003);
// Remove largest element
ll.find_mut(|v| *v == 1003).unwrap().pop();
assert_eq!(ll.peek().unwrap(), &1002);
}
}

View file

@ -1,11 +1,11 @@
use crate::{
linked_list::{ArrayLength, LinkedList, Min, Node},
time::{Clock, Instant},
Monotonic,
};
use core::cmp::Ordering;
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
#[inline]
#[inline(always)]
fn unwrapper<T, E>(val: Result<T, E>) -> T {
if let Ok(v) = val {
v
@ -14,18 +14,22 @@ fn unwrapper<T, E>(val: Result<T, E>) -> T {
}
}
pub struct TimerQueue<Mono, Task, N>(pub BinaryHeap<NotReady<Mono, Task>, N, Min>)
pub struct TimerQueue<Mono, Task, N>(pub LinkedList<NotReady<Mono, Task>, Min, N>)
where
Mono: Monotonic,
N: ArrayLength<NotReady<Mono, Task>>,
N: ArrayLength<Node<NotReady<Mono, Task>>>,
Task: Copy;
impl<Mono, Task, N> TimerQueue<Mono, Task, N>
where
Mono: Monotonic,
N: ArrayLength<NotReady<Mono, Task>>,
N: ArrayLength<Node<NotReady<Mono, Task>>>,
Task: Copy,
{
pub fn new() -> Self {
TimerQueue(LinkedList::new())
}
/// # Safety
///
/// Writing to memory with a transmute in order to enable
@ -43,26 +47,20 @@ 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
})
.map(|head| 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();A
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() {
mono.enable_timer();
enable_interrupt();
}
// Set SysTick pending
// SCB::set_pendst();
pend_handler();
}
@ -75,8 +73,39 @@ where
self.0.is_empty()
}
/// Cancel the marker value
pub fn cancel_marker(&mut self, marker: u32) -> Option<(Task, u8)> {
if let Some(val) = self.0.find_mut(|nr| nr.marker == marker) {
let nr = val.pop();
Some((nr.task, nr.index))
} else {
None
}
}
/// Update the instant at an marker value to a new instant
pub fn update_marker<F: FnOnce()>(
&mut self,
marker: u32,
new_marker: u32,
instant: Instant<Mono>,
pend_handler: F,
) -> Result<(), ()> {
if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) {
val.instant = instant;
val.marker = new_marker;
// On update pend the handler to reconfigure the next compare match
pend_handler();
Ok(())
} else {
Err(())
}
}
/// Dequeue a task from the TimerQueue
#[inline]
pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)>
where
F: FnOnce(),