diff --git a/README.md b/README.md index 3cf1b61f..29c4a221 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ GPIO pins are set up for the `nrf52840-dk` board (PCA10056) - Install `probe-run` with defmt support. ``` -cargo install --git https://github.com/knurling-rs/probe-run --branch main --features defmt +cargo install probe-run ``` - Run the example diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 5b834ff1..710c5a15 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -157,17 +157,17 @@ pub fn interrupt_take(item: TokenStream) -> TokenStream { static HANDLER: ::embassy::interrupt::Handler; } - let func = HANDLER.func.load(::embassy::atomic::Ordering::Acquire); - let ctx = HANDLER.ctx.load(::embassy::atomic::Ordering::Acquire); + let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Acquire); + let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Acquire); if !func.is_null() { let func: fn(*mut ()) = ::core::mem::transmute(func); func(ctx) } } - static TAKEN: ::embassy::atomic::AtomicBool = ::embassy::atomic::AtomicBool::new(false); + static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); - if TAKEN.compare_exchange(false, true, ::embassy::atomic::Ordering::AcqRel, ::embassy::atomic::Ordering::Acquire).is_err() { + if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { panic!("IRQ Already taken"); } diff --git a/embassy-nrf-examples/src/bin/buffered_uart.rs b/embassy-nrf-examples/src/bin/buffered_uart.rs index 42e81229..71e9b4a7 100644 --- a/embassy-nrf-examples/src/bin/buffered_uart.rs +++ b/embassy-nrf-examples/src/bin/buffered_uart.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/executor_fairness_test.rs b/embassy-nrf-examples/src/bin/executor_fairness_test.rs index 1b995573..6d0a311d 100644 --- a/embassy-nrf-examples/src/bin/executor_fairness_test.rs +++ b/embassy-nrf-examples/src/bin/executor_fairness_test.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/gpiote_channel.rs b/embassy-nrf-examples/src/bin/gpiote_channel.rs index 4c4dabc6..3764ba1c 100644 --- a/embassy-nrf-examples/src/bin/gpiote_channel.rs +++ b/embassy-nrf-examples/src/bin/gpiote_channel.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/gpiote_port.rs b/embassy-nrf-examples/src/bin/gpiote_port.rs index ccb5bb97..36ee1c8e 100644 --- a/embassy-nrf-examples/src/bin/gpiote_port.rs +++ b/embassy-nrf-examples/src/bin/gpiote_port.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/multiprio.rs b/embassy-nrf-examples/src/bin/multiprio.rs index d652633a..f5ebf67f 100644 --- a/embassy-nrf-examples/src/bin/multiprio.rs +++ b/embassy-nrf-examples/src/bin/multiprio.rs @@ -55,6 +55,8 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs index 587ed7dd..7d8a45f7 100644 --- a/embassy-nrf-examples/src/bin/qspi.rs +++ b/embassy-nrf-examples/src/bin/qspi.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/rtc_async.rs b/embassy-nrf-examples/src/bin/rtc_async.rs index 5fb68633..ec437425 100644 --- a/embassy-nrf-examples/src/bin/rtc_async.rs +++ b/embassy-nrf-examples/src/bin/rtc_async.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/rtc_raw.rs b/embassy-nrf-examples/src/bin/rtc_raw.rs index 017487da..884ca92b 100644 --- a/embassy-nrf-examples/src/bin/rtc_raw.rs +++ b/embassy-nrf-examples/src/bin/rtc_raw.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/spim.rs b/embassy-nrf-examples/src/bin/spim.rs index 0a284dc8..d6b3a5f8 100644 --- a/embassy-nrf-examples/src/bin/spim.rs +++ b/embassy-nrf-examples/src/bin/spim.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index 41f9e127..0acd6fde 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bb37ec36..2c72b912 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(generic_associated_types)] #![feature(asm)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #![allow(incomplete_features)] diff --git a/embassy-std-examples/src/bin/serial.rs b/embassy-std-examples/src/bin/serial.rs index cad64fb2..6d628fe1 100644 --- a/embassy-std-examples/src/bin/serial.rs +++ b/embassy-std-examples/src/bin/serial.rs @@ -1,3 +1,5 @@ +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../serial_port.rs"] diff --git a/embassy-std-examples/src/bin/tick.rs b/embassy-std-examples/src/bin/tick.rs index af2305f3..96eef067 100644 --- a/embassy-std-examples/src/bin/tick.rs +++ b/embassy-std-examples/src/bin/tick.rs @@ -1,3 +1,5 @@ +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] use embassy::executor::task; diff --git a/embassy-stm32f4-examples/src/bin/exti.rs b/embassy-stm32f4-examples/src/bin/exti.rs index 6b3568d6..0c6561cd 100644 --- a/embassy-stm32f4-examples/src/bin/exti.rs +++ b/embassy-stm32f4-examples/src/bin/exti.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/hello.rs b/embassy-stm32f4-examples/src/bin/hello.rs index 77c54464..8a665414 100644 --- a/embassy-stm32f4-examples/src/bin/hello.rs +++ b/embassy-stm32f4-examples/src/bin/hello.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/rtc_async.rs b/embassy-stm32f4-examples/src/bin/rtc_async.rs index e5321226..41ce2d4f 100644 --- a/embassy-stm32f4-examples/src/bin/rtc_async.rs +++ b/embassy-stm32f4-examples/src/bin/rtc_async.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/serial.rs b/embassy-stm32f4-examples/src/bin/serial.rs index 6351f728..b138bfdd 100644 --- a/embassy-stm32f4-examples/src/bin/serial.rs +++ b/embassy-stm32f4-examples/src/bin/serial.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4/src/exti.rs b/embassy-stm32f4/src/exti.rs index 52cf5a29..90f3ade3 100644 --- a/embassy-stm32f4/src/exti.rs +++ b/embassy-stm32f4/src/exti.rs @@ -10,6 +10,7 @@ use crate::hal::gpio; use crate::hal::gpio::{Edge, ExtiPin as HalExtiPin}; use crate::hal::syscfg::SysCfg; use crate::pac::EXTI; +use embedded_hal::digital::v2 as digital; use crate::interrupt; @@ -42,6 +43,52 @@ pub struct ExtiPin { _mgr: &'static ExtiManager, } +impl digital::OutputPin for ExtiPin { + type Error = T::Error; + + fn set_low(&mut self) -> Result<(), Self::Error> { + self.pin.set_low() + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.pin.set_high() + } +} + +impl digital::StatefulOutputPin + for ExtiPin +{ + fn is_set_low(&self) -> Result { + self.pin.is_set_low() + } + + fn is_set_high(&self) -> Result { + self.pin.is_set_high() + } +} + +impl digital::ToggleableOutputPin + for ExtiPin +{ + type Error = T::Error; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self.pin.toggle() + } +} + +impl digital::InputPin for ExtiPin { + type Error = T::Error; + + fn is_high(&self) -> Result { + self.pin.is_high() + } + + fn is_low(&self) -> Result { + self.pin.is_low() + } +} + /* Irq Handler Description EXTI0_IRQn EXTI0_IRQHandler Handler for pins connected to line 0 @@ -482,7 +529,7 @@ exti!(gpioj, [ feature = "stm32f469", feature = "stm32f479" ))] -exti!(gpioj, [ +exti!(gpiok, [ EXTI0 => PK0, EXTI1 => PK1, EXTI2 => PK2, diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index aa31951d..e5e82cd5 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(generic_associated_types)] #![feature(asm)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #![allow(incomplete_features)] @@ -313,5 +315,6 @@ pub(crate) mod fmt; pub mod exti; pub mod interrupt; +pub mod qei; pub mod rtc; pub mod serial; diff --git a/embassy-stm32f4/src/qei.rs b/embassy-stm32f4/src/qei.rs new file mode 100644 index 00000000..1d4689e1 --- /dev/null +++ b/embassy-stm32f4/src/qei.rs @@ -0,0 +1,95 @@ +use crate::interrupt; +use core::future::Future; +use core::pin::Pin; +use embassy::interrupt::Interrupt; +use embassy::traits::qei::WaitForRotate; +use embedded_hal::Direction; +use embedded_hal::Qei as THQei; +use stm32f4xx_hal::pac::TIM2; +use stm32f4xx_hal::qei::{Pins, Qei as HalQei}; + +pub struct Qei { + qei: HalQei, + int: T::Interrupt, +} + +impl> Qei { + pub fn tim2(tim: TIM2, pins: PINS, interrupt: interrupt::TIM2) -> Self { + let qei = HalQei::tim2(tim, pins); + + let tim = unsafe { + &mut *(stm32f4xx_hal::stm32::TIM2::ptr() + as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock) + }; + /* + enable qei interrupt + */ + tim.dier.write(|w| w.uie().set_bit()); + + Qei { + qei: qei, + int: interrupt, + } + } +} + +impl + 'static> WaitForRotate for Qei { + type RotateFuture<'a> = impl Future + 'a; + + fn wait_for_rotate<'a>( + self: Pin<&'a mut Self>, + count_down: u16, + count_up: u16, + ) -> Self::RotateFuture<'a> { + let s = unsafe { self.get_unchecked_mut() }; + + let tim = unsafe { + &mut *(stm32f4xx_hal::stm32::TIM2::ptr() + as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock) + }; + + /* + the interrupt will be reached at zero or the max count + write the total range to the qei. + */ + tim.arr + .write(|w| unsafe { w.bits((count_down + count_up) as u32) }); + + /* + set timer to the correct value in the range + */ + tim.cnt.write(|w| unsafe { w.bits(count_down as u32) }); + + /* + clear interrupt flag + */ + tim.sr.write(|w| w.uif().clear_bit()); + + async move { + embassy::util::InterruptFuture::new(&mut s.int).await; + + if tim.cnt.read().bits() == 0 { + Direction::Downcounting + } else if tim.cnt.read() == count_down + count_up { + Direction::Upcounting + } else { + panic!("unexpected value") + } + } + } +} + +mod sealed { + pub trait Sealed {} +} + +pub trait Instance: sealed::Sealed { + type Interrupt: interrupt::Interrupt; +} + +#[cfg(feature = "stm32f405")] +impl sealed::Sealed for TIM2 {} +#[cfg(feature = "stm32f405")] +impl Instance for TIM2 { + type Interrupt = interrupt::TIM2; +} diff --git a/embassy-traits/Cargo.toml b/embassy-traits/Cargo.toml index 626bf9f3..737f9118 100644 --- a/embassy-traits/Cargo.toml +++ b/embassy-traits/Cargo.toml @@ -9,3 +9,4 @@ std = [] [dependencies] defmt = { version = "0.2.0", optional = true } +embedded-hal = { version = "0.2.3", features = ["unproven"] } diff --git a/embassy-traits/src/i2c.rs b/embassy-traits/src/i2c.rs index 4dc8865e..70420388 100644 --- a/embassy-traits/src/i2c.rs +++ b/embassy-traits/src/i2c.rs @@ -91,8 +91,7 @@ impl AddressMode for SevenBitAddress {} impl AddressMode for TenBitAddress {} -/// Blocking read -pub trait Read { +pub trait I2c { /// Error type type Error; diff --git a/embassy-traits/src/lib.rs b/embassy-traits/src/lib.rs index 431a0252..81a847ef 100644 --- a/embassy-traits/src/lib.rs +++ b/embassy-traits/src/lib.rs @@ -4,11 +4,14 @@ #![feature(const_fn_fn_ptr_basics)] #![feature(const_option)] #![allow(incomplete_features)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] pub mod delay; pub mod flash; pub mod gpio; pub mod i2c; +pub mod qei; pub mod spi; pub mod uart; diff --git a/embassy-traits/src/qei.rs b/embassy-traits/src/qei.rs new file mode 100644 index 00000000..73581256 --- /dev/null +++ b/embassy-traits/src/qei.rs @@ -0,0 +1,22 @@ +use core::future::Future; +use core::pin::Pin; +use embedded_hal::Direction; + +// Wait for a specified number of rotations either up or down +pub trait WaitForRotate { + type RotateFuture<'a>: Future + 'a; + + /// Wait for a specified number of rotations, in ticks, either up or down. + /// + /// Return Direction::Upcounting if the high bound is reached. + /// Return Direction::Downcounting if the low bound is reached. + /// + /// Number of ticks is encoder dependent. As an example, if we connect + /// the Bourns PEC11H-4120F-S0020, we have 20 ticks per full rotation. + /// Other encoders may vary. + fn wait_for_rotate<'a>( + self: Pin<&'a mut Self>, + count_down: u16, + count_up: u16, + ) -> Self::RotateFuture<'a>; +} diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index c58fcbf2..39ad64a0 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml @@ -13,6 +13,8 @@ defmt-info = [] defmt-warn = [] defmt-error = [] +executor-agnostic = [] + [dependencies] defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index 7a6caf70..787230c0 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs @@ -1,5 +1,6 @@ pub use embassy_macros::task; +use atomic_polyfill::Ordering; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; @@ -15,7 +16,6 @@ mod util; mod waker; use self::util::UninitCell; -use crate::atomic::Ordering; use crate::fmt::panic; use crate::interrupt::{Interrupt, InterruptExt}; use crate::time::Alarm; diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw.rs index 0c947080..84e171df 100644 --- a/embassy/src/executor/raw.rs +++ b/embassy/src/executor/raw.rs @@ -1,3 +1,4 @@ +use atomic_polyfill::{AtomicU32, Ordering}; use core::cell::Cell; use core::cmp::min; use core::marker::PhantomData; @@ -9,7 +10,6 @@ use super::run_queue::{RunQueue, RunQueueItem}; use super::timer_queue::{TimerQueue, TimerQueueItem}; use super::util::UninitCell; use super::waker; -use crate::atomic::{AtomicU32, Ordering}; use crate::time::{Alarm, Instant}; /// Task is spawned (has a future) diff --git a/embassy/src/executor/run_queue.rs b/embassy/src/executor/run_queue.rs index fb7c65e0..1d1023e5 100644 --- a/embassy/src/executor/run_queue.rs +++ b/embassy/src/executor/run_queue.rs @@ -1,8 +1,8 @@ +use atomic_polyfill::{AtomicPtr, Ordering}; use core::ptr; use core::ptr::NonNull; use super::raw::Task; -use crate::atomic::{AtomicPtr, Ordering}; pub(crate) struct RunQueueItem { next: AtomicPtr, diff --git a/embassy/src/executor/timer_queue.rs b/embassy/src/executor/timer_queue.rs index bd043aeb..d72eb93b 100644 --- a/embassy/src/executor/timer_queue.rs +++ b/embassy/src/executor/timer_queue.rs @@ -1,10 +1,10 @@ +use atomic_polyfill::{AtomicPtr, Ordering}; use core::cell::Cell; use core::cmp::min; use core::ptr; use core::ptr::NonNull; use super::raw::{Task, STATE_TIMER_QUEUED}; -use crate::atomic::{AtomicPtr, Ordering}; use crate::time::Instant; pub(crate) struct TimerQueueItem { diff --git a/embassy/src/interrupt.rs b/embassy/src/interrupt.rs index 6491a57d..013e722e 100644 --- a/embassy/src/interrupt.rs +++ b/embassy/src/interrupt.rs @@ -1,7 +1,7 @@ use core::ptr; use cortex_m::peripheral::NVIC; -use crate::atomic::{AtomicBool, AtomicPtr, Ordering}; +use atomic_polyfill::{AtomicBool, AtomicPtr, Ordering}; pub use embassy_macros::interrupt_declare as declare; pub use embassy_macros::interrupt_take as take; diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index b7ed4e70..b6fefc9f 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -4,6 +4,8 @@ #![feature(const_fn_fn_ptr_basics)] #![feature(const_option)] #![allow(incomplete_features)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. @@ -16,4 +18,9 @@ pub mod time; pub mod util; pub use embassy_traits as traits; -pub use atomic_polyfill as atomic; + +#[doc(hidden)] +/// Implementation details for embassy macros. DO NOT USE. +pub mod export { + pub use atomic_polyfill as atomic; +} diff --git a/embassy/src/util/forever.rs b/embassy/src/util/forever.rs index fac3c260..ac23a3ce 100644 --- a/embassy/src/util/forever.rs +++ b/embassy/src/util/forever.rs @@ -1,7 +1,7 @@ use core::cell::UnsafeCell; use core::mem::MaybeUninit; -use crate::atomic::{AtomicBool, Ordering}; +use atomic_polyfill::{AtomicBool, Ordering}; pub struct Forever { used: AtomicBool, diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index ae434a8b..e64e7f1f 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs @@ -3,6 +3,8 @@ mod forever; mod mutex; mod portal; mod signal; + +#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")] mod waker; pub use drop_bomb::*; diff --git a/embassy/src/util/waker.rs b/embassy/src/util/waker.rs index 68e45cf1..1f2d3a77 100644 --- a/embassy/src/util/waker.rs +++ b/embassy/src/util/waker.rs @@ -1,11 +1,14 @@ -use core::mem; -use core::task::Context; +use core::ptr::{self, NonNull}; use core::task::Waker; +use atomic_polyfill::{AtomicPtr, Ordering}; + +use crate::executor::raw::{task_from_waker, wake_task, Task}; + /// Utility struct to register and wake a waker. #[derive(Debug)] pub struct WakerRegistration { - waker: Option, + waker: Option>, } impl WakerRegistration { @@ -15,37 +18,61 @@ impl WakerRegistration { /// Register a waker. Overwrites the previous waker, if any. pub fn register(&mut self, w: &Waker) { + let w = unsafe { task_from_waker(w) }; match self.waker { - // Optimization: If both the old and new Wakers wake the same task, we can simply - // keep the old waker, skipping the clone. (In most executor implementations, - // cloning a waker is somewhat expensive, comparable to cloning an Arc). - Some(ref w2) if (w2.will_wake(w)) => {} - _ => { - // clone the new waker and store it - if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - old_waker.wake() - } + // Optimization: If both the old and new Wakers wake the same task, do nothing. + Some(w2) if w == w2 => {} + Some(w2) => { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + + unsafe { wake_task(w2) } + self.waker = Some(w); } + None => self.waker = Some(w), } } /// Wake the registered waker, if any. pub fn wake(&mut self) { if let Some(w) = self.waker.take() { - w.wake() + unsafe { wake_task(w) } + } + } +} + +pub struct AtomicWakerRegistration { + waker: AtomicPtr, +} + +impl AtomicWakerRegistration { + pub const fn new() -> Self { + Self { + waker: AtomicPtr::new(ptr::null_mut()), } } - pub fn context(&self) -> Option> { - self.waker.as_ref().map(|w| Context::from_waker(w)) + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + let w = unsafe { task_from_waker(w) }; + let w2 = self.waker.swap(w.as_ptr(), Ordering::Relaxed); + if !w2.is_null() && w2 != w.as_ptr() { + unsafe { wake_task(NonNull::new_unchecked(w2)) }; + } + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + let w2 = self.waker.swap(ptr::null_mut(), Ordering::Relaxed); + if !w2.is_null() { + unsafe { wake_task(NonNull::new_unchecked(w2)) }; + } } } diff --git a/embassy/src/util/waker_agnostic.rs b/embassy/src/util/waker_agnostic.rs new file mode 100644 index 00000000..b4234c0f --- /dev/null +++ b/embassy/src/util/waker_agnostic.rs @@ -0,0 +1,87 @@ +use core::cell::Cell; +use core::mem; +use core::task::Waker; + +use cortex_m::interrupt::Mutex; + +/// Utility struct to register and wake a waker. +#[derive(Debug)] +pub struct WakerRegistration { + waker: Option, +} + +impl WakerRegistration { + pub const fn new() -> Self { + Self { waker: None } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + if let Some(w) = self.waker.take() { + w.wake() + } + } +} + +/// Utility struct to register and wake a waker. +pub struct AtomicWakerRegistration { + waker: Mutex>>, +} + +impl AtomicWakerRegistration { + pub const fn new() -> Self { + Self { + waker: Mutex::new(Cell::new(None)), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + cortex_m::interrupt::free(|cs| { + let cell = self.waker.borrow(cs); + cell.set(match cell.replace(None) { + Some(w2) if (w2.will_wake(w)) => Some(w2), + Some(w2) => { + w2.wake(); + Some(w.clone()) + } + None => Some(w.clone()), + }) + }) + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + cortex_m::interrupt::free(|cs| { + let cell = self.waker.borrow(cs); + if let Some(w) = cell.replace(None) { + w.wake() + } + }) + } +}