mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-09-27 12:30:35 +00:00
Merge pull request #564 from reitermarkus/queue-docs
Clean up queue docs.
This commit is contained in:
commit
c544954326
134
src/mpmc.rs
134
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<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();
|
||||
}
|
||||
|
187
src/spsc.rs
187
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 <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()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user