mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +01:00
Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic)
This commit is contained in:
parent
b8b881f446
commit
306aa47170
276 changed files with 607 additions and 713 deletions
|
@ -1,121 +0,0 @@
|
||||||
//! examples/cfg-monotonic.rs
|
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use panic_semihosting as _;
|
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])]
|
|
||||||
mod app {
|
|
||||||
use cortex_m_semihosting::{debug, hprintln};
|
|
||||||
use systick_monotonic::*; // Implements the `Monotonic` trait
|
|
||||||
|
|
||||||
// A monotonic timer to enable scheduling in RTIC
|
|
||||||
#[cfg(feature = "killmono")]
|
|
||||||
#[monotonic(binds = SysTick, default = true)]
|
|
||||||
type MyMono = Systick<100>; // 100 Hz / 10 ms granularity
|
|
||||||
|
|
||||||
// Not allowed by current rtic-syntax:
|
|
||||||
// error: `#[monotonic(...)]` on a specific type must appear at most once
|
|
||||||
// --> examples/cfg-monotonic.rs:23:10
|
|
||||||
// |
|
|
||||||
// 23 | type MyMono = Systick<100>; // 100 Hz / 10 ms granularity
|
|
||||||
// | ^^^^^^
|
|
||||||
// #[monotonic(binds = SysTick, default = true)]
|
|
||||||
// type MyMono = Systick<100>; // 100 Hz / 10 ms granularity
|
|
||||||
|
|
||||||
// Not allowed by current rtic-syntax:
|
|
||||||
// error: this interrupt is already bound
|
|
||||||
// --> examples/cfg-monotonic.rs:31:25
|
|
||||||
// |
|
|
||||||
// 31 | #[monotonic(binds = SysTick, default = true)]
|
|
||||||
// | ^^^^^^^
|
|
||||||
// #[monotonic(binds = SysTick, default = true)]
|
|
||||||
// type MyMono2 = DwtSystick<100>; // 100 Hz / 10 ms granularity
|
|
||||||
|
|
||||||
// Resources shared between tasks
|
|
||||||
#[shared]
|
|
||||||
struct Shared {
|
|
||||||
s1: u32,
|
|
||||||
s2: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local resources to specific tasks (cannot be shared)
|
|
||||||
#[local]
|
|
||||||
struct Local {
|
|
||||||
l1: u8,
|
|
||||||
l2: i8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[init]
|
|
||||||
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
|
|
||||||
let _systick = cx.core.SYST;
|
|
||||||
|
|
||||||
// Initialize the monotonic (SysTick rate in QEMU is 12 MHz)
|
|
||||||
#[cfg(feature = "killmono")]
|
|
||||||
let mono = Systick::new(systick, 12_000_000);
|
|
||||||
|
|
||||||
// Spawn the task `foo` directly after `init` finishes
|
|
||||||
foo::spawn().unwrap();
|
|
||||||
|
|
||||||
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
|
|
||||||
|
|
||||||
(
|
|
||||||
// Initialization of shared resources
|
|
||||||
Shared { s1: 0, s2: 1 },
|
|
||||||
// Initialization of task local resources
|
|
||||||
Local { l1: 2, l2: 3 },
|
|
||||||
// Move the monotonic timer to the RTIC run-time, this enables
|
|
||||||
// scheduling
|
|
||||||
#[cfg(feature = "killmono")]
|
|
||||||
init::Monotonics(mono),
|
|
||||||
init::Monotonics(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background task, runs whenever no other tasks are running
|
|
||||||
#[idle]
|
|
||||||
fn idle(_: idle::Context) -> ! {
|
|
||||||
loop {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Software task, not bound to a hardware interrupt.
|
|
||||||
// This task takes the task local resource `l1`
|
|
||||||
// The resources `s1` and `s2` are shared between all other tasks.
|
|
||||||
#[task(shared = [s1, s2], local = [l1])]
|
|
||||||
fn foo(_: foo::Context) {
|
|
||||||
// This task is only spawned once in `init`, hence this task will run
|
|
||||||
// only once
|
|
||||||
|
|
||||||
hprintln!("foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Software task, also not bound to a hardware interrupt
|
|
||||||
// This task takes the task local resource `l2`
|
|
||||||
// The resources `s1` and `s2` are shared between all other tasks.
|
|
||||||
#[task(shared = [s1, s2], local = [l2])]
|
|
||||||
fn bar(_: bar::Context) {
|
|
||||||
hprintln!("bar");
|
|
||||||
|
|
||||||
// Run `bar` once per second
|
|
||||||
// bar::spawn_after(1.secs()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hardware task, bound to a hardware interrupt
|
|
||||||
// The resources `s1` and `s2` are shared between all other tasks.
|
|
||||||
#[task(binds = UART0, priority = 3, shared = [s1, s2])]
|
|
||||||
fn uart0_interrupt(_: uart0_interrupt::Context) {
|
|
||||||
// This task is bound to the interrupt `UART0` and will run
|
|
||||||
// whenever the interrupt fires
|
|
||||||
|
|
||||||
// Note that RTIC does NOT clear the interrupt flag, this is up to the
|
|
||||||
// user
|
|
||||||
|
|
||||||
hprintln!("UART0 interrupt!");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
// NOTE these tests are specific to the Cortex-M port; `rtic-syntax` has a more extensive test suite
|
|
||||||
// that tests functionality common to all the RTIC ports
|
|
||||||
|
|
||||||
mod single;
|
|
|
@ -1,40 +0,0 @@
|
||||||
use quote::quote;
|
|
||||||
use rtic_syntax::Settings;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn analyze() {
|
|
||||||
let mut settings = Settings::default();
|
|
||||||
settings.parse_extern_interrupt = true;
|
|
||||||
let (app, analysis) = rtic_syntax::parse2(
|
|
||||||
// First interrupt is assigned to the highest priority dispatcher
|
|
||||||
quote!(device = pac, dispatchers = [B, A]),
|
|
||||||
quote!(
|
|
||||||
mod app {
|
|
||||||
#[shared]
|
|
||||||
struct Shared {}
|
|
||||||
|
|
||||||
#[local]
|
|
||||||
struct Local {}
|
|
||||||
|
|
||||||
#[init]
|
|
||||||
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
|
|
||||||
(Shared {}, Local {}, init::Monotonics())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(priority = 1)]
|
|
||||||
fn a(_: a::Context) {}
|
|
||||||
|
|
||||||
#[task(priority = 2)]
|
|
||||||
fn b(_: b::Context) {}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
settings,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let analysis = crate::analyze::app(analysis, &app);
|
|
||||||
let interrupts = &analysis.interrupts;
|
|
||||||
assert_eq!(interrupts.len(), 2);
|
|
||||||
assert_eq!(interrupts[&2].0.to_string(), "B");
|
|
||||||
assert_eq!(interrupts[&1].0.to_string(), "A");
|
|
||||||
}
|
|
6
rtic-monotonics/.gitignore
vendored
Normal file
6
rtic-monotonics/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
**/*.rs.bk
|
||||||
|
.#*
|
||||||
|
.gdb_history
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
*.hex
|
12
rtic-monotonics/Cargo.toml
Normal file
12
rtic-monotonics/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "rtic-timer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7.6" }
|
||||||
|
embedded-hal-async = "0.2.0-alpha.0"
|
||||||
|
fugit = { version = "0.3.6", features = ["defmt"] }
|
||||||
|
rtic-timer = { version = "1.0.0", path = "../rtic-timer" }
|
11
rtic-monotonics/src/lib.rs
Normal file
11
rtic-monotonics/src/lib.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
//! Crate
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(async_fn_in_trait)]
|
||||||
|
|
||||||
|
pub use rtic_timer::{Monotonic, TimeoutError, TimerQueue};
|
||||||
|
|
||||||
|
pub mod systick_monotonic;
|
1
rtic-monotonics/src/systick_monotonic.rs
Normal file
1
rtic-monotonics/src/systick_monotonic.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
//! ...
|
6
rtic-timer/.gitignore
vendored
Normal file
6
rtic-timer/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
**/*.rs.bk
|
||||||
|
.#*
|
||||||
|
.gdb_history
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
*.hex
|
|
@ -1,11 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "rtic-timer"
|
name = "rtic-timer"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.7.6"
|
critical-section = "1"
|
||||||
rtic-monotonic = "1.0.0"
|
futures-util = { version = "0.3.25", default-features = false }
|
||||||
fugit = "0.3.6"
|
|
||||||
|
|
4
rtic-timer/rust-toolchain.toml
Normal file
4
rtic-timer/rust-toolchain.toml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
|
||||||
|
targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ]
|
|
@ -1,138 +1,336 @@
|
||||||
|
//! Crate
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
#![feature(async_fn_in_trait)]
|
||||||
|
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
pub mod monotonic;
|
||||||
use core::{cmp::Ordering, task::Waker};
|
|
||||||
use cortex_m::peripheral::{syst::SystClkSource, SYST};
|
|
||||||
pub use fugit::{self, ExtU64};
|
|
||||||
pub use rtic_monotonic::Monotonic;
|
|
||||||
|
|
||||||
mod sll;
|
use core::future::{poll_fn, Future};
|
||||||
use sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode};
|
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
|
use core::task::{Poll, Waker};
|
||||||
|
use futures_util::{
|
||||||
|
future::{select, Either},
|
||||||
|
pin_mut,
|
||||||
|
};
|
||||||
|
pub use monotonic::Monotonic;
|
||||||
|
|
||||||
pub struct Timer {
|
mod linked_list;
|
||||||
cnt: AtomicU32,
|
|
||||||
// queue: IntrusiveSortedLinkedList<'static, WakerNotReady<Mono>, IsslMin>,
|
use linked_list::{Link, LinkedList};
|
||||||
|
|
||||||
|
/// Holds a waker and at which time instant this waker shall be awoken.
|
||||||
|
struct WaitingWaker<Mono: Monotonic> {
|
||||||
|
waker: Waker,
|
||||||
|
release_at: Mono::Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
impl<Mono: Monotonic> Clone for WaitingWaker<Mono> {
|
||||||
#[no_mangle]
|
fn clone(&self) -> Self {
|
||||||
fn SysTick() {
|
Self {
|
||||||
// ..
|
waker: self.waker.clone(),
|
||||||
let cnt = unsafe {
|
release_at: self.release_at,
|
||||||
static mut CNT: u32 = 0;
|
|
||||||
&mut CNT
|
|
||||||
};
|
|
||||||
|
|
||||||
*cnt = cnt.wrapping_add(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Systick implementing `rtic_monotonic::Monotonic` which runs at a
|
|
||||||
/// settable rate using the `TIMER_HZ` parameter.
|
|
||||||
pub struct Systick<const TIMER_HZ: u32> {
|
|
||||||
systick: SYST,
|
|
||||||
cnt: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const TIMER_HZ: u32> Systick<TIMER_HZ> {
|
|
||||||
/// Provide a new `Monotonic` based on SysTick.
|
|
||||||
///
|
|
||||||
/// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from
|
|
||||||
/// the clock generation function of the used HAL.
|
|
||||||
///
|
|
||||||
/// Notice that the actual rate of the timer is a best approximation based on the given
|
|
||||||
/// `sysclk` and `TIMER_HZ`.
|
|
||||||
pub fn new(mut systick: SYST, sysclk: u32) -> Self {
|
|
||||||
// + TIMER_HZ / 2 provides round to nearest instead of round to 0.
|
|
||||||
// - 1 as the counter range is inclusive [0, reload]
|
|
||||||
let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1;
|
|
||||||
|
|
||||||
assert!(reload <= 0x00ff_ffff);
|
|
||||||
assert!(reload > 0);
|
|
||||||
|
|
||||||
systick.disable_counter();
|
|
||||||
systick.set_clock_source(SystClkSource::Core);
|
|
||||||
systick.set_reload(reload);
|
|
||||||
|
|
||||||
Systick { systick, cnt: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const TIMER_HZ: u32> Monotonic for Systick<TIMER_HZ> {
|
|
||||||
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
|
|
||||||
|
|
||||||
type Instant = fugit::TimerInstantU64<TIMER_HZ>;
|
|
||||||
type Duration = fugit::TimerDurationU64<TIMER_HZ>;
|
|
||||||
|
|
||||||
fn now(&mut self) -> Self::Instant {
|
|
||||||
if self.systick.has_wrapped() {
|
|
||||||
self.cnt = self.cnt.wrapping_add(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::Instant::from_ticks(self.cnt)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn reset(&mut self) {
|
|
||||||
self.systick.clear_current();
|
|
||||||
self.systick.enable_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn set_compare(&mut self, _val: Self::Instant) {
|
|
||||||
// No need to do something here, we get interrupts anyway.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn clear_compare_flag(&mut self) {
|
|
||||||
// NOOP with SysTick interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn zero() -> Self::Instant {
|
|
||||||
Self::Instant::from_ticks(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn on_interrupt(&mut self) {
|
|
||||||
if self.systick.has_wrapped() {
|
|
||||||
self.cnt = self.cnt.wrapping_add(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WakerNotReady<Mono>
|
impl<Mono: Monotonic> PartialEq for WaitingWaker<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 {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.instant == other.instant
|
self.release_at == other.release_at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Mono> PartialOrd for WakerNotReady<Mono>
|
impl<Mono: Monotonic> PartialOrd for WaitingWaker<Mono> {
|
||||||
where
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
Mono: Monotonic,
|
self.release_at.partial_cmp(&other.release_at)
|
||||||
{
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic timer queue for async executors.
|
||||||
|
///
|
||||||
|
/// # Blocking
|
||||||
|
///
|
||||||
|
/// The internal priority queue uses global critical sections to manage access. This means that
|
||||||
|
/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock
|
||||||
|
/// duration is ~10 clock cycles per element in the queue.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This timer queue is based on an intrusive linked list, and by extension the links are strored
|
||||||
|
/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is
|
||||||
|
/// complete.
|
||||||
|
///
|
||||||
|
/// Do not call `mem::forget` on an awaited future, or there will be dragons!
|
||||||
|
pub struct TimerQueue<Mono: Monotonic> {
|
||||||
|
queue: LinkedList<WaitingWaker<Mono>>,
|
||||||
|
initialized: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This indicates that there was a timeout.
|
||||||
|
pub struct TimeoutError;
|
||||||
|
|
||||||
|
impl<Mono: Monotonic> TimerQueue<Mono> {
|
||||||
|
/// Make a new queue.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
queue: LinkedList::new(),
|
||||||
|
initialized: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forwards the `Monotonic::now()` method.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn now(&self) -> Mono::Instant {
|
||||||
|
Mono::now()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the initialized monotonic to initialize the TimerQueue.
|
||||||
|
pub fn initialize(&self, monotonic: Mono) {
|
||||||
|
self.initialized.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
// Don't run drop on `Mono`
|
||||||
|
core::mem::forget(monotonic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call this in the interrupt handler of the hardware timer supporting the `Monotonic`
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// It's always safe to call, but it must only be called from the interrupt of the
|
||||||
|
/// monotonic timer for correct operation.
|
||||||
|
pub unsafe fn on_monotonic_interrupt(&self) {
|
||||||
|
Mono::clear_compare_flag();
|
||||||
|
Mono::on_interrupt();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut release_at = None;
|
||||||
|
let head = self.queue.pop_if(|head| {
|
||||||
|
release_at = Some(head.release_at);
|
||||||
|
|
||||||
|
Mono::now() >= head.release_at
|
||||||
|
});
|
||||||
|
|
||||||
|
match (head, release_at) {
|
||||||
|
(Some(link), _) => {
|
||||||
|
link.waker.wake();
|
||||||
|
}
|
||||||
|
(None, Some(instant)) => {
|
||||||
|
Mono::enable_timer();
|
||||||
|
Mono::set_compare(instant);
|
||||||
|
|
||||||
|
if Mono::now() >= instant {
|
||||||
|
// The time for the next instant passed while handling it,
|
||||||
|
// continue dequeueing
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(None, None) => {
|
||||||
|
// Queue is empty
|
||||||
|
Mono::disable_timer();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeout at a specific time.
|
||||||
|
pub async fn timeout_at<F: Future>(
|
||||||
|
&self,
|
||||||
|
instant: Mono::Instant,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
let delay = self.delay_until(instant);
|
||||||
|
|
||||||
|
pin_mut!(future);
|
||||||
|
pin_mut!(delay);
|
||||||
|
|
||||||
|
match select(future, delay).await {
|
||||||
|
Either::Left((r, _)) => Ok(r),
|
||||||
|
Either::Right(_) => Err(TimeoutError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeout after a specific duration.
|
||||||
|
#[inline]
|
||||||
|
pub async fn timeout_after<F: Future>(
|
||||||
|
&self,
|
||||||
|
duration: Mono::Duration,
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, TimeoutError> {
|
||||||
|
self.timeout_at(Mono::now() + duration, future).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay for some duration of time.
|
||||||
|
#[inline]
|
||||||
|
pub async fn delay(&self, duration: Mono::Duration) {
|
||||||
|
let now = Mono::now();
|
||||||
|
|
||||||
|
self.delay_until(now + duration).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay to some specific time instant.
|
||||||
|
pub async fn delay_until(&self, instant: Mono::Instant) {
|
||||||
|
if !self.initialized.load(Ordering::Relaxed) {
|
||||||
|
panic!(
|
||||||
|
"The timer queue is not initialized with a monotonic, you need to run `initialize`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut first_run = true;
|
||||||
|
let queue = &self.queue;
|
||||||
|
let mut link = Link::new(WaitingWaker {
|
||||||
|
waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await,
|
||||||
|
release_at: instant,
|
||||||
|
});
|
||||||
|
|
||||||
|
let marker = &AtomicUsize::new(0);
|
||||||
|
|
||||||
|
let dropper = OnDrop::new(|| {
|
||||||
|
queue.delete(marker.load(Ordering::Relaxed));
|
||||||
|
});
|
||||||
|
|
||||||
|
poll_fn(|_| {
|
||||||
|
if Mono::now() >= instant {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if first_run {
|
||||||
|
first_run = false;
|
||||||
|
let (was_empty, addr) = queue.insert(&mut link);
|
||||||
|
marker.store(addr, Ordering::Relaxed);
|
||||||
|
|
||||||
|
if was_empty {
|
||||||
|
// Pend the monotonic handler if the queue was empty to setup the timer.
|
||||||
|
Mono::pend_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Make sure that our link is deleted from the list before we drop this stack
|
||||||
|
drop(dropper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnDrop<F: FnOnce()> {
|
||||||
|
f: core::mem::MaybeUninit<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> OnDrop<F> {
|
||||||
|
pub fn new(f: F) -> Self {
|
||||||
|
Self {
|
||||||
|
f: core::mem::MaybeUninit::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn defuse(self) {
|
||||||
|
core::mem::forget(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
173
rtic-timer/src/linked_list.rs
Normal file
173
rtic-timer/src/linked_list.rs
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
//! ...
|
||||||
|
|
||||||
|
use core::marker::PhantomPinned;
|
||||||
|
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||||
|
use critical_section as cs;
|
||||||
|
|
||||||
|
/// A sorted linked list for the timer queue.
|
||||||
|
pub struct LinkedList<T> {
|
||||||
|
head: AtomicPtr<Link<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LinkedList<T> {
|
||||||
|
/// Create a new linked list.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
head: AtomicPtr::new(core::ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialOrd + Clone> LinkedList<T> {
|
||||||
|
/// Pop the first element in the queue if the closure returns true.
|
||||||
|
pub fn pop_if<F: FnOnce(&T) -> bool>(&self, f: F) -> Option<T> {
|
||||||
|
cs::with(|_| {
|
||||||
|
// Make sure all previous writes are visible
|
||||||
|
core::sync::atomic::fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let head = self.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link
|
||||||
|
if let Some(head) = unsafe { head.as_ref() } {
|
||||||
|
if f(&head.val) {
|
||||||
|
// Move head to the next element
|
||||||
|
self.head
|
||||||
|
.store(head.next.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||||
|
|
||||||
|
// We read the value at head
|
||||||
|
let head_val = head.val.clone();
|
||||||
|
|
||||||
|
return Some(head_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a link at an address.
|
||||||
|
pub fn delete(&self, addr: usize) {
|
||||||
|
cs::with(|_| {
|
||||||
|
// Make sure all previous writes are visible
|
||||||
|
core::sync::atomic::fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let head = self.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link
|
||||||
|
let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } {
|
||||||
|
head_ref
|
||||||
|
} else {
|
||||||
|
// 1. List is empty, do nothing
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if head as *const _ as usize == addr {
|
||||||
|
// 2. Replace head with head.next
|
||||||
|
self.head
|
||||||
|
.store(head_ref.next.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. search list for correct node
|
||||||
|
let mut curr = head_ref;
|
||||||
|
let mut next = head_ref.next.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link
|
||||||
|
while let Some(next_link) = unsafe { next.as_ref() } {
|
||||||
|
// Next is not null
|
||||||
|
|
||||||
|
if next as *const _ as usize == addr {
|
||||||
|
curr.next
|
||||||
|
.store(next_link.next.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue searching
|
||||||
|
curr = next_link;
|
||||||
|
next = next_link.next.load(Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a new link into the linked list.
|
||||||
|
/// The return is (was_empty, address), where the address of the link is for use with `delete`.
|
||||||
|
pub fn insert(&self, val: &mut Link<T>) -> (bool, usize) {
|
||||||
|
cs::with(|_| {
|
||||||
|
let addr = val as *const _ as usize;
|
||||||
|
|
||||||
|
// Make sure all previous writes are visible
|
||||||
|
core::sync::atomic::fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
let head = self.head.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// 3 cases to handle
|
||||||
|
|
||||||
|
// 1. List is empty, write to head
|
||||||
|
// SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link
|
||||||
|
let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } {
|
||||||
|
head_ref
|
||||||
|
} else {
|
||||||
|
self.head.store(val, Ordering::Relaxed);
|
||||||
|
return (true, addr);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. val needs to go in first
|
||||||
|
if val.val < head_ref.val {
|
||||||
|
// Set current head as next of `val`
|
||||||
|
val.next.store(head, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// `val` is now first in the queue
|
||||||
|
self.head.store(val, Ordering::Relaxed);
|
||||||
|
|
||||||
|
return (false, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. search list for correct place
|
||||||
|
let mut curr = head_ref;
|
||||||
|
let mut next = head_ref.next.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link
|
||||||
|
while let Some(next_link) = unsafe { next.as_ref() } {
|
||||||
|
// Next is not null
|
||||||
|
|
||||||
|
if val.val < next_link.val {
|
||||||
|
// Replace next with `val`
|
||||||
|
val.next.store(next, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Insert `val`
|
||||||
|
curr.next.store(val, Ordering::Relaxed);
|
||||||
|
|
||||||
|
return (false, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue searching
|
||||||
|
curr = next_link;
|
||||||
|
next = next_link.next.load(Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No next, write link to last position in list
|
||||||
|
curr.next.store(val, Ordering::Relaxed);
|
||||||
|
|
||||||
|
(false, addr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A link in the linked list.
|
||||||
|
pub struct Link<T> {
|
||||||
|
val: T,
|
||||||
|
next: AtomicPtr<Link<T>>,
|
||||||
|
_up: PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Link<T> {
|
||||||
|
/// Create a new link.
|
||||||
|
pub const fn new(val: T) -> Self {
|
||||||
|
Self {
|
||||||
|
val,
|
||||||
|
next: AtomicPtr::new(core::ptr::null_mut()),
|
||||||
|
_up: PhantomPinned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
rtic-timer/src/monotonic.rs
Normal file
60
rtic-timer/src/monotonic.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//! ...
|
||||||
|
|
||||||
|
/// # A monotonic clock / counter definition.
|
||||||
|
///
|
||||||
|
/// ## Correctness
|
||||||
|
///
|
||||||
|
/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This
|
||||||
|
/// is a requirement on the time library that the user chooses to use.
|
||||||
|
pub trait Monotonic {
|
||||||
|
/// The time at time zero.
|
||||||
|
const ZERO: Self::Instant;
|
||||||
|
|
||||||
|
/// The type for instant, defining an instant in time.
|
||||||
|
///
|
||||||
|
/// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used.
|
||||||
|
type Instant: Ord
|
||||||
|
+ Copy
|
||||||
|
+ core::ops::Add<Self::Duration, Output = Self::Instant>
|
||||||
|
+ core::ops::Sub<Self::Duration, Output = Self::Instant>
|
||||||
|
+ core::ops::Sub<Self::Instant, Output = Self::Duration>;
|
||||||
|
|
||||||
|
/// The type for duration, defining an duration of time.
|
||||||
|
///
|
||||||
|
/// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used.
|
||||||
|
type Duration;
|
||||||
|
|
||||||
|
/// Get the current time.
|
||||||
|
fn now() -> Self::Instant;
|
||||||
|
|
||||||
|
/// Set the compare value of the timer interrupt.
|
||||||
|
///
|
||||||
|
/// **Note:** This method does not need to handle race conditions of the monotonic, the timer
|
||||||
|
/// queue in RTIC checks this.
|
||||||
|
fn set_compare(instant: Self::Instant);
|
||||||
|
|
||||||
|
/// Clear the compare interrupt flag.
|
||||||
|
fn clear_compare_flag();
|
||||||
|
|
||||||
|
/// Pend the timer's interrupt.
|
||||||
|
fn pend_interrupt();
|
||||||
|
|
||||||
|
/// Optional. Runs on interrupt before any timer queue handling.
|
||||||
|
fn on_interrupt() {}
|
||||||
|
|
||||||
|
/// Optional. This is used to save power, this is called when the timer queue is not empty.
|
||||||
|
///
|
||||||
|
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
|
||||||
|
/// based of `now()` is still valid.
|
||||||
|
///
|
||||||
|
/// NOTE: This may be called more than once.
|
||||||
|
fn enable_timer() {}
|
||||||
|
|
||||||
|
/// Optional. This is used to save power, this is called when the timer queue is empty.
|
||||||
|
///
|
||||||
|
/// Enabling and disabling the monotonic needs to propagate to `now` so that an instant
|
||||||
|
/// based of `now()` is still valid.
|
||||||
|
///
|
||||||
|
/// NOTE: This may be called more than once.
|
||||||
|
fn disable_timer() {}
|
||||||
|
}
|
|
@ -1,421 +0,0 @@
|
||||||
//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC.
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::fmt;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::ptr::NonNull;
|
|
||||||
|
|
||||||
/// Marker for Min sorted [`IntrusiveSortedLinkedList`].
|
|
||||||
pub struct Min;
|
|
||||||
|
|
||||||
/// Marker for Max sorted [`IntrusiveSortedLinkedList`].
|
|
||||||
pub struct Max;
|
|
||||||
|
|
||||||
/// The linked list kind: min-list or max-list
|
|
||||||
pub trait Kind: private::Sealed {
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn ordering() -> Ordering;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Kind for Min {
|
|
||||||
fn ordering() -> Ordering {
|
|
||||||
Ordering::Less
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Kind for Max {
|
|
||||||
fn ordering() -> Ordering {
|
|
||||||
Ordering::Greater
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sealed traits
|
|
||||||
mod private {
|
|
||||||
pub trait Sealed {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl private::Sealed for Max {}
|
|
||||||
impl private::Sealed for Min {}
|
|
||||||
|
|
||||||
/// A node in the [`IntrusiveSortedLinkedList`].
|
|
||||||
pub struct Node<T> {
|
|
||||||
pub val: T,
|
|
||||||
next: Option<NonNull<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Node<T> {
|
|
||||||
pub fn new(val: T) -> Self {
|
|
||||||
Self { val, next: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The linked list.
|
|
||||||
pub struct IntrusiveSortedLinkedList<'a, T, K> {
|
|
||||||
head: Option<NonNull<Node<T>>>,
|
|
||||||
_kind: PhantomData<K>,
|
|
||||||
_lt: PhantomData<&'a ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K>
|
|
||||||
where
|
|
||||||
T: Ord + core::fmt::Debug,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut l = f.debug_list();
|
|
||||||
let mut current = self.head;
|
|
||||||
|
|
||||||
while let Some(head) = current {
|
|
||||||
let head = unsafe { head.as_ref() };
|
|
||||||
current = head.next;
|
|
||||||
|
|
||||||
l.entry(&head.val);
|
|
||||||
}
|
|
||||||
|
|
||||||
l.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
head: None,
|
|
||||||
_kind: PhantomData,
|
|
||||||
_lt: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push to the list.
|
|
||||||
pub fn push(&mut self, new: &'a mut Node<T>) {
|
|
||||||
unsafe {
|
|
||||||
if let Some(head) = self.head {
|
|
||||||
if head.as_ref().val.cmp(&new.val) != K::ordering() {
|
|
||||||
// This is newer than head, replace head
|
|
||||||
new.next = self.head;
|
|
||||||
self.head = Some(NonNull::new_unchecked(new));
|
|
||||||
} else {
|
|
||||||
// It's not head, search the list for the correct placement
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = current.as_ref().next {
|
|
||||||
if next.as_ref().val.cmp(&new.val) != K::ordering() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
new.next = current.as_ref().next;
|
|
||||||
current.as_mut().next = Some(NonNull::new_unchecked(new));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// List is empty, place at head
|
|
||||||
self.head = Some(NonNull::new_unchecked(new))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over the sorted list.
|
|
||||||
pub fn iter(&self) -> Iter<'_, T, K> {
|
|
||||||
Iter {
|
|
||||||
_list: self,
|
|
||||||
index: self.head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find an element in the list that can be changed and resorted.
|
|
||||||
pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, 'a, T, K>>
|
|
||||||
where
|
|
||||||
F: FnMut(&T) -> bool,
|
|
||||||
{
|
|
||||||
let head = self.head?;
|
|
||||||
|
|
||||||
// Special-case, first element
|
|
||||||
if f(&unsafe { head.as_ref() }.val) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: true,
|
|
||||||
prev_index: None,
|
|
||||||
index: self.head,
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut current = head;
|
|
||||||
|
|
||||||
while let Some(next) = unsafe { current.as_ref() }.next {
|
|
||||||
if f(&unsafe { next.as_ref() }.val) {
|
|
||||||
return Some(FindMut {
|
|
||||||
is_head: false,
|
|
||||||
prev_index: Some(current),
|
|
||||||
index: Some(next),
|
|
||||||
list: self,
|
|
||||||
maybe_changed: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Peek at the first element.
|
|
||||||
pub fn peek(&self) -> Option<&T> {
|
|
||||||
self.head.map(|head| unsafe { &head.as_ref().val })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pops the first element in the list.
|
|
||||||
///
|
|
||||||
/// Complexity is worst-case `O(1)`.
|
|
||||||
pub fn pop(&mut self) -> Option<&'a Node<T>> {
|
|
||||||
if let Some(head) = self.head {
|
|
||||||
let v = unsafe { head.as_ref() };
|
|
||||||
self.head = v.next;
|
|
||||||
Some(v)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the linked list is empty.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.head.is_none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for the linked list.
|
|
||||||
pub struct Iter<'a, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
_list: &'a IntrusiveSortedLinkedList<'a, T, K>,
|
|
||||||
index: Option<NonNull<Node<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, K> Iterator for Iter<'a, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
type Item = &'a T;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let index = self.index?;
|
|
||||||
|
|
||||||
let node = unsafe { index.as_ref() };
|
|
||||||
self.index = node.next;
|
|
||||||
|
|
||||||
Some(&node.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comes from [`IntrusiveSortedLinkedList::find_mut`].
|
|
||||||
pub struct FindMut<'a, 'b, T, K>
|
|
||||||
where
|
|
||||||
T: Ord + 'b,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
list: &'a mut IntrusiveSortedLinkedList<'b, T, K>,
|
|
||||||
is_head: bool,
|
|
||||||
prev_index: Option<NonNull<Node<T>>>,
|
|
||||||
index: Option<NonNull<Node<T>>>,
|
|
||||||
maybe_changed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, T, K> FindMut<'a, 'b, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
unsafe fn pop_internal(&mut self) -> &'b mut Node<T> {
|
|
||||||
if self.is_head {
|
|
||||||
// If it is the head element, we can do a normal pop
|
|
||||||
let mut head = self.list.head.unwrap_unchecked();
|
|
||||||
let v = head.as_mut();
|
|
||||||
self.list.head = v.next;
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
// Somewhere in the list
|
|
||||||
let mut prev = self.prev_index.unwrap_unchecked();
|
|
||||||
let mut curr = self.index.unwrap_unchecked();
|
|
||||||
|
|
||||||
// Re-point the previous index
|
|
||||||
prev.as_mut().next = curr.as_ref().next;
|
|
||||||
|
|
||||||
curr.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will pop the element from the list.
|
|
||||||
///
|
|
||||||
/// Complexity is worst-case `O(1)`.
|
|
||||||
#[inline]
|
|
||||||
pub fn pop(mut self) -> &'b mut Node<T> {
|
|
||||||
unsafe { self.pop_internal() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will resort the element into the correct position in the list if needed. The resorting
|
|
||||||
/// will only happen if the element has been accessed mutably.
|
|
||||||
///
|
|
||||||
/// Same as calling `drop`.
|
|
||||||
///
|
|
||||||
/// Complexity is worst-case `O(N)`.
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
drop(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b, T, K> Drop for FindMut<'_, 'b, T, K>
|
|
||||||
where
|
|
||||||
T: Ord + 'b,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Only resort the list if the element has changed
|
|
||||||
if self.maybe_changed {
|
|
||||||
unsafe {
|
|
||||||
let val = self.pop_internal();
|
|
||||||
self.list.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, K> Deref for FindMut<'_, '_, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { &self.index.unwrap_unchecked().as_ref().val }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, K> DerefMut for FindMut<'_, '_, T, K>
|
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
K: Kind,
|
|
||||||
{
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self.maybe_changed = true;
|
|
||||||
unsafe { &mut self.index.unwrap_unchecked().as_mut().val }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn const_new() {
|
|
||||||
static mut _V1: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_peek() {
|
|
||||||
let mut ll: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
let mut a = Node { val: 1, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
let mut a = Node { val: 2, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
let mut a = Node { val: 3, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &3);
|
|
||||||
|
|
||||||
let mut ll: IntrusiveSortedLinkedList<u32, Min> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
let mut a = Node { val: 2, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &2);
|
|
||||||
|
|
||||||
let mut a = Node { val: 1, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
|
|
||||||
let mut a = Node { val: 3, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let ll: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
assert!(ll.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_updating() {
|
|
||||||
let mut ll: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
let mut a = Node { val: 1, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
|
|
||||||
let mut a = Node { val: 2, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
|
|
||||||
let mut a = Node { val: 3, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_updating_1() {
|
|
||||||
let mut ll: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
let mut a = Node { val: 1, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
|
|
||||||
let v = ll.pop().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(v.val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_updating_2() {
|
|
||||||
let mut ll: IntrusiveSortedLinkedList<u32, Max> = IntrusiveSortedLinkedList::new();
|
|
||||||
|
|
||||||
let mut a = Node { val: 1, next: None };
|
|
||||||
ll.push(&mut a);
|
|
||||||
|
|
||||||
let mut find = ll.find_mut(|v| *v == 1).unwrap();
|
|
||||||
|
|
||||||
*find += 1000;
|
|
||||||
find.finish();
|
|
||||||
|
|
||||||
assert_eq!(ll.peek().unwrap(), &1001);
|
|
||||||
}
|
|
||||||
}
|
|
6
rtic/.gitignore
vendored
Normal file
6
rtic/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
**/*.rs.bk
|
||||||
|
.#*
|
||||||
|
.gdb_history
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
*.hex
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue