Example running, timeout and delay futures available

This commit is contained in:
Emil Fresk 2022-06-10 20:08:46 +02:00
parent 13ccd92e63
commit b2ec1fa651
12 changed files with 724 additions and 974 deletions

View file

@ -4,7 +4,7 @@ use core::{
sync::atomic::{AtomicBool, Ordering},
};
pub use crate::tq::{NotReady, TimerQueue};
pub use crate::tq::{TaskNotReady, TaskOrWaker, TimerQueue, WakerNotReady};
pub use bare_metal::CriticalSection;
pub use cortex_m::{
asm::nop,
@ -16,8 +16,74 @@ pub use cortex_m::{
pub use heapless::sorted_linked_list::SortedLinkedList;
pub use heapless::spsc::Queue;
pub use heapless::BinaryHeap;
pub use heapless::Vec;
pub use rtic_monotonic as monotonic;
pub mod executor {
use core::{
future::Future,
mem,
pin::Pin,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
static WAKER_VTABLE: RawWakerVTable =
RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop);
unsafe fn waker_clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &WAKER_VTABLE)
}
unsafe fn waker_wake(p: *const ()) {
// The only thing we need from a waker is the function to call to pend the async
// dispatcher.
let f: fn() = mem::transmute(p);
f();
}
unsafe fn waker_drop(_: *const ()) {
// nop
}
//============
// AsyncTaskExecutor
pub struct AsyncTaskExecutor<F: Future + 'static> {
task: Option<F>,
}
impl<F: Future + 'static> AsyncTaskExecutor<F> {
pub const fn new() -> Self {
Self { task: None }
}
pub fn is_running(&self) -> bool {
self.task.is_some()
}
pub fn spawn(&mut self, future: F) {
self.task = Some(future);
}
pub fn poll(&mut self, wake: fn()) {
if let Some(future) = &mut self.task {
unsafe {
let waker = Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE));
let mut cx = Context::from_waker(&waker);
let future = Pin::new_unchecked(future);
match future.poll(&mut cx) {
Poll::Ready(_) => {
self.task = None;
}
Poll::Pending => {}
};
}
}
}
}
}
pub type SCFQ<const N: usize> = Queue<u8, N>;
pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>;

270
src/tq.rs
View file

@ -1,29 +1,25 @@
use crate::Monotonic;
use core::cmp::Ordering;
use core::task::Waker;
use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList};
pub struct TimerQueue<Mono, Task, const N: usize>(
pub SortedLinkedList<NotReady<Mono, Task>, LinkedIndexU16, Min, N>,
)
where
Mono: Monotonic,
Task: Copy;
impl<Mono, Task, const N: usize> TimerQueue<Mono, Task, N>
pub struct TimerQueue<Mono, Task, const N_TASK: usize, const N_WAKER: usize>
where
Mono: Monotonic,
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<F1, F2>(
&mut self,
nr: NotReady<Mono, Task>,
pub task_queue: SortedLinkedList<TaskNotReady<Mono, Task>, LinkedIndexU16, Min, N_TASK>,
pub waker_queue: SortedLinkedList<WakerNotReady<Mono>, LinkedIndexU16, Min, N_WAKER>,
}
impl<Mono, Task, const N_TASK: usize, const N_WAKER: usize> TimerQueue<Mono, Task, N_TASK, N_WAKER>
where
Mono: Monotonic,
Task: Copy,
{
fn check_if_enable<F1, F2>(
&self,
instant: Mono::Instant,
enable_interrupt: F1,
pend_handler: F2,
mono: Option<&mut Mono>,
@ -33,11 +29,17 @@ where
{
// 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_or(true, |head| nr.instant < head.instant);
let if_task_heap_max_greater_than_nr = self
.task_queue
.peek()
.map_or(true, |head| instant < head.instant);
let if_waker_heap_max_greater_than_nr = self
.waker_queue
.peek()
.map_or(true, |head| instant < head.instant);
if if_heap_max_greater_than_nr {
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() {
if if_task_heap_max_greater_than_nr || if_waker_heap_max_greater_than_nr {
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.is_empty() {
if let Some(mono) = mono {
mono.enable_timer();
}
@ -46,19 +48,50 @@ where
pend_handler();
}
self.0.push_unchecked(nr);
}
/// Check if the timer queue is empty.
/// Enqueue a task without checking if it is full
#[inline]
pub unsafe fn enqueue_task_unchecked<F1, F2>(
&mut self,
nr: TaskNotReady<Mono, Task>,
enable_interrupt: F1,
pend_handler: F2,
mono: Option<&mut Mono>,
) where
F1: FnOnce(),
F2: FnOnce(),
{
self.check_if_enable(nr.instant, enable_interrupt, pend_handler, mono);
self.task_queue.push_unchecked(nr);
}
/// Enqueue a waker
#[inline]
pub fn enqueue_waker<F1, F2>(
&mut self,
nr: WakerNotReady<Mono>,
enable_interrupt: F1,
pend_handler: F2,
mono: Option<&mut Mono>,
) -> Result<(), ()>
where
F1: FnOnce(),
F2: FnOnce(),
{
self.check_if_enable(nr.instant, enable_interrupt, pend_handler, mono);
self.waker_queue.push(nr).map_err(|_| ())
}
/// Check if all the timer queue is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
self.task_queue.is_empty() && self.waker_queue.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) {
/// Cancel the marker value for a task
pub fn cancel_task_marker(&mut self, marker: u32) -> Option<(Task, u8)> {
if let Some(val) = self.task_queue.find_mut(|nr| nr.marker == marker) {
let nr = val.pop();
Some((nr.task, nr.index))
@ -67,16 +100,23 @@ where
}
}
/// Update the instant at an marker value to a new instant
/// Cancel the marker value for a waker
pub fn cancel_waker_marker(&mut self, marker: u32) {
if let Some(val) = self.waker_queue.find_mut(|nr| nr.marker == marker) {
let _ = val.pop();
}
}
/// Update the instant at an marker value for a task to a new instant
#[allow(clippy::result_unit_err)]
pub fn update_marker<F: FnOnce()>(
pub fn update_task_marker<F: FnOnce()>(
&mut self,
marker: u32,
new_marker: u32,
instant: Mono::Instant,
pend_handler: F,
) -> Result<(), ()> {
if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) {
if let Some(mut val) = self.task_queue.find_mut(|nr| nr.marker == marker) {
val.instant = instant;
val.marker = new_marker;
@ -89,66 +129,134 @@ where
}
}
fn dequeue_task_queue(
&mut self,
instant: Mono::Instant,
mono: &mut Mono,
) -> Option<TaskOrWaker<Task>> {
let now = mono.now();
if instant <= now {
// task became ready
let nr = unsafe { self.task_queue.pop_unchecked() };
Some(TaskOrWaker::Task((nr.task, nr.index)))
} else {
// Set compare
mono.set_compare(instant);
// 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 <= now {
let nr = unsafe { self.task_queue.pop_unchecked() };
Some(TaskOrWaker::Task((nr.task, nr.index)))
} else {
None
}
}
}
fn dequeue_waker_queue(
&mut self,
instant: Mono::Instant,
mono: &mut Mono,
) -> Option<TaskOrWaker<Task>> {
let now = mono.now();
if instant <= now {
// task became ready
let nr = unsafe { self.waker_queue.pop_unchecked() };
Some(TaskOrWaker::Waker(nr.waker))
} else {
// Set compare
mono.set_compare(instant);
// 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 <= now {
let nr = unsafe { self.waker_queue.pop_unchecked() };
Some(TaskOrWaker::Waker(nr.waker))
} else {
None
}
}
}
/// Dequeue a task from the ``TimerQueue``
pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)>
pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<TaskOrWaker<Task>>
where
F: FnOnce(),
{
mono.clear_compare_flag();
if let Some(instant) = self.0.peek().map(|p| p.instant) {
if instant <= mono.now() {
// task became ready
let nr = unsafe { self.0.pop_unchecked() };
let tq = self.task_queue.peek().map(|p| p.instant);
let wq = self.waker_queue.peek().map(|p| p.instant);
Some((nr.task, nr.index))
} else {
// Set compare
mono.set_compare(instant);
let dequeue_task;
let instant;
// 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 = unsafe { self.0.pop_unchecked() };
Some((nr.task, nr.index))
match (tq, wq) {
(Some(tq_instant), Some(wq_instant)) => {
if tq_instant <= wq_instant {
dequeue_task = true;
instant = tq_instant;
} else {
None
dequeue_task = false;
instant = wq_instant;
}
}
} else {
// The queue is empty, disable the interrupt.
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
disable_interrupt();
mono.disable_timer();
(Some(tq_instant), None) => {
dequeue_task = true;
instant = tq_instant;
}
(None, Some(wq_instant)) => {
dequeue_task = false;
instant = wq_instant;
}
(None, None) => {
// The queue is empty, disable the interrupt.
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
disable_interrupt();
mono.disable_timer();
}
None
return None;
}
}
if dequeue_task {
self.dequeue_task_queue(instant, mono)
} else {
self.dequeue_waker_queue(instant, mono)
}
}
}
pub struct NotReady<Mono, Task>
pub enum TaskOrWaker<Task> {
Task((Task, u8)),
Waker(Waker),
}
pub struct TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
{
pub task: Task,
pub index: u8,
pub instant: Mono::Instant,
pub task: Task,
pub marker: u32,
}
impl<Mono, Task> Eq for NotReady<Mono, Task>
impl<Mono, Task> Eq for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
{
}
impl<Mono, Task> Ord for NotReady<Mono, Task>
impl<Mono, Task> Ord for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@ -158,7 +266,7 @@ where
}
}
impl<Mono, Task> PartialEq for NotReady<Mono, Task>
impl<Mono, Task> PartialEq for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@ -168,7 +276,7 @@ where
}
}
impl<Mono, Task> PartialOrd for NotReady<Mono, Task>
impl<Mono, Task> PartialOrd for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@ -177,3 +285,41 @@ where
Some(self.cmp(other))
}
}
pub struct WakerNotReady<Mono>
where
Mono: Monotonic,
{
pub waker: Waker,
pub instant: Mono::Instant,
pub marker: u32,
}
impl<Mono> Eq for WakerNotReady<Mono> where Mono: Monotonic {}
impl<Mono> Ord for WakerNotReady<Mono>
where
Mono: Monotonic,
{
fn cmp(&self, other: &Self) -> Ordering {
self.instant.cmp(&other.instant)
}
}
impl<Mono> PartialEq for WakerNotReady<Mono>
where
Mono: Monotonic,
{
fn eq(&self, other: &Self) -> bool {
self.instant == other.instant
}
}
impl<Mono> PartialOrd for WakerNotReady<Mono>
where
Mono: Monotonic,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}