Merge pull request #564 from reitermarkus/queue-docs

Clean up queue docs.
This commit is contained in:
Markus Reiter 2025-04-27 15:11:46 +00:00 committed by GitHub
commit c544954326
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 159 additions and 162 deletions

View File

@ -1,90 +1,72 @@
//! A fixed capacity Multiple-Producer Multiple-Consumer (MPMC) lock-free queue.
//! A fixed capacity multiple-producer, multiple-consumer (MPMC) lock-free queue.
//!
//! NOTE: This module requires atomic CAS operations. On targets where they're not natively available,
//! they are emulated by the [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
//! **Note:** This module requires atomic compare-and-swap (CAS) instructions. On
//! targets where they're not natively available, they are emulated by the
//! [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
//!
//! # Example
//!
//! This queue can be constructed in "const context". Placing it in a `static` variable lets *all*
//! contexts (interrupts/threads/`main`) safely enqueue and dequeue items from it.
//! This queue can be constructed in `const` context. Placing it in a `static` variable lets *all*
//! contexts (interrupts/threads/`main`) safely enqueue and dequeue items.
//!
//! ``` ignore
//! #![no_main]
//! #![no_std]
//! ```
//! use core::sync::atomic::{AtomicU8, Ordering};
//!
//! use panic_semihosting as _;
//! use heapless::mpmc::Queue;
//!
//! use cortex_m::{asm, peripheral::syst::SystClkSource};
//! use cortex_m_rt::{entry, exception};
//! use cortex_m_semihosting::hprintln;
//! use heapless::mpmc::Queue::<_, 2>;
//! static Q: Queue<u8, 2> = Queue::new();
//!
//! static Q: Queue::<_, 2><u8> = Q2::new();
//!
//! #[entry]
//! fn main() -> ! {
//! if let Some(p) = cortex_m::Peripherals::take() {
//! let mut syst = p.SYST;
//!
//! // configures the system timer to trigger a SysTick exception every second
//! syst.set_clock_source(SystClkSource::Core);
//! syst.set_reload(12_000_000);
//! syst.enable_counter();
//! syst.enable_interrupt();
//! }
//! fn main() {
//! // Configure systick interrupt.
//!
//! loop {
//! if let Some(x) = Q.dequeue() {
//! hprintln!("{}", x).ok();
//! println!("{}", x);
//! } else {
//! asm::wfi();
//! // Wait for interrupt.
//! }
//! # break
//! }
//! }
//!
//! #[exception]
//! fn SysTick() {
//! static mut COUNT: u8 = 0;
//! fn systick() {
//! static COUNT: AtomicU8 = AtomicU8::new(0);
//! let count = COUNT.fetch_add(1, Ordering::SeqCst);
//!
//! Q.enqueue(*COUNT).ok();
//! *COUNT += 1;
//! # let _ =
//! Q.enqueue(count);
//! }
//! ```
//!
//! # Benchmark
//!
//! Measured on a ARM Cortex-M3 core running at 8 MHz and with zero Flash wait cycles
//! Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with `-C opt-level=z`:
//!
//! N| `Q8::<u8>::enqueue().ok()` (`z`) | `Q8::<u8>::dequeue()` (`z`) |
//! -|----------------------------------|-----------------------------|
//! 0|34 |35 |
//! 1|52 |53 |
//! 2|69 |71 |
//! | Method | Time | N |
//! |:----------------------------|-----:|---:|
//! | `Queue::<u8, 8>::enqueue()` | 34 | 0 |
//! | `Queue::<u8, 8>::enqueue()` | 52 | 1 |
//! | `Queue::<u8, 8>::enqueue()` | 69 | 2 |
//! | `Queue::<u8, 8>::dequeue()` | 35 | 0 |
//! | `Queue::<u8, 8>::dequeue()` | 53 | 1 |
//! | `Queue::<u8, 8>::dequeue()` | 71 | 2 |
//!
//! - `N` denotes the number of *interruptions*. On Cortex-M, an interruption consists of an
//! - N denotes the number of interruptions. On Cortex-M, an interruption consists of an
//! interrupt handler preempting the would-be atomic section of the `enqueue`/`dequeue`
//! operation. Note that it does *not* matter if the higher priority handler uses the queue or
//! not.
//! - All execution times are in clock cycles. 1 clock cycle = 125 ns.
//! - Execution time is *dependent* of `mem::size_of::<T>()`. Both operations include one
//! `memcpy(T)` in their successful path.
//! - The optimization level is indicated in parentheses.
//! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue`
//! and `Ok` is returned by `enqueue`).
//!
//! # Portability
//!
//! This module requires CAS atomic instructions which are not available on all architectures
//! (e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`)). These atomics can be
//! emulated however with [`portable-atomic`](https://crates.io/crates/portable-atomic), which is
//! enabled with the `cas` feature and is enabled by default for `thumbv6m-none-eabi` and `riscv32`
//! targets.
//! - All execution times are in clock cycles (1 clock cycle = 125 ns).
//! - Execution time is *dependent* on `mem::size_of::<T>()`, as both operations include
//! `ptr::read::<T>()` or `ptr::write::<T>()` in their successful path.
//! - The numbers reported correspond to the successful path, i.e. `dequeue` returning `Some`
//! and `enqueue` returning `Ok`.
//!
//! # References
//!
//! This is an implementation of Dmitry Vyukov's ["Bounded MPMC queue"][0] minus the cache padding.
//! This is an implementation of Dmitry Vyukov's [bounded MPMC queue], minus the
//! cache padding.
//!
//! [0]: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
//! [bounded MPMC queue]: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
use core::{cell::UnsafeCell, mem::MaybeUninit};
@ -122,18 +104,24 @@ pub struct QueueInner<T, S: Storage> {
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.
/// A statically allocated multi-producer, multi-consumer queue with a capacity of `N` elements.
///
/// <div class="warning">
///
/// `N` must be a power of 2.
///
/// </div>
///
/// The maximum value of `N` is 128 if the `mpmc_large` feature is not enabled.
pub type Queue<T, const N: usize> = QueueInner<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.
/// A [`Queue`] with dynamic capacity.
///
/// [`Queue`] coerces to `QueueView`. `QueueView` is `!Sized`, meaning it can only ever be used by reference.
pub type QueueView<T> = QueueInner<T, ViewStorage>;
impl<T, const N: usize> Queue<T, N> {
/// Creates an empty queue
/// Creates an empty queue.
pub const fn new() -> Self {
const {
assert!(N > 1);
@ -156,17 +144,23 @@ impl<T, const N: usize> Queue<T, N> {
}
}
/// Used in `Storage` implementation
/// Used in `Storage` implementation.
pub(crate) fn as_view_private(&self) -> &QueueView<T> {
self
}
/// Used in `Storage` implementation
/// Used in `Storage` implementation.
pub(crate) fn as_view_mut_private(&mut self) -> &mut QueueView<T> {
self
}
}
impl<T, S: Storage> QueueInner<T, S> {
/// Returns the maximum number of elements the queue can hold.
#[inline]
pub fn capacity(&self) -> usize {
S::len(self.buffer.get())
}
/// Get a reference to the `Queue`, erasing the `N` const-generic.
///
///
@ -212,14 +206,14 @@ impl<T, S: Storage> QueueInner<T, S> {
(S::len(self.buffer.get()) - 1) as _
}
/// Returns the item in the front of the queue, or `None` if the queue is empty
/// Returns the item in the front of the queue, or `None` if the queue is empty.
pub fn dequeue(&self) -> Option<T> {
unsafe { dequeue(S::as_ptr(self.buffer.get()), &self.dequeue_pos, self.mask()) }
}
/// Adds an `item` to the end of the queue
/// Adds an `item` to the end of the queue.
///
/// Returns back the `item` if the queue is full
/// Returns back the `item` if the queue is full.
pub fn enqueue(&self, item: T) -> Result<(), T> {
unsafe {
enqueue(
@ -240,7 +234,7 @@ impl<T, const N: usize> Default for Queue<T, N> {
impl<T, S: Storage> Drop for QueueInner<T, S> {
fn drop(&mut self) {
// drop all contents currently in the queue
// Drop all elements currently in the queue.
while self.dequeue().is_some() {}
}
}
@ -416,6 +410,8 @@ mod tests {
let q: Queue<u8, CAPACITY> = Queue::new();
assert_eq!(q.capacity(), CAPACITY);
for _ in 0..CAPACITY {
q.enqueue(0xAA).unwrap();
}

View File

@ -1,38 +1,31 @@
//! A fixed capacity single-producer, single-consumer (SPSC) queue.
//! A fixed capacity single-producer, single-consumer (SPSC) lock-free queue.
//!
//! Implementation based on <https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular>.
//!
//! # Portability
//!
//! This module requires CAS atomic instructions which are not available on all architectures,
//! e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`). These atomics can be
//! emulated however with [`portable-atomic`](https://crates.io/crates/portable-atomic), which is
//! enabled with the `cas` feature and is enabled by default for `thumbv6m-none-eabi` and `riscv32`
//! targets.
//! *Note:* This module requires atomic load and store instructions. On
//! targets where they're not natively available, they are emulated by the
//! [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
//!
//! # Examples
//!
//! [`Queue`] can be used as a plain queue.
//! [`Queue`] can be used as a plain queue:
//!
//! ```
//! use heapless::spsc::Queue;
//!
//! let mut queue: Queue<u8, 4> = Queue::new();
//!
//! assert!(queue.enqueue(0).is_ok());
//! assert!(queue.enqueue(1).is_ok());
//! assert!(queue.enqueue(2).is_ok());
//! assert!(queue.enqueue(3).is_err()); // Queue is full.
//! assert_eq!(queue.enqueue(0), Ok(()));
//! assert_eq!(queue.enqueue(1), Ok(()));
//! assert_eq!(queue.enqueue(2), Ok(()));
//! assert_eq!(queue.enqueue(3), Err(3)); // Queue is full.
//!
//! assert_eq!(queue.dequeue(), Some(0));
//! ```
//!
//! [`Queue`] can be [`split`](QueueInner::split) and then be used in single-producer, single-consumer mode.
//! [`Queue::split`] can be used to split the queue into a [`Producer`]/[`Consumer`] pair.
//!
//! After splitting a `&'static mut Queue`, the resulting [`Producer`] and [`Consumer`]
//! can be moved into different execution contexts, e.g. threads, interrupt handlers, etc.
//!
//! "no alloc" applications can create a `&'static mut` reference to a `Queue` -- using a static
//! variable and then `split` it, which consumes the static reference. The resulting `Producer`
//! and `Consumer` can then be moved into different execution contexts, e.g. threads, interrupt handlers,
//! etc.
//!
//! ```
//! use heapless::spsc::{Producer, Queue};
@ -86,20 +79,26 @@
//!
//! # Benchmarks
//!
//! Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with `-C opt-level=3`:
//! Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with `-C opt-level=3`:
//!
//! Method | Time |
//! ------------------------|-----:|
//! `Producer<u8>::enqueue` | 16|
//! `Queue<u8>::enqueue` | 14|
//! `Consumer<u8>::dequeue` | 15|
//! `Queue<u8>::dequeue` | 12|
//! | Method | Time |
//! |:-------------------------------|-----:|
//! | `Producer::<u8, _>::enqueue()` | 16 |
//! | `Queue::<u8, _>::enqueue()` | 14 |
//! | `Consumer::<u8, _>::dequeue()` | 15 |
//! | `Queue::<u8, _>::dequeue()` | 12 |
//!
//! - All execution times are in clock cycles. 1 clock cycle = 125 ns.
//! - All execution times are in clock cycles (1 clock cycle = 125 ns).
//! - Execution time is *dependent* on `mem::size_of::<T>()`, as both operations include
//! `ptr::read::<T>()` or `ptr::write::<T>()` in their successful path.
//! - The numbers reported correspond to the successful path, i.e. `Some` is returned by `dequeue`
//! and `Ok` is returned by `enqueue`.
//! - The numbers reported correspond to the successful path, i.e. `dequeue` returning `Some`
//! and `enqueue` returning `Ok`.
//!
//! # References
//!
//! This is an implementation based on [https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular](
//! https://web.archive.org/web/20250117082625/https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular
//! ).
use core::{borrow::Borrow, cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
@ -130,7 +129,7 @@ pub struct QueueInner<T, S: Storage> {
///
/// <div class="warning">
///
/// To get better performance use a value for `N` that is a power of 2, e.g. 16, 32, etc.
/// To get better performance, use a value for `N` that is a power of 2.
///
/// </div>
///
@ -143,7 +142,7 @@ pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
pub type QueueView<T> = QueueInner<T, ViewStorage>;
impl<T, const N: usize> Queue<T, N> {
/// Creates an empty queue with a fixed capacity of `N - 1`
/// Creates an empty queue.
pub const fn new() -> Self {
const {
assert!(N > 1);
@ -156,14 +155,6 @@ impl<T, const N: usize> Queue<T, N> {
}
}
/// Returns the maximum number of elements the queue can hold
///
/// For the same method on [`QueueView`], see [`storage_capacity`](QueueInner::storage_capacity)
#[inline]
pub const fn capacity(&self) -> usize {
N - 1
}
/// Used in `Storage` implementation
pub(crate) fn as_view_private(&self) -> &QueueView<T> {
self
@ -196,13 +187,13 @@ impl<T, S: Storage> QueueInner<T, S> {
self.buffer.borrow().len()
}
/// Returns the maximum number of elements the queue can hold
/// Returns the maximum number of elements the queue can hold.
#[inline]
pub fn storage_capacity(&self) -> usize {
pub fn capacity(&self) -> usize {
self.n() - 1
}
/// Returns the number of elements in the queue
/// Returns the number of elements in the queue.
#[inline]
pub fn len(&self) -> usize {
let current_head = self.head.load(Ordering::Relaxed);
@ -214,19 +205,19 @@ impl<T, S: Storage> QueueInner<T, S> {
% self.n()
}
/// Returns `true` if the queue is empty
/// Returns whether the queue is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.head.load(Ordering::Relaxed) == self.tail.load(Ordering::Relaxed)
}
/// Returns `true` if the queue is full
/// Returns whether the queue is full.
#[inline]
pub fn is_full(&self) -> bool {
self.increment(self.tail.load(Ordering::Relaxed)) == self.head.load(Ordering::Relaxed)
}
/// Iterates from the front of the queue to the back
/// Iterates from the front of the queue to the back.
pub fn iter(&self) -> IterInner<'_, T, S> {
IterInner {
rb: self,
@ -235,7 +226,7 @@ impl<T, S: Storage> QueueInner<T, S> {
}
}
/// Returns an iterator that allows modifying each value
/// Returns an iterator that allows modifying each value.
pub fn iter_mut(&mut self) -> IterMutInner<'_, T, S> {
let len = self.len();
IterMutInner {
@ -245,21 +236,21 @@ impl<T, S: Storage> QueueInner<T, S> {
}
}
/// Adds an `item` to the end of the queue
/// Adds an `item` to the end of the queue.
///
/// Returns back the `item` if the queue is full
/// Returns back the `item` if the queue is full.
#[inline]
pub fn enqueue(&mut self, val: T) -> Result<(), T> {
unsafe { self.inner_enqueue(val) }
pub fn enqueue(&mut self, item: T) -> Result<(), T> {
unsafe { self.inner_enqueue(item) }
}
/// Returns the item in the front of the queue, or `None` if the queue is empty
/// Returns the item in the front of the queue, or `None` if the queue is empty.
#[inline]
pub fn dequeue(&mut self) -> Option<T> {
unsafe { self.inner_dequeue() }
}
/// Returns a reference to the item in the front of the queue without dequeuing, or
/// Returns a reference to the item in the front of the queue without dequeuing it, or
/// `None` if the queue is empty.
///
/// # Examples
@ -284,6 +275,7 @@ impl<T, S: Storage> QueueInner<T, S> {
}
// The memory for enqueueing is "owned" by the tail pointer.
//
// NOTE: This internal function uses internal mutability to allow the [`Producer`] to enqueue
// items without doing pointer arithmetic and accessing internal fields of this type.
unsafe fn inner_enqueue(&self, val: T) -> Result<(), T> {
@ -301,6 +293,7 @@ impl<T, S: Storage> QueueInner<T, S> {
}
// The memory for enqueueing is "owned" by the tail pointer.
//
// NOTE: This internal function uses internal mutability to allow the [`Producer`] to enqueue
// items without doing pointer arithmetic and accessing internal fields of this type.
unsafe fn inner_enqueue_unchecked(&self, val: T) {
@ -311,19 +304,20 @@ impl<T, S: Storage> QueueInner<T, S> {
.store(self.increment(current_tail), Ordering::Release);
}
/// Adds an `item` to the end of the queue, without checking if it's full
/// 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
/// 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.inner_enqueue_unchecked(val);
pub unsafe fn enqueue_unchecked(&mut self, item: T) {
self.inner_enqueue_unchecked(item);
}
// The memory for dequeuing is "owned" by the head pointer,.
// The memory for dequeuing is "owned" by the head pointer.
//
// NOTE: This internal function uses internal mutability to allow the [`Consumer`] to dequeue
// items without doing pointer arithmetic and accessing internal fields of this type.
unsafe fn inner_dequeue(&self) -> Option<T> {
@ -341,7 +335,8 @@ impl<T, S: Storage> QueueInner<T, S> {
}
}
// The memory for dequeuing is "owned" by the head pointer,.
// The memory for dequeuing is "owned" by the head pointer.
//
// NOTE: This internal function uses internal mutability to allow the [`Consumer`] to dequeue
// items without doing pointer arithmetic and accessing internal fields of this type.
unsafe fn inner_dequeue_unchecked(&self) -> T {
@ -355,11 +350,13 @@ impl<T, S: Storage> QueueInner<T, S> {
}
/// Returns the item in the front of the queue, without checking if there is something in the
/// queue
/// queue.
///
/// # Safety
///
/// If the queue is empty this operation will return uninitialized memory.
/// The queue must not be empty. Calling this on an empty queue causes [undefined behavior].
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
pub unsafe fn dequeue_unchecked(&mut self) -> T {
self.inner_dequeue_unchecked()
}
@ -520,10 +517,10 @@ pub struct IterInner<'a, T, S: Storage> {
len: usize,
}
/// An iterator over the items of a queue
/// An iterator over the items of a queue.
pub type Iter<'a, T, const N: usize> = IterInner<'a, T, OwnedStorage<N>>;
/// An iterator over the items of a queue
/// An iterator over the items of a queue.
pub type IterView<'a, T> = IterInner<'a, T, ViewStorage>;
impl<T, const N: usize> Clone for Iter<'_, T, N> {
@ -546,10 +543,10 @@ pub struct IterMutInner<'a, T, S: Storage> {
len: usize,
}
/// An iterator over the items of a queue
/// An iterator over the items of a queue.
pub type IterMut<'a, T, const N: usize> = IterMutInner<'a, T, OwnedStorage<N>>;
/// An iterator over the items of a queue
/// An iterator over the items of a queue.
pub type IterMutView<'a, T> = IterMutInner<'a, T, ViewStorage>;
impl<'a, T, S: Storage> Iterator for IterInner<'a, T, S> {
@ -675,12 +672,14 @@ pub struct ConsumerInner<'a, T, S: Storage> {
rb: &'a QueueInner<T, S>,
}
/// A queue "consumer"; it can dequeue items from the queue
/// NOTE the consumer semantically owns the `head` pointer of the queue
/// A consumer; it can dequeue items from the queue.
///
/// **Note:** The consumer semantically owns the `head` pointer of the queue.
pub type Consumer<'a, T, const N: usize> = ConsumerInner<'a, T, OwnedStorage<N>>;
/// A queue "consumer"; it can dequeue items from the queue
/// NOTE the consumer semantically owns the `head` pointer of the queue
/// A consumer; it can dequeue items from the queue.
///
/// **Note:** The consumer semantically owns the `head` pointer of the queue.
pub type ConsumerView<'a, T> = ConsumerInner<'a, T, ViewStorage>;
unsafe impl<T, S: Storage> Send for ConsumerInner<'_, T, S> where T: Send {}
@ -693,48 +692,50 @@ pub struct ProducerInner<'a, T, S: Storage> {
rb: &'a QueueInner<T, S>,
}
/// A queue "producer"; it can enqueue items into the queue
/// NOTE the producer semantically owns the `tail` pointer of the queue
/// A producer; it can enqueue items into the queue.
///
/// **Note:** The producer semantically owns the `tail` pointer of the queue.
pub type Producer<'a, T, const N: usize> = ProducerInner<'a, T, OwnedStorage<N>>;
/// A queue "producer"; it can enqueue items into the queue
/// NOTE the producer semantically owns the `tail` pointer of the queue
/// A producer; it can enqueue items into the queue.
///
/// **Note:** The producer semantically owns the `tail` pointer of the queue.
pub type ProducerView<'a, T> = ProducerInner<'a, T, ViewStorage>;
unsafe impl<T, S: Storage> Send for ProducerInner<'_, T, S> where T: Send {}
impl<T, S: Storage> ConsumerInner<'_, T, S> {
/// Returns the item in the front of the queue, or `None` if the queue is empty
/// Returns the item in the front of the queue, or `None` if the queue is empty.
#[inline]
pub fn dequeue(&mut self) -> Option<T> {
unsafe { self.rb.inner_dequeue() }
}
/// Returns the item in the front of the queue, without checking if there are elements in the
/// queue
/// queue.
///
/// # Safety
///
/// See [`Queue::dequeue_unchecked`]
/// See [`Queue::dequeue_unchecked`].
#[inline]
pub unsafe fn dequeue_unchecked(&mut self) -> T {
self.rb.inner_dequeue_unchecked()
}
/// Returns if there are any items to dequeue. When this returns `true`, at least the
/// first subsequent dequeue will succeed
/// first subsequent dequeue will succeed.
#[inline]
pub fn ready(&self) -> bool {
!self.rb.is_empty()
}
/// Returns the number of elements in the queue
/// Returns the number of elements in the queue.
#[inline]
pub fn len(&self) -> usize {
self.rb.len()
}
/// Returns true if the queue is empty
/// Returns whether the queue is empty.
///
/// # Examples
///
@ -750,14 +751,14 @@ impl<T, S: Storage> ConsumerInner<'_, T, S> {
self.len() == 0
}
/// Returns the maximum number of elements the queue can hold
/// Returns the maximum number of elements the queue can hold.
#[inline]
pub fn capacity(&self) -> usize {
self.rb.storage_capacity()
self.rb.capacity()
}
/// Returns the item in the front of the queue without dequeuing, or `None` if the queue is
/// empty
/// empty.
///
/// # Examples
///
@ -779,20 +780,20 @@ impl<T, S: Storage> ConsumerInner<'_, T, S> {
}
impl<T, S: Storage> ProducerInner<'_, T, S> {
/// Adds an `item` to the end of the queue, returns back the `item` if the queue is full
/// 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> {
unsafe { self.rb.inner_enqueue(val) }
pub fn enqueue(&mut self, item: T) -> Result<(), T> {
unsafe { self.rb.inner_enqueue(item) }
}
/// Adds an `item` to the end of the queue, without checking if the queue is full
/// Adds an `item` to the end of the queue, without checking if the queue is full.
///
/// # Safety
///
/// See [`Queue::enqueue_unchecked`]
/// See [`Queue::enqueue_unchecked`].
#[inline]
pub unsafe fn enqueue_unchecked(&mut self, val: T) {
self.rb.inner_enqueue_unchecked(val);
pub unsafe fn enqueue_unchecked(&mut self, item: T) {
self.rb.inner_enqueue_unchecked(item);
}
/// Returns if there is any space to enqueue a new item. When this returns true, at
@ -802,13 +803,13 @@ impl<T, S: Storage> ProducerInner<'_, T, S> {
!self.rb.is_full()
}
/// Returns the number of elements in the queue
/// Returns the number of elements in the queue.
#[inline]
pub fn len(&self) -> usize {
self.rb.len()
}
/// Returns true if the queue is empty
/// Returns whether the queue is empty.
///
/// # Examples
///
@ -824,10 +825,10 @@ impl<T, S: Storage> ProducerInner<'_, T, S> {
self.len() == 0
}
/// Returns the maximum number of elements the queue can hold
/// Returns the maximum number of elements the queue can hold.
#[inline]
pub fn capacity(&self) -> usize {
self.rb.storage_capacity()
self.rb.capacity()
}
}