From 3343adaa70f1248505a8a6042ab14564ea7d0d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 28 Jun 2024 17:22:24 +0200 Subject: [PATCH 1/3] Implement MpMcQueueView --- CHANGELOG.md | 1 + src/mpmc.rs | 80 +++++++++++++++++++++++++++++++++++++++++++------- src/storage.rs | 23 +++++++++++++++ 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9992d86028..b5e600c1c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/mpmc.rs b/src/mpmc.rs index bb9b1fdea4..caef2caf9d 100644 --- a/src/mpmc.rs +++ b/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 = MpMcQueue; /// MPMC queue with a capability for 64 elements. pub type Q64 = MpMcQueue; -/// 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 { - buffer: UnsafeCell<[Cell; N]>, +/// 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 { dequeue_pos: AtomicTargetSize, enqueue_pos: AtomicTargetSize, + buffer: UnsafeCell>>, } +/// 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 MpMcQueue = MpMcQueueInner>; + +/// 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 = MpMcQueueInner; + impl MpMcQueue { - const MASK: UintSize = (N - 1) as UintSize; const EMPTY_CELL: Cell = Cell::new(0); const ASSERT: [(); 1] = [()]; @@ -167,10 +179,56 @@ impl MpMcQueue { 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 = MpMcQueue::new(); + /// let view: &MpMcQueueView = queue.as_view(); + /// ``` + /// + /// It is often preferable to do the same through type coerction, since `MpMcQueue` implements `Unsize>`: + /// + /// ```rust + /// # use heapless::mpmc::{MpMcQueue, MpMcQueueView}; + /// let queue: MpMcQueue = MpMcQueue::new(); + /// let view: &MpMcQueueView = &queue; + /// ``` + #[inline] + pub const fn as_view(&self) -> &MpMcQueueView { + self + } + + /// Get a mutable reference to the `MpMcQueue`, erasing the `N` const-generic. + /// + /// ```rust + /// # use heapless::mpmc::{MpMcQueue, MpMcQueueView}; + /// let mut queue: MpMcQueue = MpMcQueue::new(); + /// let view: &mut MpMcQueueView = queue.as_mut_view(); + /// ``` + /// + /// It is often preferable to do the same through type coerction, since `MpMcQueue` implements `Unsize>`: + /// + /// ```rust + /// # use heapless::mpmc::{MpMcQueue, MpMcQueueView}; + /// let mut queue: MpMcQueue = MpMcQueue::new(); + /// let view: &mut MpMcQueueView = &mut queue; + /// ``` + #[inline] + pub fn as_mut_view(&mut self) -> &mut MpMcQueueView { + self + } +} + +impl MpMcQueueInner { + 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 { - 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 MpMcQueue { 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 Default for MpMcQueue { } } -impl Drop for MpMcQueue { +impl Drop for MpMcQueueInner { fn drop(&mut self) { // drop all contents currently in the queue while self.dequeue().is_some() {} } } -unsafe impl Sync for MpMcQueue where T: Send {} +unsafe impl Sync for MpMcQueueInner where T: Send {} struct Cell { data: MaybeUninit, diff --git a/src/storage.rs b/src/storage.rs index b4e9269cef..6daa4e5719 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -4,6 +4,10 @@ use core::borrow::{Borrow, BorrowMut}; pub(crate) trait SealedStorage { type Buffer: ?Sized + Borrow<[T]> + BorrowMut<[T]>; + /// Obtain the length of the buffer + fn len(this: *const Self::Buffer) -> usize; + /// Obtain access to the first element of the buffer + fn as_ptr(this: *mut Self::Buffer) -> *mut T; } /// Trait defining how data for a container is stored. @@ -33,6 +37,12 @@ pub enum OwnedStorage {} impl Storage for OwnedStorage {} impl SealedStorage for OwnedStorage { type Buffer = [T; N]; + fn len(_: *const Self::Buffer) -> usize { + N + } + fn as_ptr(this: *mut Self::Buffer) -> *mut T { + this as _ + } } /// Implementation of [`Storage`] that stores the data in an unsized `[T]`. @@ -40,4 +50,17 @@ pub enum ViewStorage {} impl Storage for ViewStorage {} impl SealedStorage for ViewStorage { type Buffer = [T]; + fn len(this: *const Self::Buffer) -> usize { + // We get the len of the buffer. There is no reverse method of `core::ptr::from_raw_parts`, so we have to work around it. + let ptr: *const [()] = this as _; + // SAFETY: There is no aliasing as () is zero-sized + let slice: &[()] = unsafe { &*ptr }; + let len = slice.len(); + + len - 1 + } + + fn as_ptr(this: *mut Self::Buffer) -> *mut T { + this as _ + } } From e36e665a1b1058f9eae7b7466d1e05982cfe5639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 1 Jul 2024 10:27:57 +0200 Subject: [PATCH 2/3] Fix unused warnings with `mpmc` disabled --- src/storage.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/storage.rs b/src/storage.rs index 6daa4e5719..90cedd15a1 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -5,8 +5,10 @@ use core::borrow::{Borrow, BorrowMut}; pub(crate) trait SealedStorage { type Buffer: ?Sized + Borrow<[T]> + BorrowMut<[T]>; /// Obtain the length of the buffer + #[allow(unused)] fn len(this: *const Self::Buffer) -> usize; /// Obtain access to the first element of the buffer + #[allow(unused)] fn as_ptr(this: *mut Self::Buffer) -> *mut T; } From 2849ab8456d9adc0c4d95544a851798b488bd291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 1 Jul 2024 13:38:32 +0200 Subject: [PATCH 3/3] Use 1.79 stabilized api to remove use of unsafe --- src/storage.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/storage.rs b/src/storage.rs index 90cedd15a1..34c3746e18 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -53,13 +53,7 @@ impl Storage for ViewStorage {} impl SealedStorage for ViewStorage { type Buffer = [T]; fn len(this: *const Self::Buffer) -> usize { - // We get the len of the buffer. There is no reverse method of `core::ptr::from_raw_parts`, so we have to work around it. - let ptr: *const [()] = this as _; - // SAFETY: There is no aliasing as () is zero-sized - let slice: &[()] = unsafe { &*ptr }; - let len = slice.len(); - - len - 1 + this.len() } fn as_ptr(this: *mut Self::Buffer) -> *mut T {