mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-10-02 14:54:30 +00:00
Merge pull request #488 from Nitrokey/mpmc-dedup
`MpMcQueue`: add `MpMcQueueView`, similar to `VecView` on top of #486
This commit is contained in:
commit
55ed7c2469
@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Implemented `DoubleEndedIterator` for `OldestOrdered`.
|
- Implemented `DoubleEndedIterator` for `OldestOrdered`.
|
||||||
- Added std `Entry` methods to indexmap `Entry`.
|
- Added std `Entry` methods to indexmap `Entry`.
|
||||||
- Added `StringView`, the `!Sized` version of `String`.
|
- Added `StringView`, the `!Sized` version of `String`.
|
||||||
|
- Added `MpMcQueueView`, the `!Sized` version of `MpMcQueue`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
80
src/mpmc.rs
80
src/mpmc.rs
@ -95,6 +95,8 @@ use portable_atomic as atomic;
|
|||||||
|
|
||||||
use atomic::Ordering;
|
use atomic::Ordering;
|
||||||
|
|
||||||
|
use crate::storage::{OwnedStorage, Storage, ViewStorage};
|
||||||
|
|
||||||
#[cfg(feature = "mpmc_large")]
|
#[cfg(feature = "mpmc_large")]
|
||||||
type AtomicTargetSize = atomic::AtomicUsize;
|
type AtomicTargetSize = atomic::AtomicUsize;
|
||||||
#[cfg(not(feature = "mpmc_large"))]
|
#[cfg(not(feature = "mpmc_large"))]
|
||||||
@ -128,17 +130,27 @@ pub type Q32<T> = MpMcQueue<T, 32>;
|
|||||||
/// MPMC queue with a capability for 64 elements.
|
/// MPMC queue with a capability for 64 elements.
|
||||||
pub type Q64<T> = MpMcQueue<T, 64>;
|
pub type Q64<T> = MpMcQueue<T, 64>;
|
||||||
|
|
||||||
|
/// Base struct for [`MpMcQueue`] and [`MpMcQueueView`], generic over the [`Storage`].
|
||||||
|
///
|
||||||
|
/// In most cases you should use [`MpMcQueue`] or [`MpMcQueueView`] directly. Only use this
|
||||||
|
/// struct if you want to write code that's generic over both.
|
||||||
|
pub struct MpMcQueueInner<T, S: Storage> {
|
||||||
|
dequeue_pos: AtomicTargetSize,
|
||||||
|
enqueue_pos: AtomicTargetSize,
|
||||||
|
buffer: UnsafeCell<S::Buffer<Cell<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// MPMC queue with a capacity for N elements
|
/// MPMC queue with a capacity for N elements
|
||||||
/// N must be a power of 2
|
/// N must be a power of 2
|
||||||
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
|
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
|
||||||
pub struct MpMcQueue<T, const N: usize> {
|
pub type MpMcQueue<T, const N: usize> = MpMcQueueInner<T, OwnedStorage<N>>;
|
||||||
buffer: UnsafeCell<[Cell<T>; N]>,
|
|
||||||
dequeue_pos: AtomicTargetSize,
|
/// MPMC queue with a capacity for N elements
|
||||||
enqueue_pos: AtomicTargetSize,
|
/// N must be a power of 2
|
||||||
}
|
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
|
||||||
|
pub type MpMcQueueView<T> = MpMcQueueInner<T, ViewStorage>;
|
||||||
|
|
||||||
impl<T, const N: usize> MpMcQueue<T, N> {
|
impl<T, const N: usize> MpMcQueue<T, N> {
|
||||||
const MASK: UintSize = (N - 1) as UintSize;
|
|
||||||
const EMPTY_CELL: Cell<T> = Cell::new(0);
|
const EMPTY_CELL: Cell<T> = Cell::new(0);
|
||||||
|
|
||||||
const ASSERT: [(); 1] = [()];
|
const ASSERT: [(); 1] = [()];
|
||||||
@ -167,10 +179,56 @@ impl<T, const N: usize> MpMcQueue<T, N> {
|
|||||||
enqueue_pos: AtomicTargetSize::new(0),
|
enqueue_pos: AtomicTargetSize::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Get a reference to the `MpMcQueue`, erasing the `N` const-generic.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
|
||||||
|
/// let queue: MpMcQueue<u8, 2> = MpMcQueue::new();
|
||||||
|
/// let view: &MpMcQueueView<u8> = queue.as_view();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueueView<T>>`:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
|
||||||
|
/// let queue: MpMcQueue<u8, 2> = MpMcQueue::new();
|
||||||
|
/// let view: &MpMcQueueView<u8> = &queue;
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_view(&self) -> &MpMcQueueView<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the `MpMcQueue`, erasing the `N` const-generic.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
|
||||||
|
/// let mut queue: MpMcQueue<u8, 2> = MpMcQueue::new();
|
||||||
|
/// let view: &mut MpMcQueueView<u8> = queue.as_mut_view();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueueView<T>>`:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
|
||||||
|
/// let mut queue: MpMcQueue<u8, 2> = MpMcQueue::new();
|
||||||
|
/// let view: &mut MpMcQueueView<u8> = &mut queue;
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn as_mut_view(&mut self) -> &mut MpMcQueueView<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S: Storage> MpMcQueueInner<T, S> {
|
||||||
|
fn mask(&self) -> UintSize {
|
||||||
|
(S::len(self.buffer.get()) - 1) as _
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the item in the front of the queue, or `None` if the queue is empty
|
/// Returns the item in the front of the queue, or `None` if the queue is empty
|
||||||
pub fn dequeue(&self) -> Option<T> {
|
pub fn dequeue(&self) -> Option<T> {
|
||||||
unsafe { dequeue(self.buffer.get() as *mut _, &self.dequeue_pos, Self::MASK) }
|
unsafe { dequeue(S::as_ptr(self.buffer.get()), &self.dequeue_pos, self.mask()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an `item` to the end of the queue
|
/// Adds an `item` to the end of the queue
|
||||||
@ -179,9 +237,9 @@ impl<T, const N: usize> MpMcQueue<T, N> {
|
|||||||
pub fn enqueue(&self, item: T) -> Result<(), T> {
|
pub fn enqueue(&self, item: T) -> Result<(), T> {
|
||||||
unsafe {
|
unsafe {
|
||||||
enqueue(
|
enqueue(
|
||||||
self.buffer.get() as *mut _,
|
S::as_ptr(self.buffer.get()),
|
||||||
&self.enqueue_pos,
|
&self.enqueue_pos,
|
||||||
Self::MASK,
|
self.mask(),
|
||||||
item,
|
item,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -194,14 +252,14 @@ impl<T, const N: usize> Default for MpMcQueue<T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Drop for MpMcQueue<T, N> {
|
impl<T, S: Storage> Drop for MpMcQueueInner<T, S> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// drop all contents currently in the queue
|
// drop all contents currently in the queue
|
||||||
while self.dequeue().is_some() {}
|
while self.dequeue().is_some() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T, const N: usize> Sync for MpMcQueue<T, N> where T: Send {}
|
unsafe impl<T, S: Storage> Sync for MpMcQueueInner<T, S> where T: Send {}
|
||||||
|
|
||||||
struct Cell<T> {
|
struct Cell<T> {
|
||||||
data: MaybeUninit<T>,
|
data: MaybeUninit<T>,
|
||||||
|
@ -4,6 +4,12 @@ use core::borrow::{Borrow, BorrowMut};
|
|||||||
|
|
||||||
pub(crate) trait SealedStorage {
|
pub(crate) trait SealedStorage {
|
||||||
type Buffer<T>: ?Sized + Borrow<[T]> + BorrowMut<[T]>;
|
type Buffer<T>: ?Sized + Borrow<[T]> + BorrowMut<[T]>;
|
||||||
|
/// Obtain the length of the buffer
|
||||||
|
#[allow(unused)]
|
||||||
|
fn len<T>(this: *const Self::Buffer<T>) -> usize;
|
||||||
|
/// Obtain access to the first element of the buffer
|
||||||
|
#[allow(unused)]
|
||||||
|
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait defining how data for a container is stored.
|
/// Trait defining how data for a container is stored.
|
||||||
@ -33,6 +39,12 @@ pub enum OwnedStorage<const N: usize> {}
|
|||||||
impl<const N: usize> Storage for OwnedStorage<N> {}
|
impl<const N: usize> Storage for OwnedStorage<N> {}
|
||||||
impl<const N: usize> SealedStorage for OwnedStorage<N> {
|
impl<const N: usize> SealedStorage for OwnedStorage<N> {
|
||||||
type Buffer<T> = [T; N];
|
type Buffer<T> = [T; N];
|
||||||
|
fn len<T>(_: *const Self::Buffer<T>) -> usize {
|
||||||
|
N
|
||||||
|
}
|
||||||
|
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T {
|
||||||
|
this as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of [`Storage`] that stores the data in an unsized `[T]`.
|
/// Implementation of [`Storage`] that stores the data in an unsized `[T]`.
|
||||||
@ -40,4 +52,11 @@ pub enum ViewStorage {}
|
|||||||
impl Storage for ViewStorage {}
|
impl Storage for ViewStorage {}
|
||||||
impl SealedStorage for ViewStorage {
|
impl SealedStorage for ViewStorage {
|
||||||
type Buffer<T> = [T];
|
type Buffer<T> = [T];
|
||||||
|
fn len<T>(this: *const Self::Buffer<T>) -> usize {
|
||||||
|
this.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T {
|
||||||
|
this as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user