This commit is contained in:
Emil Fresk 2024-02-15 17:36:27 +01:00
parent 7b2b7c64cc
commit faf03f414b

View file

@ -1,8 +1,11 @@
//! A channel operating on bitflags. //! A channel operating on bitflags.
use core::{future::poll_fn, task::Poll};
use atomic::AtomicType;
use bitflags::{Bits, Flags}; use bitflags::{Bits, Flags};
use core::sync::atomic::{AtomicU16, AtomicU8, Ordering}; use portable_atomic::Ordering;
use portable_atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU32, AtomicU64}; use rtic_common::waker_registration::CriticalSectionWakerRegistration as CSWaker;
/// A channel for setting and clearing `bitflags` concurrently. /// A channel for setting and clearing `bitflags` concurrently.
pub struct BitChannel<T: Flags> pub struct BitChannel<T: Flags>
@ -11,6 +14,7 @@ where
T::Bits: AtomicType, T::Bits: AtomicType,
{ {
atomic: <T::Bits as AtomicType>::Atomic, atomic: <T::Bits as AtomicType>::Atomic,
waker: CSWaker,
} }
impl<T> BitChannel<T> impl<T> BitChannel<T>
@ -22,72 +26,96 @@ where
pub const fn new() -> Self { pub const fn new() -> Self {
BitChannel { BitChannel {
atomic: T::Bits::ATOMIC_ZERO, atomic: T::Bits::ATOMIC_ZERO,
waker: CSWaker::new(),
} }
} }
/// Set `bitflag`s. /// Set `bitflag`s.
#[inline] #[inline]
pub fn send(&self, flags: T) { pub fn send(&self, flags: T) {
T::Bits::fetch_or(&self.atomic, flags.bits(), Ordering::Relaxed); T::Bits::fetch_or(&self.atomic, flags.bits(), Ordering::Acquire);
self.waker.wake();
} }
/// Receive the current value of the `bitflags` and reset all flags. /// Receive the current value of the `bitflags` and reset all flags. This can be accessed
/// concurrently but not all receivers will see the flags, only the first one will.
#[inline] #[inline]
pub fn recv(&self) -> T { pub fn recv(&self) -> T {
<T as Flags>::from_bits_retain(T::Bits::fetch_and( <T as Flags>::from_bits_retain(T::Bits::fetch_and(
&self.atomic, &self.atomic,
T::Bits::EMPTY, T::Bits::EMPTY,
Ordering::Relaxed, Ordering::Release,
)) ))
} }
/// Wait for new values, the return is guaranteed non-empty.
pub async fn wait(&self) -> T {
poll_fn(|cx| {
self.waker.register(cx.waker());
let val = self.recv();
if val.is_empty() {
Poll::Pending
} else {
Poll::Ready(val)
}
})
.await
}
} }
/// Generic atomic trait, allows for taking any `bitflags::Bits` as an atomic. mod atomic {
pub trait AtomicType: Sized { use portable_atomic::{
/// The underlying atomic, e.g. `AtomicU8`, `AtomicU16`. AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, AtomicU32, AtomicU64, AtomicU8,
type Atomic: From<Self>; Ordering,
/// The definition of that atomic with value 0.
const ATOMIC_ZERO: Self::Atomic;
/// The atomic's `fetch_and` implementation forwarded.
fn fetch_and(a: &Self::Atomic, b: Self, order: Ordering) -> Self;
/// The atomic's `fetch_or` implementation forwarded.
fn fetch_or(a: &Self::Atomic, b: Self, order: Ordering) -> Self;
}
macro_rules! atomic_type_impl {
($atomic:ty, $integer:ty) => {
impl AtomicType for $integer {
type Atomic = $atomic;
const ATOMIC_ZERO: Self::Atomic = <$atomic>::new(0);
#[inline(always)]
fn fetch_and(a: &Self::Atomic, b: Self, order: Ordering) -> Self {
a.fetch_and(b, order)
}
#[inline(always)]
fn fetch_or(a: &Self::Atomic, b: Self, order: Ordering) -> Self {
a.fetch_or(b, order)
}
}
}; };
/// Generic atomic trait, allows for taking any `bitflags::Bits` as an atomic.
pub trait AtomicType: Sized {
/// The underlying atomic, e.g. `AtomicU8`, `AtomicU16`.
type Atomic: From<Self>;
/// The definition of that atomic with value 0.
const ATOMIC_ZERO: Self::Atomic;
/// The atomic's `fetch_and` implementation forwarded.
fn fetch_and(a: &Self::Atomic, b: Self, order: Ordering) -> Self;
/// The atomic's `fetch_or` implementation forwarded.
fn fetch_or(a: &Self::Atomic, b: Self, order: Ordering) -> Self;
}
macro_rules! atomic_type_impl {
($atomic:ty, $integer:ty) => {
impl AtomicType for $integer {
type Atomic = $atomic;
const ATOMIC_ZERO: Self::Atomic = <$atomic>::new(0);
#[inline(always)]
fn fetch_and(a: &Self::Atomic, b: Self, order: Ordering) -> Self {
a.fetch_and(b, order)
}
#[inline(always)]
fn fetch_or(a: &Self::Atomic, b: Self, order: Ordering) -> Self {
a.fetch_or(b, order)
}
}
};
}
atomic_type_impl!(AtomicU8, u8);
atomic_type_impl!(AtomicU16, u16);
atomic_type_impl!(AtomicU32, u32);
atomic_type_impl!(AtomicU64, u64);
atomic_type_impl!(AtomicI8, i8);
atomic_type_impl!(AtomicI16, i16);
atomic_type_impl!(AtomicI32, i32);
atomic_type_impl!(AtomicI64, i64);
} }
atomic_type_impl!(AtomicU8, u8);
atomic_type_impl!(AtomicU16, u16);
atomic_type_impl!(AtomicU32, u32);
atomic_type_impl!(AtomicU64, u64);
atomic_type_impl!(AtomicI8, i8);
atomic_type_impl!(AtomicI16, i16);
atomic_type_impl!(AtomicI32, i32);
atomic_type_impl!(AtomicI64, i64);
// etc...
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;