From 6b05894851829384d8c791cfa9f06bd773d930e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 5 Apr 2024 11:31:59 +0200 Subject: [PATCH 1/4] Fix memory leak on VecView drop Drop must also be implemented on `VecView` for the cases wher the `VecView` is owned even if it is `!Sized`. This can happen when it is boxed. --- src/vec.rs | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index f7c3efbb66..bb972336bc 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -15,12 +15,6 @@ pub trait VecDrop { } impl VecDrop for [MaybeUninit] { - unsafe fn drop_with_len(&mut self, _len: usize) { - // Case of a view, drop does nothing - } -} - -impl VecDrop for [MaybeUninit; N] { unsafe fn drop_with_len(&mut self, len: usize) { // NOTE(unsafe) avoid bound checks in the slicing operation // &mut buffer[..len] @@ -31,6 +25,12 @@ impl VecDrop for [MaybeUninit; N] { } } +impl VecDrop for [MaybeUninit; N] { + unsafe fn drop_with_len(&mut self, len: usize) { + VecDrop::drop_with_len(self.as_mut_slice(), len) + } +} + ///
This is private API and should not be used
pub struct VecInner { len: usize, @@ -1953,7 +1953,7 @@ mod tests { use static_assertions::assert_not_impl_any; - use crate::Vec; + use super::{Vec, VecView}; // Ensure a `Vec` containing `!Send` values stays `!Send` itself. assert_not_impl_any!(Vec<*const (), 4>: Send); @@ -2014,6 +2014,33 @@ mod tests { assert_eq!(Droppable::count(), 0); } + #[test] + fn drop_vecview() { + droppable!(); + + { + let v: Vec = Vec::new(); + let mut v: Box> = Box::new(v); + v.push(Droppable::new()).ok().unwrap(); + v.push(Droppable::new()).ok().unwrap(); + assert_eq!(Droppable::count(), 2); + v.pop().unwrap(); + assert_eq!(Droppable::count(), 1); + } + + assert_eq!(Droppable::count(), 0); + + { + let v: Vec = Vec::new(); + let mut v: Box> = Box::new(v); + v.push(Droppable::new()).ok().unwrap(); + v.push(Droppable::new()).ok().unwrap(); + assert_eq!(Droppable::count(), 2); + } + + assert_eq!(Droppable::count(), 0); + } + #[test] fn eq() { let mut xs: Vec = Vec::new(); From d3a6db08095ea786f5b7d0638be51eefa81bfaec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Fri, 5 Apr 2024 15:24:45 +0200 Subject: [PATCH 2/4] Rename VecDrop to VecBuffer This represent more clearly the role of the trait now that it's not used to work around drop specialization. --- src/vec.rs | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index bb972336bc..1db505bb8f 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -5,34 +5,37 @@ use core::{ ops, ptr, slice, }; -/// Workaround forbidden specialization of Drop -pub trait VecDrop { - // SAFETY: drop_with_len will be called to call drop in place the first `len` elements of the buffer. - // Only the Owned buffer (`[MaybeUninit; N]`) must drop the items - // and the view (`[MaybeUninit]`) drops nothing. - // `drop_with_len `assumes that the buffer can contain `len` elements. - unsafe fn drop_with_len(&mut self, len: usize); +pub trait VecBuffer { + type T; + + fn as_vecview(vec: &VecInner) -> &VecView; + fn as_mut_vecview(vec: &mut VecInner) -> &mut VecView; } -impl VecDrop for [MaybeUninit] { - unsafe fn drop_with_len(&mut self, len: usize) { - // NOTE(unsafe) avoid bound checks in the slicing operation - // &mut buffer[..len] - // SAFETY: buffer[..len] must be valid to drop given the safety requirement of the trait definition. - let mut_slice = slice::from_raw_parts_mut(self.as_mut_ptr() as *mut T, len); - // We drop each element used in the vector by turning into a `&mut [T]`. - ptr::drop_in_place(mut_slice); +impl VecBuffer for [MaybeUninit; N] { + type T = T; + + fn as_vecview(vec: &VecInner) -> &VecView { + vec + } + fn as_mut_vecview(vec: &mut VecInner) -> &mut VecView { + vec } } -impl VecDrop for [MaybeUninit; N] { - unsafe fn drop_with_len(&mut self, len: usize) { - VecDrop::drop_with_len(self.as_mut_slice(), len) +impl VecBuffer for [MaybeUninit] { + type T = T; + + fn as_vecview(vec: &VecInner) -> &VecView { + vec + } + fn as_mut_vecview(vec: &mut VecInner) -> &mut VecView { + vec } } ///
This is private API and should not be used
-pub struct VecInner { +pub struct VecInner { len: usize, buffer: B, } @@ -1572,10 +1575,12 @@ impl From<[T; M]> for Vec { } } -impl Drop for VecInner { +impl Drop for VecInner { fn drop(&mut self) { + let mut_slice = VecBuffer::as_mut_vecview(self).as_mut_slice(); + // We drop each element used in the vector by turning into a `&mut [T]`. // SAFETY: the buffer contains initialized data for the range 0..self.len - unsafe { self.buffer.drop_with_len(self.len) } + unsafe { ptr::drop_in_place(mut_slice) } } } From 16a030d98e793e3d0112cfc4a193c69fffcd92b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 27 Jun 2024 17:05:55 +0200 Subject: [PATCH 3/4] Add spsc::QueueView --- src/spsc.rs | 585 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 526 insertions(+), 59 deletions(-) diff --git a/src/spsc.rs b/src/spsc.rs index 1b7ab0354b..9da343a77b 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -97,7 +97,7 @@ //! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue` //! and `Ok` is returned by `enqueue`). -use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr}; +use core::{cell::UnsafeCell, fmt, hash, marker::PhantomData, mem::MaybeUninit, ptr}; #[cfg(not(feature = "portable-atomic"))] use core::sync::atomic; @@ -106,26 +106,106 @@ use portable_atomic as atomic; use atomic::{AtomicUsize, Ordering}; +mod private { + use super::*; + + ///
This is private API and should not be used
+ pub struct QueueInner { + // this is from where we dequeue items + pub(crate) head: AtomicUsize, + + // this is where we enqueue new items + pub(crate) tail: AtomicUsize, + + pub(crate) buffer: B, + } + + pub trait QueueBuffer { + type T; + fn as_view(this: &QueueInner) -> &QueueView; + fn as_mut_view(this: &mut QueueInner) -> &mut QueueView; + } +} + +// Workaround https://github.com/rust-lang/rust/issues/119015. This is required so that the methods on `Queue` and `QueueView` are properly documented. +// cfg(doc) prevents `QueueInner` being part of the public API. +// doc(hidden) prevents the `pub use vec::VecInner` from being visible in the documentation. +#[cfg(doc)] +#[doc(hidden)] +pub use private::QueueInner as _; + /// A statically allocated single producer single consumer queue with a capacity of `N - 1` elements /// /// *IMPORTANT*: To get better performance use a value for `N` that is a power of 2 (e.g. `16`, `32`, /// etc.). -pub struct Queue { - // this is from where we dequeue items - pub(crate) head: AtomicUsize, +pub type Queue = private::QueueInner<[UnsafeCell>; N]>; + +/// A statically allocated single producer single consumer queue with dynamic capacity +pub type QueueView = private::QueueInner<[UnsafeCell>]>; + +impl private::QueueBuffer for [UnsafeCell>; N] { + type T = T; + + fn as_view(this: &private::QueueInner) -> &QueueView { + this + } + fn as_mut_view(this: &mut private::QueueInner) -> &mut QueueView { + this + } +} - // this is where we enqueue new items - pub(crate) tail: AtomicUsize, +impl private::QueueBuffer for [UnsafeCell>] { + type T = T; - pub(crate) buffer: [UnsafeCell>; N], + fn as_view(this: &private::QueueInner) -> &QueueView { + this + } + fn as_mut_view(this: &mut private::QueueInner) -> &mut QueueView { + this + } } impl Queue { const INIT: UnsafeCell> = UnsafeCell::new(MaybeUninit::uninit()); - #[inline] - fn increment(val: usize) -> usize { - (val + 1) % N + /// Get a reference to the `Queue`, erasing the `N` const-generic. + /// + /// ``` + /// use heapless::spsc::{Queue, QueueView}; + /// + /// let rb: Queue = Queue::new(); + /// let rb_view: &QueueView = rb.as_view(); + /// ``` + /// It is often preferable to do the same through type coerction, since `Queue` implements `Unsize>`: + /// + /// ``` + /// use heapless::spsc::{Queue, QueueView}; + /// + /// let rb: Queue = Queue::new(); + /// let rb_view: &QueueView = &rb; + /// ``` + pub fn as_view(&self) -> &QueueView { + self + } + + /// Get a mutable reference to the `Queue`, erasing the `N` const-generic. + /// + /// ``` + /// use heapless::spsc::{Queue, QueueView}; + /// + /// let mut rb: Queue = Queue::new(); + /// let rb_view: &mut QueueView = rb.as_mut_view(); + /// ``` + /// It is often preferable to do the same through type coerction, since `Queue` implements `Unsize>`: + /// + /// ``` + /// use heapless::spsc::{Queue, QueueView}; + /// + /// let mut rb: Queue = Queue::new(); + /// let rb_view: &mut QueueView = &mut rb; + /// ``` + pub fn as_mut_view(&mut self) -> &mut QueueView { + self } /// Creates an empty queue with a fixed capacity of `N - 1` @@ -146,13 +226,138 @@ impl Queue { N - 1 } + /// Returns the number of elements in the queue + #[inline] + pub fn len(&self) -> usize { + self.as_view().len() + } + + /// Returns `true` if the queue is empty + #[inline] + pub fn is_empty(&self) -> bool { + self.as_view().is_empty() + } + + /// Returns `true` if the queue is full + #[inline] + pub fn is_full(&self) -> bool { + self.as_view().is_full() + } + + /// Iterates from the front of the queue to the back + pub fn iter(&self) -> Iter<'_, T, N> { + Iter { + inner: self.as_view().iter(), + phantom: PhantomData, + } + } + + /// Returns an iterator that allows modifying each value + pub fn iter_mut(&mut self) -> IterMut<'_, T, N> { + IterMut { + inner: self.as_mut_view().iter_mut(), + phantom: PhantomData, + } + } + + /// Adds an `item` to the end of the queue + /// + /// Returns back the `item` if the queue is full + #[inline] + pub fn enqueue(&mut self, val: T) -> Result<(), T> { + self.as_mut_view().enqueue(val) + } + + /// Returns the item in the front of the queue, or `None` if the queue is empty + #[inline] + pub fn dequeue(&mut self) -> Option { + self.as_mut_view().dequeue() + } + + /// Returns a reference to the item in the front of the queue without dequeuing, or + /// `None` if the queue is empty. + /// + /// # Examples + /// ``` + /// use heapless::spsc::Queue; + /// + /// let mut queue: Queue = Queue::new(); + /// let (mut producer, mut consumer) = queue.split(); + /// assert_eq!(None, consumer.peek()); + /// producer.enqueue(1); + /// assert_eq!(Some(&1), consumer.peek()); + /// assert_eq!(Some(1), consumer.dequeue()); + /// assert_eq!(None, consumer.peek()); + /// ``` + pub fn peek(&self) -> Option<&T> { + self.as_view().peek() + } + + /// Adds an `item` to the end of the queue, without checking if it's full + /// + /// # Safety + /// + /// If the queue is full this operation will leak a value (T's destructor won't run on + /// the value that got overwritten by `item`), *and* will allow the `dequeue` operation + /// to create a copy of `item`, which could result in `T`'s destructor running on `item` + /// twice. + pub unsafe fn enqueue_unchecked(&mut self, val: T) { + self.as_mut_view().inner_enqueue_unchecked(val) + } + + /// Returns the item in the front of the queue, without checking if there is something in the + /// queue + /// + /// # Safety + /// + /// If the queue is empty this operation will return uninitialized memory. + pub unsafe fn dequeue_unchecked(&mut self) -> T { + self.as_mut_view().dequeue_unchecked() + } + + /// Splits a queue into producer and consumer endpoints + pub fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) { + let (producer, consumer) = self.as_mut_view().split(); + ( + Producer { + inner: producer, + phantom: PhantomData, + }, + Consumer { + inner: consumer, + phantom: PhantomData, + }, + ) + } +} + +impl QueueView { + #[inline] + fn increment(&self, val: usize) -> usize { + (val + 1) % self.n() + } + + /// Returns the maximum number of elements the queue can hold + #[inline] + pub const fn capacity(&self) -> usize { + self.n() - 1 + } + + #[inline] + const fn n(&self) -> usize { + self.buffer.len() + } + /// Returns the number of elements in the queue #[inline] pub fn len(&self) -> usize { let current_head = self.head.load(Ordering::Relaxed); let current_tail = self.tail.load(Ordering::Relaxed); - current_tail.wrapping_sub(current_head).wrapping_add(N) % N + current_tail + .wrapping_sub(current_head) + .wrapping_add(self.n()) + % self.n() } /// Returns `true` if the queue is empty @@ -164,12 +369,12 @@ impl Queue { /// Returns `true` if the queue is full #[inline] pub fn is_full(&self) -> bool { - Self::increment(self.tail.load(Ordering::Relaxed)) == self.head.load(Ordering::Relaxed) + self.increment(self.tail.load(Ordering::Relaxed)) == self.head.load(Ordering::Relaxed) } /// Iterates from the front of the queue to the back - pub fn iter(&self) -> Iter<'_, T, N> { - Iter { + pub fn iter(&self) -> IterView<'_, T> { + IterView { rb: self, index: 0, len: self.len(), @@ -177,9 +382,9 @@ impl Queue { } /// Returns an iterator that allows modifying each value - pub fn iter_mut(&mut self) -> IterMut<'_, T, N> { + pub fn iter_mut(&mut self) -> IterMutView<'_, T> { let len = self.len(); - IterMut { + IterMutView { rb: self, index: 0, len, @@ -205,10 +410,11 @@ impl Queue { /// /// # Examples /// ``` - /// use heapless::spsc::Queue; + /// use heapless::spsc::{Queue, QueueView}; /// /// let mut queue: Queue = Queue::new(); - /// let (mut producer, mut consumer) = queue.split(); + /// let queue_view: &mut QueueView = &mut queue; + /// let (mut producer, mut consumer) = queue_view.split(); /// assert_eq!(None, consumer.peek()); /// producer.enqueue(1); /// assert_eq!(Some(&1), consumer.peek()); @@ -225,11 +431,11 @@ impl Queue { } // The memory for enqueueing is "owned" by the tail pointer. - // NOTE: This internal function uses internal mutability to allow the [`Producer`] to enqueue + // NOTE: This internal function uses internal mutability to allow the [`ProducerView`] to enqueue // items without doing pointer arithmetic and accessing internal fields of this type. unsafe fn inner_enqueue(&self, val: T) -> Result<(), T> { let current_tail = self.tail.load(Ordering::Relaxed); - let next_tail = Self::increment(current_tail); + let next_tail = self.increment(current_tail); if next_tail != self.head.load(Ordering::Acquire) { (self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val)); @@ -242,14 +448,14 @@ impl Queue { } // The memory for enqueueing is "owned" by the tail pointer. - // NOTE: This internal function uses internal mutability to allow the [`Producer`] to enqueue + // NOTE: This internal function uses internal mutability to allow the [`ProducerView`] to enqueue // items without doing pointer arithmetic and accessing internal fields of this type. unsafe fn inner_enqueue_unchecked(&self, val: T) { let current_tail = self.tail.load(Ordering::Relaxed); (self.buffer.get_unchecked(current_tail).get()).write(MaybeUninit::new(val)); self.tail - .store(Self::increment(current_tail), Ordering::Release); + .store(self.increment(current_tail), Ordering::Release); } /// Adds an `item` to the end of the queue, without checking if it's full @@ -265,7 +471,7 @@ impl Queue { } // The memory for dequeuing is "owned" by the head pointer,. - // NOTE: This internal function uses internal mutability to allow the [`Consumer`] to dequeue + // NOTE: This internal function uses internal mutability to allow the [`ConsumerView`] to dequeue // items without doing pointer arithmetic and accessing internal fields of this type. unsafe fn inner_dequeue(&self) -> Option { let current_head = self.head.load(Ordering::Relaxed); @@ -276,21 +482,21 @@ impl Queue { let v = (self.buffer.get_unchecked(current_head).get() as *const T).read(); self.head - .store(Self::increment(current_head), Ordering::Release); + .store(self.increment(current_head), Ordering::Release); Some(v) } } // The memory for dequeuing is "owned" by the head pointer,. - // NOTE: This internal function uses internal mutability to allow the [`Consumer`] to dequeue + // NOTE: This internal function uses internal mutability to allow the [`ConsumerView`] to dequeue // items without doing pointer arithmetic and accessing internal fields of this type. unsafe fn inner_dequeue_unchecked(&self) -> T { let current_head = self.head.load(Ordering::Relaxed); let v = (self.buffer.get_unchecked(current_head).get() as *const T).read(); self.head - .store(Self::increment(current_head), Ordering::Release); + .store(self.increment(current_head), Ordering::Release); v } @@ -306,8 +512,8 @@ impl Queue { } /// Splits a queue into producer and consumer endpoints - pub fn split(&mut self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) { - (Producer { rb: self }, Consumer { rb: self }) + pub fn split(&mut self) -> (ProducerView<'_, T>, ConsumerView<'_, T>) { + (ProducerView { rb: self }, ConsumerView { rb: self }) } } @@ -341,20 +547,49 @@ where T: PartialEq, { fn eq(&self, other: &Queue) -> bool { - self.len() == other.len() && self.iter().zip(other.iter()).all(|(v1, v2)| v1 == v2) + self.as_view().eq(other.as_view()) } } impl Eq for Queue where T: Eq {} +impl PartialEq> for QueueView +where + T: PartialEq, +{ + fn eq(&self, other: &QueueView) -> bool { + self.len() == other.len() && self.iter().zip(other.iter()).all(|(v1, v2)| v1 == v2) + } +} + +impl PartialEq> for QueueView +where + T: PartialEq, +{ + fn eq(&self, other: &Queue) -> bool { + self.eq(other.as_view()) + } +} + +impl PartialEq> for Queue +where + T: PartialEq, +{ + fn eq(&self, other: &QueueView) -> bool { + self.as_view().eq(other) + } +} + +impl Eq for QueueView where T: Eq {} + /// An iterator over the items of a queue -pub struct Iter<'a, T, const N: usize> { - rb: &'a Queue, +pub struct IterView<'a, T> { + rb: &'a QueueView, index: usize, len: usize, } -impl<'a, T, const N: usize> Clone for Iter<'a, T, N> { +impl<'a, T> Clone for IterView<'a, T> { fn clone(&self) -> Self { Self { rb: self.rb, @@ -365,20 +600,20 @@ impl<'a, T, const N: usize> Clone for Iter<'a, T, N> { } /// A mutable iterator over the items of a queue -pub struct IterMut<'a, T, const N: usize> { - rb: &'a mut Queue, +pub struct IterMutView<'a, T> { + rb: &'a mut QueueView, index: usize, len: usize, } -impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> { +impl<'a, T> Iterator for IterView<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { if self.index < self.len { let head = self.rb.head.load(Ordering::Relaxed); - let i = (head + self.index) % N; + let i = (head + self.index) % self.rb.n(); self.index += 1; Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) }) @@ -388,46 +623,46 @@ impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> { } } -impl<'a, T, const N: usize> Iterator for IterMut<'a, T, N> { - type Item = &'a mut T; - - fn next(&mut self) -> Option { +impl<'a, T> DoubleEndedIterator for IterView<'a, T> { + fn next_back(&mut self) -> Option { if self.index < self.len { let head = self.rb.head.load(Ordering::Relaxed); - let i = (head + self.index) % N; - self.index += 1; - - Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) }) + // self.len > 0, since it's larger than self.index > 0 + let i = (head + self.len - 1) % self.rb.n(); + self.len -= 1; + Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) }) } else { None } } } -impl<'a, T, const N: usize> DoubleEndedIterator for Iter<'a, T, N> { +impl<'a, T> DoubleEndedIterator for IterMutView<'a, T> { fn next_back(&mut self) -> Option { if self.index < self.len { let head = self.rb.head.load(Ordering::Relaxed); // self.len > 0, since it's larger than self.index > 0 - let i = (head + self.len - 1) % N; + let i = (head + self.len - 1) % self.rb.n(); self.len -= 1; - Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) }) + Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) }) } else { None } } } -impl<'a, T, const N: usize> DoubleEndedIterator for IterMut<'a, T, N> { - fn next_back(&mut self) -> Option { +impl<'a, T> Iterator for IterMutView<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { if self.index < self.len { let head = self.rb.head.load(Ordering::Relaxed); - // self.len > 0, since it's larger than self.index > 0 - let i = (head + self.len - 1) % N; - self.len -= 1; + let i = (head + self.index) % self.rb.n(); + self.index += 1; + Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) }) } else { None @@ -435,9 +670,61 @@ impl<'a, T, const N: usize> DoubleEndedIterator for IterMut<'a, T, N> { } } -impl Drop for Queue { +/// A mutable iterator over the items of a queue +pub struct Iter<'a, T, const N: usize> { + inner: IterView<'a, T>, + /// PhantomData to keep the `N` const generic and avoid a breaking change + phantom: PhantomData<[T; N]>, +} + +impl<'a, T, const N: usize> Clone for Iter<'a, T, N> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + phantom: PhantomData, + } + } +} + +/// A mutable iterator over the items of a queue +pub struct IterMut<'a, T, const N: usize> { + inner: IterMutView<'a, T>, + /// PhantomData to keep the `N` const generic and avoid a breaking change + phantom: PhantomData<[T; N]>, +} + +impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.inner.next() + } +} + +impl<'a, T, const N: usize> Iterator for IterMut<'a, T, N> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + self.inner.next() + } +} + +impl<'a, T, const N: usize> DoubleEndedIterator for Iter<'a, T, N> { + fn next_back(&mut self) -> Option { + self.inner.next_back() + } +} + +impl<'a, T, const N: usize> DoubleEndedIterator for IterMut<'a, T, N> { + fn next_back(&mut self) -> Option { + self.inner.next_back() + } +} + +impl Drop for private::QueueInner { fn drop(&mut self) { - for item in self { + let this = private::QueueBuffer::as_mut_view(self); + for item in this { unsafe { ptr::drop_in_place(item); } @@ -446,6 +733,15 @@ impl Drop for Queue { } impl fmt::Debug for Queue +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_view().fmt(f) + } +} + +impl fmt::Debug for QueueView where T: fmt::Debug, { @@ -455,6 +751,15 @@ where } impl hash::Hash for Queue +where + T: hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.as_view().hash(state) + } +} + +impl hash::Hash for QueueView where T: hash::Hash, { @@ -466,6 +771,24 @@ where } } +impl<'a, T> IntoIterator for &'a QueueView { + type Item = &'a T; + type IntoIter = IterView<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut QueueView { + type Item = &'a mut T; + type IntoIter = IterMutView<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + impl<'a, T, const N: usize> IntoIterator for &'a Queue { type Item = &'a T; type IntoIter = Iter<'a, T, N>; @@ -487,20 +810,164 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut Queue { /// A queue "consumer"; it can dequeue items from the queue /// NOTE the consumer semantically owns the `head` pointer of the queue pub struct Consumer<'a, T, const N: usize> { - rb: &'a Queue, + phantom: PhantomData<[T; N]>, + inner: ConsumerView<'a, T>, } -unsafe impl<'a, T, const N: usize> Send for Consumer<'a, T, N> where T: Send {} - /// A queue "producer"; it can enqueue items into the queue /// NOTE the producer semantically owns the `tail` pointer of the queue pub struct Producer<'a, T, const N: usize> { - rb: &'a Queue, + phantom: PhantomData<[T; N]>, + inner: ProducerView<'a, T>, } unsafe impl<'a, T, const N: usize> Send for Producer<'a, T, N> where T: Send {} impl<'a, T, const N: usize> Consumer<'a, T, N> { + /// Returns the item in the front of the queue, or `None` if the queue is empty + #[inline] + pub fn dequeue(&mut self) -> Option { + self.inner.dequeue() + } + + /// Returns the item in the front of the queue, without checking if there are elements in the + /// queue + /// + /// # Safety + /// + /// See [`Queue::dequeue_unchecked`] + #[inline] + pub unsafe fn dequeue_unchecked(&mut self) -> T { + self.inner.dequeue_unchecked() + } + + /// Returns if there are any items to dequeue. When this returns `true`, at least the + /// first subsequent dequeue will succeed + #[inline] + pub fn ready(&self) -> bool { + self.inner.ready() + } + + /// Returns the number of elements in the queue + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns true if the queue is empty + /// + /// # Examples + /// + /// ``` + /// use heapless::spsc::Queue; + /// + /// let mut queue: Queue = Queue::new(); + /// let (mut producer, mut consumer) = queue.split(); + /// assert!(consumer.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns the maximum number of elements the queue can hold + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Returns the item in the front of the queue without dequeuing, or `None` if the queue is + /// empty + /// + /// # Examples + /// + /// ``` + /// use heapless::spsc::Queue; + /// + /// let mut queue: Queue = Queue::new(); + /// let (mut producer, mut consumer) = queue.split(); + /// assert_eq!(None, consumer.peek()); + /// producer.enqueue(1); + /// assert_eq!(Some(&1), consumer.peek()); + /// assert_eq!(Some(1), consumer.dequeue()); + /// assert_eq!(None, consumer.peek()); + /// ``` + #[inline] + pub fn peek(&self) -> Option<&T> { + self.inner.peek() + } +} + +impl<'a, T, const N: usize> Producer<'a, T, N> { + /// Adds an `item` to the end of the queue, returns back the `item` if the queue is full + #[inline] + pub fn enqueue(&mut self, val: T) -> Result<(), T> { + self.inner.enqueue(val) + } + + /// Adds an `item` to the end of the queue, without checking if the queue is full + /// + /// # Safety + /// + /// See [`Queue::enqueue_unchecked`] + #[inline] + pub unsafe fn enqueue_unchecked(&mut self, val: T) { + self.inner.enqueue_unchecked(val) + } + + /// Returns if there is any space to enqueue a new item. When this returns true, at + /// least the first subsequent enqueue will succeed. + #[inline] + pub fn ready(&self) -> bool { + self.inner.ready() + } + + /// Returns the number of elements in the queue + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns true if the queue is empty + /// + /// # Examples + /// + /// ``` + /// use heapless::spsc::Queue; + /// + /// let mut queue: Queue = Queue::new(); + /// let (mut producer, mut consumer) = queue.split(); + /// assert!(producer.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns the maximum number of elements the queue can hold + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } +} + +/// A queue "consumer"; it can dequeue items from the queue +/// NOTE the consumer semantically owns the `head` pointer of the queue +pub struct ConsumerView<'a, T> { + rb: &'a QueueView, +} + +unsafe impl<'a, T> Send for ConsumerView<'a, T> where T: Send {} + +/// A queue "producer"; it can enqueue items into the queue +/// NOTE the producer semantically owns the `tail` pointer of the queue +pub struct ProducerView<'a, T> { + rb: &'a QueueView, +} + +unsafe impl<'a, T> Send for ProducerView<'a, T> where T: Send {} + +impl<'a, T> ConsumerView<'a, T> { /// Returns the item in the front of the queue, or `None` if the queue is empty #[inline] pub fn dequeue(&mut self) -> Option { @@ -575,7 +1042,7 @@ impl<'a, T, const N: usize> Consumer<'a, T, N> { } } -impl<'a, T, const N: usize> Producer<'a, T, N> { +impl<'a, T> ProducerView<'a, T> { /// Adds an `item` to the end of the queue, returns back the `item` if the queue is full #[inline] pub fn enqueue(&mut self, val: T) -> Result<(), T> { From f64c713bc65049b7a7ac878b5c1fdddf4cc6629a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Thu, 27 Jun 2024 17:51:44 +0200 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bea10baa6..26692f7c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `VecView`, the `!Sized` version of `Vec`. - Added pool implementations for 64-bit architectures. - Added `IntoIterator` implementation for `LinearMap` +- Added `spsc::QueueView`, the `!Sized` version of `spsc::Queue`. ### Changed