Skip to content

Commit 55ed7c2

Browse files
authored
Merge pull request #488 from Nitrokey/mpmc-dedup
`MpMcQueue`: add `MpMcQueueView`, similar to `VecView` on top of #486
2 parents e43a751 + 2849ab8 commit 55ed7c2

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2626
- Implemented `DoubleEndedIterator` for `OldestOrdered`.
2727
- Added std `Entry` methods to indexmap `Entry`.
2828
- Added `StringView`, the `!Sized` version of `String`.
29+
- Added `MpMcQueueView`, the `!Sized` version of `MpMcQueue`.
2930

3031
### Changed
3132

src/mpmc.rs

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ use portable_atomic as atomic;
9595

9696
use atomic::Ordering;
9797

98+
use crate::storage::{OwnedStorage, Storage, ViewStorage};
99+
98100
#[cfg(feature = "mpmc_large")]
99101
type AtomicTargetSize = atomic::AtomicUsize;
100102
#[cfg(not(feature = "mpmc_large"))]
@@ -128,17 +130,27 @@ pub type Q32<T> = MpMcQueue<T, 32>;
128130
/// MPMC queue with a capability for 64 elements.
129131
pub type Q64<T> = MpMcQueue<T, 64>;
130132

131-
/// MPMC queue with a capacity for N elements
132-
/// N must be a power of 2
133-
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
134-
pub struct MpMcQueue<T, const N: usize> {
135-
buffer: UnsafeCell<[Cell<T>; N]>,
133+
/// Base struct for [`MpMcQueue`] and [`MpMcQueueView`], generic over the [`Storage`].
134+
///
135+
/// In most cases you should use [`MpMcQueue`] or [`MpMcQueueView`] directly. Only use this
136+
/// struct if you want to write code that's generic over both.
137+
pub struct MpMcQueueInner<T, S: Storage> {
136138
dequeue_pos: AtomicTargetSize,
137139
enqueue_pos: AtomicTargetSize,
140+
buffer: UnsafeCell<S::Buffer<Cell<T>>>,
138141
}
139142

143+
/// MPMC queue with a capacity for N elements
144+
/// N must be a power of 2
145+
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
146+
pub type MpMcQueue<T, const N: usize> = MpMcQueueInner<T, OwnedStorage<N>>;
147+
148+
/// MPMC queue with a capacity for N elements
149+
/// N must be a power of 2
150+
/// The max value of N is u8::MAX - 1 if `mpmc_large` feature is not enabled.
151+
pub type MpMcQueueView<T> = MpMcQueueInner<T, ViewStorage>;
152+
140153
impl<T, const N: usize> MpMcQueue<T, N> {
141-
const MASK: UintSize = (N - 1) as UintSize;
142154
const EMPTY_CELL: Cell<T> = Cell::new(0);
143155

144156
const ASSERT: [(); 1] = [()];
@@ -167,10 +179,56 @@ impl<T, const N: usize> MpMcQueue<T, N> {
167179
enqueue_pos: AtomicTargetSize::new(0),
168180
}
169181
}
182+
/// Get a reference to the `MpMcQueue`, erasing the `N` const-generic.
183+
///
184+
///
185+
/// ```rust
186+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
187+
/// let queue: MpMcQueue<u8, 2> = MpMcQueue::new();
188+
/// let view: &MpMcQueueView<u8> = queue.as_view();
189+
/// ```
190+
///
191+
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueueView<T>>`:
192+
///
193+
/// ```rust
194+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
195+
/// let queue: MpMcQueue<u8, 2> = MpMcQueue::new();
196+
/// let view: &MpMcQueueView<u8> = &queue;
197+
/// ```
198+
#[inline]
199+
pub const fn as_view(&self) -> &MpMcQueueView<T> {
200+
self
201+
}
202+
203+
/// Get a mutable reference to the `MpMcQueue`, erasing the `N` const-generic.
204+
///
205+
/// ```rust
206+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
207+
/// let mut queue: MpMcQueue<u8, 2> = MpMcQueue::new();
208+
/// let view: &mut MpMcQueueView<u8> = queue.as_mut_view();
209+
/// ```
210+
///
211+
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueueView<T>>`:
212+
///
213+
/// ```rust
214+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView};
215+
/// let mut queue: MpMcQueue<u8, 2> = MpMcQueue::new();
216+
/// let view: &mut MpMcQueueView<u8> = &mut queue;
217+
/// ```
218+
#[inline]
219+
pub fn as_mut_view(&mut self) -> &mut MpMcQueueView<T> {
220+
self
221+
}
222+
}
223+
224+
impl<T, S: Storage> MpMcQueueInner<T, S> {
225+
fn mask(&self) -> UintSize {
226+
(S::len(self.buffer.get()) - 1) as _
227+
}
170228

171229
/// Returns the item in the front of the queue, or `None` if the queue is empty
172230
pub fn dequeue(&self) -> Option<T> {
173-
unsafe { dequeue(self.buffer.get() as *mut _, &self.dequeue_pos, Self::MASK) }
231+
unsafe { dequeue(S::as_ptr(self.buffer.get()), &self.dequeue_pos, self.mask()) }
174232
}
175233

176234
/// Adds an `item` to the end of the queue
@@ -179,9 +237,9 @@ impl<T, const N: usize> MpMcQueue<T, N> {
179237
pub fn enqueue(&self, item: T) -> Result<(), T> {
180238
unsafe {
181239
enqueue(
182-
self.buffer.get() as *mut _,
240+
S::as_ptr(self.buffer.get()),
183241
&self.enqueue_pos,
184-
Self::MASK,
242+
self.mask(),
185243
item,
186244
)
187245
}
@@ -194,14 +252,14 @@ impl<T, const N: usize> Default for MpMcQueue<T, N> {
194252
}
195253
}
196254

197-
impl<T, const N: usize> Drop for MpMcQueue<T, N> {
255+
impl<T, S: Storage> Drop for MpMcQueueInner<T, S> {
198256
fn drop(&mut self) {
199257
// drop all contents currently in the queue
200258
while self.dequeue().is_some() {}
201259
}
202260
}
203261

204-
unsafe impl<T, const N: usize> Sync for MpMcQueue<T, N> where T: Send {}
262+
unsafe impl<T, S: Storage> Sync for MpMcQueueInner<T, S> where T: Send {}
205263

206264
struct Cell<T> {
207265
data: MaybeUninit<T>,

src/storage.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ use core::borrow::{Borrow, BorrowMut};
44

55
pub(crate) trait SealedStorage {
66
type Buffer<T>: ?Sized + Borrow<[T]> + BorrowMut<[T]>;
7+
/// Obtain the length of the buffer
8+
#[allow(unused)]
9+
fn len<T>(this: *const Self::Buffer<T>) -> usize;
10+
/// Obtain access to the first element of the buffer
11+
#[allow(unused)]
12+
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T;
713
}
814

915
/// Trait defining how data for a container is stored.
@@ -33,11 +39,24 @@ pub enum OwnedStorage<const N: usize> {}
3339
impl<const N: usize> Storage for OwnedStorage<N> {}
3440
impl<const N: usize> SealedStorage for OwnedStorage<N> {
3541
type Buffer<T> = [T; N];
42+
fn len<T>(_: *const Self::Buffer<T>) -> usize {
43+
N
44+
}
45+
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T {
46+
this as _
47+
}
3648
}
3749

3850
/// Implementation of [`Storage`] that stores the data in an unsized `[T]`.
3951
pub enum ViewStorage {}
4052
impl Storage for ViewStorage {}
4153
impl SealedStorage for ViewStorage {
4254
type Buffer<T> = [T];
55+
fn len<T>(this: *const Self::Buffer<T>) -> usize {
56+
this.len()
57+
}
58+
59+
fn as_ptr<T>(this: *mut Self::Buffer<T>) -> *mut T {
60+
this as _
61+
}
4362
}

0 commit comments

Comments
 (0)