mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-09-30 22:10:37 +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`.
|
||||
- Added std `Entry` methods to indexmap `Entry`.
|
||||
- Added `StringView`, the `!Sized` version of `String`.
|
||||
- Added `MpMcQueueView`, the `!Sized` version of `MpMcQueue`.
|
||||
|
||||
### Changed
|
||||
|
||||
|
80
src/mpmc.rs
80
src/mpmc.rs
@ -95,6 +95,8 @@ use portable_atomic as atomic;
|
||||
|
||||
use atomic::Ordering;
|
||||
|
||||
use crate::storage::{OwnedStorage, Storage, ViewStorage};
|
||||
|
||||
#[cfg(feature = "mpmc_large")]
|
||||
type AtomicTargetSize = atomic::AtomicUsize;
|
||||
#[cfg(not(feature = "mpmc_large"))]
|
||||
@ -128,17 +130,27 @@ pub type Q32<T> = MpMcQueue<T, 32>;
|
||||
/// MPMC queue with a capability for 64 elements.
|
||||
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
|
||||
/// N must be a power of 2
|
||||
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
|
||||
pub struct MpMcQueue<T, const N: usize> {
|
||||
buffer: UnsafeCell<[Cell<T>; N]>,
|
||||
dequeue_pos: AtomicTargetSize,
|
||||
enqueue_pos: AtomicTargetSize,
|
||||
}
|
||||
pub type MpMcQueue<T, const N: usize> = MpMcQueueInner<T, OwnedStorage<N>>;
|
||||
|
||||
/// MPMC queue with a capacity for N elements
|
||||
/// 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> {
|
||||
const MASK: UintSize = (N - 1) as UintSize;
|
||||
const EMPTY_CELL: Cell<T> = Cell::new(0);
|
||||
|
||||
const ASSERT: [(); 1] = [()];
|
||||
@ -167,10 +179,56 @@ impl<T, const N: usize> MpMcQueue<T, N> {
|
||||
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
|
||||
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
|
||||
@ -179,9 +237,9 @@ impl<T, const N: usize> MpMcQueue<T, N> {
|
||||
pub fn enqueue(&self, item: T) -> Result<(), T> {
|
||||
unsafe {
|
||||
enqueue(
|
||||
self.buffer.get() as *mut _,
|
||||
S::as_ptr(self.buffer.get()),
|
||||
&self.enqueue_pos,
|
||||
Self::MASK,
|
||||
self.mask(),
|
||||
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) {
|
||||
// drop all contents currently in the queue
|
||||
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> {
|
||||
data: MaybeUninit<T>,
|
||||
|
@ -4,6 +4,12 @@ use core::borrow::{Borrow, BorrowMut};
|
||||
|
||||
pub(crate) trait SealedStorage {
|
||||
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.
|
||||
@ -33,6 +39,12 @@ pub enum OwnedStorage<const N: usize> {}
|
||||
impl<const N: usize> Storage for OwnedStorage<N> {}
|
||||
impl<const N: usize> SealedStorage for OwnedStorage<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]`.
|
||||
@ -40,4 +52,11 @@ pub enum ViewStorage {}
|
||||
impl Storage for ViewStorage {}
|
||||
impl SealedStorage for ViewStorage {
|
||||
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