mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-27 14:04:56 +01:00
Optimize linked list popping so delete is not run everytime
This commit is contained in:
parent
1f9eb45dd0
commit
897bcf78fe
2 changed files with 28 additions and 107 deletions
|
@ -6,9 +6,8 @@
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
|
||||||
pub mod monotonic;
|
|
||||||
|
|
||||||
use core::future::{poll_fn, Future};
|
use core::future::{poll_fn, Future};
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use core::task::{Poll, Waker};
|
use core::task::{Poll, Waker};
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
|
@ -18,20 +17,25 @@ use futures_util::{
|
||||||
pub use monotonic::Monotonic;
|
pub use monotonic::Monotonic;
|
||||||
|
|
||||||
mod linked_list;
|
mod linked_list;
|
||||||
|
mod monotonic;
|
||||||
|
|
||||||
use linked_list::{Link, LinkedList};
|
use linked_list::{Link, LinkedList};
|
||||||
|
|
||||||
/// Holds a waker and at which time instant this waker shall be awoken.
|
/// Holds a waker and at which time instant this waker shall be awoken.
|
||||||
struct WaitingWaker<Mono: Monotonic> {
|
struct WaitingWaker<Mono: Monotonic> {
|
||||||
waker: Waker,
|
// This is alway initialized when used, we create this struct on the async stack and then
|
||||||
|
// initialize the waker field in the `poll_fn` closure (we then know the waker)
|
||||||
|
waker: MaybeUninit<Waker>,
|
||||||
release_at: Mono::Instant,
|
release_at: Mono::Instant,
|
||||||
|
was_poped: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
|
impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
waker: self.waker.clone(),
|
waker: MaybeUninit::new(unsafe { self.waker.assume_init_ref() }.clone()),
|
||||||
release_at: self.release_at,
|
release_at: self.release_at,
|
||||||
|
was_poped: AtomicBool::new(self.was_poped.load(Ordering::Relaxed)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,12 +113,15 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
let head = self.queue.pop_if(|head| {
|
let head = self.queue.pop_if(|head| {
|
||||||
release_at = Some(head.release_at);
|
release_at = Some(head.release_at);
|
||||||
|
|
||||||
Mono::now() >= head.release_at
|
let should_pop = Mono::now() >= head.release_at;
|
||||||
|
head.was_poped.store(should_pop, Ordering::Relaxed);
|
||||||
|
|
||||||
|
should_pop
|
||||||
});
|
});
|
||||||
|
|
||||||
match (head, release_at) {
|
match (head, release_at) {
|
||||||
(Some(link), _) => {
|
(Some(link), _) => {
|
||||||
link.waker.wake();
|
link.waker.assume_init().wake();
|
||||||
}
|
}
|
||||||
(None, Some(instant)) => {
|
(None, Some(instant)) => {
|
||||||
Mono::enable_timer();
|
Mono::enable_timer();
|
||||||
|
@ -181,26 +188,28 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut first_run = true;
|
|
||||||
let queue = &self.queue;
|
|
||||||
let mut link = Link::new(WaitingWaker {
|
let mut link = Link::new(WaitingWaker {
|
||||||
waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await,
|
waker: MaybeUninit::uninit(),
|
||||||
release_at: instant,
|
release_at: instant,
|
||||||
|
was_poped: AtomicBool::new(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut first_run = true;
|
||||||
|
let queue = &self.queue;
|
||||||
let marker = &AtomicUsize::new(0);
|
let marker = &AtomicUsize::new(0);
|
||||||
|
|
||||||
let dropper = OnDrop::new(|| {
|
let dropper = OnDrop::new(|| {
|
||||||
queue.delete(marker.load(Ordering::Relaxed));
|
queue.delete(marker.load(Ordering::Relaxed));
|
||||||
});
|
});
|
||||||
|
|
||||||
poll_fn(|_| {
|
poll_fn(|cx| {
|
||||||
if Mono::now() >= instant {
|
if Mono::now() >= instant {
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_run {
|
if first_run {
|
||||||
first_run = false;
|
first_run = false;
|
||||||
|
link.val.waker.write(cx.waker().clone());
|
||||||
let (was_empty, addr) = queue.insert(&mut link);
|
let (was_empty, addr) = queue.insert(&mut link);
|
||||||
marker.store(addr, Ordering::Relaxed);
|
marker.store(addr, Ordering::Relaxed);
|
||||||
|
|
||||||
|
@ -214,8 +223,13 @@ impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Make sure that our link is deleted from the list before we drop this stack
|
if link.val.was_poped.load(Ordering::Relaxed) {
|
||||||
drop(dropper);
|
// If it was poped from the queue there is no need to run delete
|
||||||
|
dropper.defuse();
|
||||||
|
} else {
|
||||||
|
// Make sure that our link is deleted from the list before we drop this stack
|
||||||
|
drop(dropper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,96 +255,3 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
unsafe { self.f.as_ptr().read()() }
|
unsafe { self.f.as_ptr().read()() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- Test program ---------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// use systick_monotonic::{Systick, TimerQueue};
|
|
||||||
//
|
|
||||||
// // same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
|
||||||
// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
|
||||||
// #[defmt::panic_handler]
|
|
||||||
// fn panic() -> ! {
|
|
||||||
// cortex_m::asm::udf()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// Terminates the application and makes `probe-run` exit with exit-code = 0
|
|
||||||
// pub fn exit() -> ! {
|
|
||||||
// loop {
|
|
||||||
// cortex_m::asm::bkpt();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// defmt::timestamp!("{=u64:us}", {
|
|
||||||
// let time_us: fugit::MicrosDurationU32 = MONO.now().duration_since_epoch().convert();
|
|
||||||
//
|
|
||||||
// time_us.ticks() as u64
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// make_systick_timer_queue!(MONO, Systick<1_000>);
|
|
||||||
//
|
|
||||||
// #[rtic::app(
|
|
||||||
// device = nrf52832_hal::pac,
|
|
||||||
// dispatchers = [SWI0_EGU0, SWI1_EGU1, SWI2_EGU2, SWI3_EGU3, SWI4_EGU4, SWI5_EGU5],
|
|
||||||
// )]
|
|
||||||
// mod app {
|
|
||||||
// use super::{Systick, MONO};
|
|
||||||
// use fugit::ExtU32;
|
|
||||||
//
|
|
||||||
// #[shared]
|
|
||||||
// struct Shared {}
|
|
||||||
//
|
|
||||||
// #[local]
|
|
||||||
// struct Local {}
|
|
||||||
//
|
|
||||||
// #[init]
|
|
||||||
// fn init(cx: init::Context) -> (Shared, Local) {
|
|
||||||
// defmt::println!("init");
|
|
||||||
//
|
|
||||||
// let systick = Systick::start(cx.core.SYST, 64_000_000);
|
|
||||||
//
|
|
||||||
// defmt::println!("initializing monotonic");
|
|
||||||
//
|
|
||||||
// MONO.initialize(systick);
|
|
||||||
//
|
|
||||||
// async_task::spawn().ok();
|
|
||||||
// async_task2::spawn().ok();
|
|
||||||
// async_task3::spawn().ok();
|
|
||||||
//
|
|
||||||
// (Shared {}, Local {})
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[idle]
|
|
||||||
// fn idle(_: idle::Context) -> ! {
|
|
||||||
// defmt::println!("idle");
|
|
||||||
//
|
|
||||||
// loop {
|
|
||||||
// core::hint::spin_loop();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[task]
|
|
||||||
// async fn async_task(_: async_task::Context) {
|
|
||||||
// loop {
|
|
||||||
// defmt::println!("async task waiting for 1 second");
|
|
||||||
// MONO.delay(1.secs()).await;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[task]
|
|
||||||
// async fn async_task2(_: async_task2::Context) {
|
|
||||||
// loop {
|
|
||||||
// defmt::println!(" async task 2 waiting for 0.5 second");
|
|
||||||
// MONO.delay(500.millis()).await;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[task]
|
|
||||||
// async fn async_task3(_: async_task3::Context) {
|
|
||||||
// loop {
|
|
||||||
// defmt::println!(" async task 3 waiting for 0.2 second");
|
|
||||||
// MONO.delay(200.millis()).await;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicPtr, Ordering};
|
||||||
use critical_section as cs;
|
use critical_section as cs;
|
||||||
|
|
||||||
/// A sorted linked list for the timer queue.
|
/// A sorted linked list for the timer queue.
|
||||||
pub struct LinkedList<T> {
|
pub(crate) struct LinkedList<T> {
|
||||||
head: AtomicPtr<Link<T>>,
|
head: AtomicPtr<Link<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ impl<T: PartialOrd + Clone> LinkedList<T> {
|
||||||
|
|
||||||
/// A link in the linked list.
|
/// A link in the linked list.
|
||||||
pub struct Link<T> {
|
pub struct Link<T> {
|
||||||
val: T,
|
pub(crate) val: T,
|
||||||
next: AtomicPtr<Link<T>>,
|
next: AtomicPtr<Link<T>>,
|
||||||
_up: PhantomPinned,
|
_up: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue