mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-20 14:55:30 +01:00
Example running, timeout and delay futures available
This commit is contained in:
parent
13ccd92e63
commit
b2ec1fa651
12 changed files with 724 additions and 974 deletions
|
|
@ -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
270
src/tq.rs
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue