Skip to content

Commit 82a5953

Browse files
Implement MpMcQueueView
1 parent 7cfdb84 commit 82a5953

File tree

1 file changed

+131
-15
lines changed

1 file changed

+131
-15
lines changed

src/mpmc.rs

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,77 @@ pub type Q32<T> = MpMcQueue<T, 32>;
128128
/// MPMC queue with a capability for 64 elements.
129129
pub type Q64<T> = MpMcQueue<T, 64>;
130130

131+
mod private {
132+
use core::marker::PhantomData;
133+
134+
use super::*;
135+
136+
/// <div class="warn">This is private API and should not be used</div>
137+
pub struct Owned<T, const N: usize>(PhantomData<[T; N]>);
138+
/// <div class="warn">This is private API and should not be used</div>
139+
pub struct View<T>(PhantomData<[T]>);
140+
141+
/// <div class="warn">This is private API and should not be used</div>
142+
pub struct MpMcQueueInner<B: MpMcQueueBuffer + ?Sized> {
143+
pub(super) dequeue_pos: AtomicTargetSize,
144+
pub(super) enqueue_pos: AtomicTargetSize,
145+
pub(super) buffer: UnsafeCell<B::Buffer>,
146+
}
147+
pub trait MpMcQueueBuffer {
148+
type Buffer: ?Sized;
149+
type T;
150+
151+
fn as_view(this: &private::MpMcQueueInner<Self>) -> &MpMcQueueView<Self::T>;
152+
fn as_mut_view(this: &mut private::MpMcQueueInner<Self>) -> &mut MpMcQueueView<Self::T>;
153+
}
154+
155+
impl<T, const N: usize> MpMcQueueBuffer for private::Owned<T, N> {
156+
type Buffer = [Cell<T>; N];
157+
type T = T;
158+
fn as_view(this: &private::MpMcQueueInner<Self>) -> &MpMcQueueView<Self::T> {
159+
this
160+
}
161+
fn as_mut_view(this: &mut private::MpMcQueueInner<Self>) -> &mut MpMcQueueView<Self::T> {
162+
this
163+
}
164+
}
165+
166+
impl<T> MpMcQueueBuffer for private::View<T> {
167+
type Buffer = [Cell<T>];
168+
type T = T;
169+
fn as_view(this: &private::MpMcQueueInner<Self>) -> &MpMcQueueView<Self::T> {
170+
this
171+
}
172+
fn as_mut_view(this: &mut private::MpMcQueueInner<Self>) -> &mut MpMcQueueView<Self::T> {
173+
this
174+
}
175+
}
176+
177+
// Cell is sealed to satisfy the compiler's requirement of not leaking private types through the MpMcQueueBuffer trait implementations
178+
pub struct Cell<T> {
179+
pub(super) data: MaybeUninit<T>,
180+
pub(super) sequence: AtomicTargetSize,
181+
}
182+
}
183+
184+
// Workaround https://github.com/rust-lang/rust/issues/119015. This is required so that the methods on `VecView` and `Vec` are properly documented.
185+
// cfg(doc) prevents `MpMcQueueInner` being part of the public API.
186+
// doc(hidden) prevents the `pub use vec::VecInner` from being visible in the documentation.
187+
#[cfg(doc)]
188+
#[doc(hidden)]
189+
pub use private::MpMcQueueInner as _;
190+
191+
use private::Cell;
192+
131193
/// MPMC queue with a capacity for N elements
132194
/// N must be a power of 2
133195
/// 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]>,
136-
dequeue_pos: AtomicTargetSize,
137-
enqueue_pos: AtomicTargetSize,
138-
}
196+
pub type MpMcQueue<T, const N: usize> = private::MpMcQueueInner<private::Owned<T, N>>;
197+
198+
/// MPMC queue with a capacity for a dynamic number of element
199+
pub type MpMcQueueView<T> = private::MpMcQueueInner<private::View<T>>;
139200

