From 133bdaa4e3d82b03b7771a86a79f5355583391c9 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 27 Apr 2025 02:27:53 +0200 Subject: [PATCH] Clean up queue docs. --- src/mpmc.rs | 134 ++++++++++++++++++------------------- src/spsc.rs | 187 ++++++++++++++++++++++++++-------------------------- 2 files changed, 159 insertions(+), 162 deletions(-) diff --git a/src/mpmc.rs b/src/mpmc.rs index 90b0217c..f676c7c9 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -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 = Queue::new(); //! -//! static Q: Queue::<_, 2> = 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::::enqueue().ok()` (`z`) | `Q8::::dequeue()` (`z`) | -//! -|----------------------------------|-----------------------------| -//! 0|34 |35 | -//! 1|52 |53 | -//! 2|69 |71 | +//! | Method | Time | N | +//! |:----------------------------|-----:|---:| +//! | `Queue::::enqueue()` | 34 | 0 | +//! | `Queue::::enqueue()` | 52 | 1 | +//! | `Queue::::enqueue()` | 69 | 2 | +//! | `Queue::::dequeue()` | 35 | 0 | +//! | `Queue::::dequeue()` | 53 | 1 | +//! | `Queue::::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::()`. 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::()`, as both operations include +//! `ptr::read::()` or `ptr::write::()` 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 { 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. +/// A statically allocated multi-producer, multi-consumer queue with a capacity of `N` elements. +/// +///
+/// +/// `N` must be a power of 2. +/// +///
+/// +/// The maximum value of `N` is 128 if the `mpmc_large` feature is not enabled. pub type Queue = QueueInner>; -/// 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 = QueueInner; impl Queue { - /// Creates an empty queue + /// Creates an empty queue. pub const fn new() -> Self { const { assert!(N > 1); @@ -156,17 +144,23 @@ impl Queue { } } - /// Used in `Storage` implementation + /// Used in `Storage` implementation. pub(crate) fn as_view_private(&self) -> &QueueView { self } - /// Used in `Storage` implementation + /// Used in `Storage` implementation. pub(crate) fn as_view_mut_private(&mut self) -> &mut QueueView { self } } impl QueueInner { + /// 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 QueueInner { (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 { 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 Default for Queue { impl Drop for QueueInner { 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 = Queue::new(); + assert_eq!(q.capacity(), CAPACITY); + for _ in 0..CAPACITY { q.enqueue(0xAA).unwrap(); } diff --git a/src/spsc.rs b/src/spsc.rs index a0cb7307..eb206456 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -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 . -//! -//! # 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 = 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::enqueue` | 16| -//! `Queue::enqueue` | 14| -//! `Consumer::dequeue` | 15| -//! `Queue::dequeue` | 12| +//! | Method | Time | +//! |:-------------------------------|-----:| +//! | `Producer::::enqueue()` | 16 | +//! | `Queue::::enqueue()` | 14 | +//! | `Consumer::::dequeue()` | 15 | +//! | `Queue::::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::()`, as both operations include //! `ptr::read::()` or `ptr::write::()` 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 { /// ///
/// -/// 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. /// ///
/// @@ -143,7 +142,7 @@ pub type Queue = QueueInner>; pub type QueueView = QueueInner; impl Queue { - /// 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 Queue { } } - /// 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 { self @@ -196,13 +187,13 @@ impl QueueInner { 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 QueueInner { % 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 QueueInner { } } - /// 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 QueueInner { } } - /// 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 { 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 QueueInner { } // 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 QueueInner { } // 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 QueueInner { .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 { @@ -341,7 +335,8 @@ impl QueueInner { } } - // 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 QueueInner { } /// 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>; -/// 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 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>; -/// 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, } -/// 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>; -/// 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 Send for ConsumerInner<'_, T, S> where T: Send {} @@ -693,48 +692,50 @@ pub struct ProducerInner<'a, T, S: Storage> { rb: &'a QueueInner, } -/// 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>; -/// 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 Send for ProducerInner<'_, T, S> where T: Send {} impl 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 { 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 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 ConsumerInner<'_, T, S> { } impl 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 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 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() } }