embassy: Refactor module structure to remove kitchen-sink util
.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
//! Blocking mutex (not async)
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use critical_section::CriticalSection;
|
||||
|
4
embassy/src/channel/mod.rs
Normal file
4
embassy/src/channel/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! Async channels
|
||||
|
||||
pub mod mpsc;
|
||||
pub mod signal;
|
@@ -49,11 +49,8 @@ use core::task::Waker;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
use super::CriticalSectionMutex;
|
||||
use super::Mutex;
|
||||
use super::NoopMutex;
|
||||
use super::ThreadModeMutex;
|
||||
use super::WakerRegistration;
|
||||
use crate::blocking_mutex::{CriticalSectionMutex, Mutex, NoopMutex, ThreadModeMutex};
|
||||
use crate::waitqueue::WakerRegistration;
|
||||
|
||||
/// Send values to the associated `Receiver`.
|
||||
///
|
||||
@@ -108,8 +105,8 @@ unsafe impl<'ch, M, T, const N: usize> Sync for Receiver<'ch, M, T, N> where
|
||||
/// their channel. The following will therefore fail compilation:
|
||||
////
|
||||
/// ```compile_fail
|
||||
/// use embassy::util::mpsc;
|
||||
/// use embassy::util::mpsc::{Channel, WithThreadModeOnly};
|
||||
/// use embassy::channel::mpsc;
|
||||
/// use embassy::channel::mpsc::{Channel, WithThreadModeOnly};
|
||||
///
|
||||
/// let (sender, receiver) = {
|
||||
/// let mut channel = Channel::<WithThreadModeOnly, u32, 3>::with_thread_mode_only();
|
||||
@@ -635,8 +632,8 @@ where
|
||||
/// Establish a new bounded channel. For example, to create one with a NoopMutex:
|
||||
///
|
||||
/// ```
|
||||
/// use embassy::util::mpsc;
|
||||
/// use embassy::util::mpsc::{Channel, WithNoThreads};
|
||||
/// use embassy::channel::mpsc;
|
||||
/// use embassy::channel::mpsc::{Channel, WithNoThreads};
|
||||
///
|
||||
/// // Declare a bounded channel of 3 u32s.
|
||||
/// let mut channel = Channel::<WithNoThreads, u32, 3>::new();
|
73
embassy/src/channel/signal.rs
Normal file
73
embassy/src/channel/signal.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
/// Synchronization primitive. Allows creating awaitable signals that may be passed between tasks.
|
||||
///
|
||||
/// For more advanced use cases, please consider [futures-intrusive](https://crates.io/crates/futures-intrusive) channels or mutexes.
|
||||
pub struct Signal<T> {
|
||||
state: UnsafeCell<State<T>>,
|
||||
}
|
||||
|
||||
enum State<T> {
|
||||
None,
|
||||
Waiting(Waker),
|
||||
Signaled(T),
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Signal<T> {}
|
||||
unsafe impl<T: Send> Sync for Signal<T> {}
|
||||
|
||||
impl<T: Send> Signal<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: UnsafeCell::new(State::None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this Signal as completed.
|
||||
pub fn signal(&self, val: T) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) {
|
||||
waker.wake();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
*state = State::None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
match state {
|
||||
State::None => {
|
||||
*state = State::Waiting(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
|
||||
State::Waiting(_) => panic!("waker overflow"),
|
||||
State::Signaled(_) => match mem::replace(state, State::None) {
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Future that completes when this Signal has been signaled.
|
||||
pub fn wait(&self) -> impl Future<Output = T> + '_ {
|
||||
futures::future::poll_fn(move |cx| self.poll_wait(cx))
|
||||
}
|
||||
|
||||
/// non-blocking method to check whether this signal has been signaled.
|
||||
pub fn signaled(&self) -> bool {
|
||||
critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
|
||||
}
|
||||
}
|
@@ -7,6 +7,10 @@
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub mod blocking_mutex;
|
||||
pub mod channel;
|
||||
pub mod waitqueue;
|
||||
|
||||
pub mod executor;
|
||||
pub mod interrupt;
|
||||
pub mod io;
|
||||
|
@@ -1,28 +0,0 @@
|
||||
use core::mem;
|
||||
|
||||
/// An explosive ordinance that panics if it is improperly disposed of.
|
||||
///
|
||||
/// This is to forbid dropping futures, when there is absolutely no other choice.
|
||||
///
|
||||
/// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse)
|
||||
/// method before this object is dropped.
|
||||
pub struct DropBomb {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl DropBomb {
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
/// Diffuses the bomb, rendering it safe to drop.
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropBomb {
|
||||
fn drop(&mut self) {
|
||||
panic!("boom")
|
||||
}
|
||||
}
|
@@ -1,21 +1,7 @@
|
||||
//! Async utilities
|
||||
mod drop_bomb;
|
||||
//! Misc utilities
|
||||
mod forever;
|
||||
mod mutex;
|
||||
mod on_drop;
|
||||
mod signal;
|
||||
|
||||
#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")]
|
||||
mod waker;
|
||||
|
||||
pub use drop_bomb::*;
|
||||
pub use forever::*;
|
||||
pub mod mpsc;
|
||||
pub use mutex::*;
|
||||
pub use on_drop::*;
|
||||
pub use signal::*;
|
||||
pub use waker::*;
|
||||
|
||||
/// Unsafely unborrow an owned singleton out of a `&mut`.
|
||||
///
|
||||
/// It is intended to be implemented for owned peripheral singletons, such as `USART3` or `AnyPin`.
|
||||
|
@@ -1,24 +0,0 @@
|
||||
use core::mem;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
pub struct OnDrop<F: FnOnce()> {
|
||||
f: MaybeUninit<F>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> OnDrop<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
Self {
|
||||
f: MaybeUninit::new(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.f.as_ptr().read()() }
|
||||
}
|
||||
}
|
@@ -1,171 +0,0 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
use cortex_m::peripheral::NVIC;
|
||||
use cortex_m::peripheral::{scb, SCB};
|
||||
use executor::raw::TaskHeader;
|
||||
use ptr::NonNull;
|
||||
|
||||
use crate::executor;
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
|
||||
/// Synchronization primitive. Allows creating awaitable signals that may be passed between tasks.
|
||||
///
|
||||
/// For more advanced use cases, please consider [futures-intrusive](https://crates.io/crates/futures-intrusive) channels or mutexes.
|
||||
pub struct Signal<T> {
|
||||
state: UnsafeCell<State<T>>,
|
||||
}
|
||||
|
||||
enum State<T> {
|
||||
None,
|
||||
Waiting(Waker),
|
||||
Signaled(T),
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Signal<T> {}
|
||||
unsafe impl<T: Send> Sync for Signal<T> {}
|
||||
|
||||
impl<T: Send> Signal<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: UnsafeCell::new(State::None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this Signal as completed.
|
||||
pub fn signal(&self, val: T) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) {
|
||||
waker.wake();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
*state = State::None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
match state {
|
||||
State::None => {
|
||||
*state = State::Waiting(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
|
||||
State::Waiting(_) => panic!("waker overflow"),
|
||||
State::Signaled(_) => match mem::replace(state, State::None) {
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Future that completes when this Signal has been signaled.
|
||||
pub fn wait(&self) -> impl Future<Output = T> + '_ {
|
||||
futures::future::poll_fn(move |cx| self.poll_wait(cx))
|
||||
}
|
||||
|
||||
/// non-blocking method to check whether this signal has been signaled.
|
||||
pub fn signaled(&self) -> bool {
|
||||
critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
|
||||
}
|
||||
}
|
||||
|
||||
// ==========
|
||||
|
||||
pub fn wake_on_interrupt(interrupt: &mut impl Interrupt, waker: &Waker) {
|
||||
interrupt.disable();
|
||||
interrupt.set_handler(irq_wake_handler);
|
||||
interrupt.set_handler_context(unsafe { executor::raw::task_from_waker(waker) }.as_ptr() as _);
|
||||
interrupt.enable();
|
||||
}
|
||||
|
||||
unsafe fn irq_wake_handler(ctx: *mut ()) {
|
||||
if let Some(task) = NonNull::new(ctx as *mut TaskHeader) {
|
||||
executor::raw::wake_task(task);
|
||||
}
|
||||
|
||||
let irq = match SCB::vect_active() {
|
||||
scb::VectActive::Interrupt { irqn } => irqn,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
NVIC::mask(crate::interrupt::NrWrap(irq as u16));
|
||||
}
|
||||
|
||||
// ==========
|
||||
|
||||
struct NrWrap(u8);
|
||||
unsafe impl cortex_m::interrupt::Nr for NrWrap {
|
||||
fn nr(&self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a future that completes when the specified Interrupt is triggered.
|
||||
///
|
||||
/// The input handler is unregistered when this Future is dropped.
|
||||
///
|
||||
/// Example:
|
||||
/// ``` no_compile
|
||||
/// use embassy::traits::*;
|
||||
/// use embassy::util::InterruptFuture;
|
||||
/// use embassy_stm32::interrupt; // Adjust this to your MCU's embassy HAL.
|
||||
/// #[embassy::task]
|
||||
/// async fn demo_interrupt_future() {
|
||||
/// // Using STM32f446 interrupt names, adjust this to your application as necessary.
|
||||
/// // Wait for TIM2 to tick.
|
||||
/// let mut tim2_interrupt = interrupt::take!(TIM2);
|
||||
/// InterruptFuture::new(&mut tim2_interrupt).await;
|
||||
/// // TIM2 interrupt went off, do something...
|
||||
/// }
|
||||
/// ```
|
||||
pub struct InterruptFuture<'a, I: Interrupt> {
|
||||
interrupt: &'a mut I,
|
||||
}
|
||||
|
||||
impl<'a, I: Interrupt> Drop for InterruptFuture<'a, I> {
|
||||
fn drop(&mut self) {
|
||||
self.interrupt.disable();
|
||||
self.interrupt.remove_handler();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Interrupt> InterruptFuture<'a, I> {
|
||||
pub fn new(interrupt: &'a mut I) -> Self {
|
||||
interrupt.disable();
|
||||
interrupt.set_handler(irq_wake_handler);
|
||||
interrupt.set_handler_context(ptr::null_mut());
|
||||
interrupt.unpend();
|
||||
interrupt.enable();
|
||||
|
||||
Self { interrupt }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Interrupt> Unpin for InterruptFuture<'a, I> {}
|
||||
|
||||
impl<'a, I: Interrupt> Future for InterruptFuture<'a, I> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
let s = unsafe { self.get_unchecked_mut() };
|
||||
s.interrupt.set_handler_context(unsafe {
|
||||
executor::raw::task_from_waker(&cx.waker()).cast().as_ptr()
|
||||
});
|
||||
if s.interrupt.is_enabled() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
}
|
5
embassy/src/waitqueue/mod.rs
Normal file
5
embassy/src/waitqueue/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
//! Async low-level wait queues
|
||||
|
||||
#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")]
|
||||
mod waker;
|
||||
pub use waker::*;
|
@@ -1,8 +1,7 @@
|
||||
use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering};
|
||||
use core::ptr::{self, NonNull};
|
||||
use core::task::Waker;
|
||||
|
||||
use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering};
|
||||
|
||||
use crate::executor::raw::{task_from_waker, wake_task, TaskHeader};
|
||||
|
||||
/// Utility struct to register and wake a waker.
|
@@ -2,7 +2,7 @@ use core::cell::Cell;
|
||||
use core::mem;
|
||||
use core::task::Waker;
|
||||
|
||||
use crate::util::CriticalSectionMutex as Mutex;
|
||||
use crate::blocking_mutex::CriticalSectionMutex as Mutex;
|
||||
|
||||
/// Utility struct to register and wake a waker.
|
||||
#[derive(Debug)]
|
Reference in New Issue
Block a user