140201
impl<T, const N: usize> MpMcQueue<T, N> {
141-
const MASK: UintSize = (N - 1) as UintSize;
142202
const EMPTY_CELL: Cell<T> = Cell::new(0);
143203

144204
const ASSERT: [(); 1] = [()];
@@ -170,7 +230,66 @@ impl<T, const N: usize> MpMcQueue<T, N> {
170230

171231
/// Returns the item in the front of the queue, or `None` if the queue is empty
172232
pub fn dequeue(&self) -> Option<T> {
173-
unsafe { dequeue(self.buffer.get() as *mut _, &self.dequeue_pos, Self::MASK) }
233+
self.as_view().dequeue()
234+
}
235+
236+
/// Adds an `item` to the end of the queue
237+
///
238+
/// Returns back the `item` if the queue is full
239+
pub fn enqueue(&self, item: T) -> Result<(), T> {
240+
self.as_view().enqueue(item)
241+
}
242+
243+
/// Get a reference to the `MpMcQueue`, erasing the `N` const-generic.
244+
///
245+
/// ```rust
246+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView, Q2};
247+
/// let q: MpMcQueue<u8, 2> = Q2::new();
248+
/// let view: &MpMcQueueView<u8> = q.as_view();
249+
/// ```
250+
///
251+
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueue<T>>`:
252+
///
253+
/// ```rust
254+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView, Q2};
255+
/// let q: MpMcQueue<u8, 2> = Q2::new();
256+
/// let view: &MpMcQueueView<u8> = &q;
257+
/// ```
258+
pub fn as_view(&self) -> &MpMcQueueView<T> {
259+
self
260+
}
261+
262+
/// Get a mutable reference to the `MpMcQueue`, erasing the `N` const-generic.
263+
///
264+
/// ```rust
265+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView, Q2};
266+
/// let mut q: MpMcQueue<u8, 2> = Q2::new();
267+
/// let view: &mut MpMcQueueView<u8> = q.as_mut_view();
268+
/// ```
269+
///
270+
/// It is often preferable to do the same through type coerction, since `MpMcQueue<T, N>` implements `Unsize<MpMcQueue<T>>`:
271+
///
272+
/// ```rust
273+
/// # use heapless::mpmc::{MpMcQueue, MpMcQueueView, Q2};
274+
/// let mut q: MpMcQueue<u8, 2> = Q2::new();
275+
/// let view: &mut MpMcQueueView<u8> = &mut q;
276+
/// ```
277+
pub fn as_mut_view(&mut self) -> &mut MpMcQueueView<T> {
278+
self
279+
}
280+
}
281+
282+
impl<T> MpMcQueueView<T> {
283+
fn mask(&self) -> UintSize {
284+
let ptr: *const [()] = self.buffer.get() as _;
285+
// SAFETY: There is no aliasing as () is zero-sized
286+
let slice: &[()] = unsafe { &*ptr };
287+
(slice.len() - 1) as _
288+
}
289+
290+
/// Returns the item in the front of the queue, or `None` if the queue is empty
291+
pub fn dequeue(&self) -> Option<T> {
292+
unsafe { dequeue(self.buffer.get() as *mut _, &self.dequeue_pos, self.mask()) }
174293
}
175294

176295
/// Adds an `item` to the end of the queue
@@ -181,7 +300,7 @@ impl<T, const N: usize> MpMcQueue<T, N> {
181300
enqueue(
182301
self.buffer.get() as *mut _,
183302
&self.enqueue_pos,
184-
Self::MASK,
303+
self.mask(),
185304
item,
186305
)
187306
}
@@ -194,19 +313,16 @@ impl<T, const N: usize> Default for MpMcQueue<T, N> {
194313
}
195314
}
196315

197-
impl<T, const N: usize> Drop for MpMcQueue<T, N> {
316+
impl<B: private::MpMcQueueBuffer + ?Sized> Drop for private::MpMcQueueInner<B> {
198317
fn drop(&mut self) {
318+
let this = B::as_mut_view(self);
199319
// drop all contents currently in the queue
200-
while self.dequeue().is_some() {}
320+
while this.dequeue().is_some() {}
201321
}
202322
}
203323

204324
unsafe impl<T, const N: usize> Sync for MpMcQueue<T, N> where T: Send {}
205-
206-
struct Cell<T> {
207-
data: MaybeUninit<T>,
208-
sequence: AtomicTargetSize,
209-
}
325+
unsafe impl<T> Sync for MpMcQueueView<T> where T: Send {}
210326

211327
impl<T> Cell<T> {
212328
const fn new(seq: usize) -> Self {

0 commit comments

Comments
 (0)