Skip to content

Commit c544954

Browse files
authored
Merge pull request #564 from reitermarkus/queue-docs
Clean up queue docs.
2 parents f692384 + 133bdaa commit c544954

File tree

2 files changed

+159
-162
lines changed

2 files changed

+159
-162
lines changed

src/mpmc.rs

Lines changed: 65 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,72 @@
1-
//! A fixed capacity Multiple-Producer Multiple-Consumer (MPMC) lock-free queue.
1+
//! A fixed capacity multiple-producer, multiple-consumer (MPMC) lock-free queue.
22
//!
3-
//! NOTE: This module requires atomic CAS operations. On targets where they're not natively available,
4-
//! they are emulated by the [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
3+
//! **Note:** This module requires atomic compare-and-swap (CAS) instructions. On
4+
//! targets where they're not natively available, they are emulated by the
5+
//! [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
56
//!
67
//! # Example
78
//!
8-
//! This queue can be constructed in "const context". Placing it in a `static` variable lets *all*
9-
//! contexts (interrupts/threads/`main`) safely enqueue and dequeue items from it.
9+
//! This queue can be constructed in `const` context. Placing it in a `static` variable lets *all*
10+
//! contexts (interrupts/threads/`main`) safely enqueue and dequeue items.
1011
//!
11-
//! ``` ignore
12-
//! #![no_main]
13-
//! #![no_std]
14-
//!
15-
//! use panic_semihosting as _;
16-
//!
17-
//! use cortex_m::{asm, peripheral::syst::SystClkSource};
18-
//! use cortex_m_rt::{entry, exception};
19-
//! use cortex_m_semihosting::hprintln;
20-
//! use heapless::mpmc::Queue::<_, 2>;
12+
//! ```
13+
//! use core::sync::atomic::{AtomicU8, Ordering};
2114
//!
22-
//! static Q: Queue::<_, 2><u8> = Q2::new();
15+
//! use heapless::mpmc::Queue;
2316
//!
24-
//! #[entry]
25-
//! fn main() -> ! {
26-
//! if let Some(p) = cortex_m::Peripherals::take() {
27-
//! let mut syst = p.SYST;
17+
//! static Q: Queue<u8, 2> = Queue::new();
2818
//!
29-
//! // configures the system timer to trigger a SysTick exception every second
30-
//! syst.set_clock_source(SystClkSource::Core);
31-
//! syst.set_reload(12_000_000);
32-
//! syst.enable_counter();
33-
//! syst.enable_interrupt();
34-
//! }
19+
//! fn main() {
20+
//! // Configure systick interrupt.
3521
//!
3622
//! loop {
3723
//! if let Some(x) = Q.dequeue() {
38-
//! hprintln!("{}", x).ok();
24+
//! println!("{}", x);
3925
//! } else {
40-
//! asm::wfi();
26+
//! // Wait for interrupt.
4127
//! }
28+
//! # break
4229
//! }
4330
//! }
4431
//!
45-
//! #[exception]
46-
//! fn SysTick() {
47-
//! static mut COUNT: u8 = 0;
32+
//! fn systick() {
33+
//! static COUNT: AtomicU8 = AtomicU8::new(0);
34+
//! let count = COUNT.fetch_add(1, Ordering::SeqCst);
4835
//!
49-
//! Q.enqueue(*COUNT).ok();
50-
//! *COUNT += 1;
36+
//! # let _ =
37+
//! Q.enqueue(count);
5138
//! }
5239
//! ```
5340
//!
5441
//! # Benchmark
5542
//!
56-
//! Measured on a ARM Cortex-M3 core running at 8 MHz and with zero Flash wait cycles
43+
//! Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with `-C opt-level=z`:
5744
//!
58-
//! N| `Q8::<u8>::enqueue().ok()` (`z`) | `Q8::<u8>::dequeue()` (`z`) |
59-
//! -|----------------------------------|-----------------------------|
60-
//! 0|34 |35 |
61-
//! 1|52 |53 |
62-
//! 2|69 |71 |
45+
//! | Method | Time | N |
46+
//! |:----------------------------|-----:|---:|
47+
//! | `Queue::<u8, 8>::enqueue()` | 34 | 0 |
48+
//! | `Queue::<u8, 8>::enqueue()` | 52 | 1 |
49+
//! | `Queue::<u8, 8>::enqueue()` | 69 | 2 |
50+
//! | `Queue::<u8, 8>::dequeue()` | 35 | 0 |
51+
//! | `Queue::<u8, 8>::dequeue()` | 53 | 1 |
52+
//! | `Queue::<u8, 8>::dequeue()` | 71 | 2 |
6353
//!
64-
//! - `N` denotes the number of *interruptions*. On Cortex-M, an interruption consists of an
54+
//! - N denotes the number of interruptions. On Cortex-M, an interruption consists of an
6555
//! interrupt handler preempting the would-be atomic section of the `enqueue`/`dequeue`
6656
//! operation. Note that it does *not* matter if the higher priority handler uses the queue or
6757
//! not.
68-
//! - All execution times are in clock cycles. 1 clock cycle = 125 ns.
69-
//! - Execution time is *dependent* of `mem::size_of::<T>()`. Both operations include one
70-
//! `memcpy(T)` in their successful path.
71-
//! - The optimization level is indicated in parentheses.
72-
//! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue`
73-
//! and `Ok` is returned by `enqueue`).
74-
//!
75-
//! # Portability
76-
//!
77-
//! This module requires CAS atomic instructions which are not available on all architectures
78-
//! (e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`)). These atomics can be
79-
//! emulated however with [`portable-atomic`](https://crates.io/crates/portable-atomic), which is
80-
//! enabled with the `cas` feature and is enabled by default for `thumbv6m-none-eabi` and `riscv32`
81-
//! targets.
58+
//! - All execution times are in clock cycles (1 clock cycle = 125 ns).
59+
//! - Execution time is *dependent* on `mem::size_of::<T>()`, as both operations include
60+
//! `ptr::read::<T>()` or `ptr::write::<T>()` in their successful path.
61+
//! - The numbers reported correspond to the successful path, i.e. `dequeue` returning `Some`
62+
//! and `enqueue` returning `Ok`.
8263
//!
8364
//! # References
8465
//!
85-
//! This is an implementation of Dmitry Vyukov's ["Bounded MPMC queue"][0] minus the cache padding.
66+
//! This is an implementation of Dmitry Vyukov's [bounded MPMC queue], minus the
67+
//! cache padding.
8668
//!
87-
//! [0]: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
69+
//! [bounded MPMC queue]: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
8870
8971
use core::{cell::UnsafeCell, mem::MaybeUninit};
9072

@@ -122,18 +104,24 @@ pub struct QueueInner<T, S: Storage> {
122104
buffer: UnsafeCell<S::Buffer<Cell<T>>>,
123105
}
124106

125-
/// MPMC queue with a capacity for N elements
126-
/// N must be a power of 2
127-
/// The max value of N is `u8::MAX` - 1 if `mpmc_large` feature is not enabled.
107+
/// A statically allocated multi-producer, multi-consumer queue with a capacity of `N` elements.
108+
///
109+
/// <div class="warning">
110+
///
111+
/// `N` must be a power of 2.
112+
///
113+
/// </div>
114+
///
115+
/// The maximum value of `N` is 128 if the `mpmc_large` feature is not enabled.
128116
pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
129117

130-
/// MPMC queue with a capacity for N elements
131-
/// N must be a power of 2
132-
/// The max value of N is `u8::MAX` - 1 if `mpmc_large` feature is not enabled.
118+
/// A [`Queue`] with dynamic capacity.
119+
///
120+
/// [`Queue`] coerces to `QueueView`. `QueueView` is `!Sized`, meaning it can only ever be used by reference.
133121
pub type QueueView<T> = QueueInner<T, ViewStorage>;
134122

135123
impl<T, const N: usize> Queue<T, N> {
136-
/// Creates an empty queue
124+
/// Creates an empty queue.
137125
pub const fn new() -> Self {
138126
const {
139127
assert!(N > 1);
@@ -156,17 +144,23 @@ impl<T, const N: usize> Queue<T, N> {
156144
}
157145
}
158146

159-
/// Used in `Storage` implementation
147+
/// Used in `Storage` implementation.
160148
pub(crate) fn as_view_private(&self) -> &QueueView<T> {
161149
self
162150
}
163-
/// Used in `Storage` implementation
151+
/// Used in `Storage` implementation.
164152
pub(crate) fn as_view_mut_private(&mut self) -> &mut QueueView<T> {
165153
self
166154
}
167155
}
168156

169157
impl<T, S: Storage> QueueInner<T, S> {
158+
/// Returns the maximum number of elements the queue can hold.
159+
#[inline]
160+
pub fn capacity(&self) -> usize {
161+
S::len(self.buffer.get())
162+
}
163+
170164
/// Get a reference to the `Queue`, erasing the `N` const-generic.
171165
///
172166
///
@@ -212,14 +206,14 @@ impl<T, S: Storage> QueueInner<T, S> {
212206
(S::len(self.buffer.get()) - 1) as _
213207
}
214208

215-
/// Returns the item in the front of the queue, or `None` if the queue is empty
209+
/// Returns the item in the front of the queue, or `None` if the queue is empty.
216210
pub fn dequeue(&self) -> Option<T> {
217211
unsafe { dequeue(S::as_ptr(self.buffer.get()), &self.dequeue_pos, self.mask()) }
218212
}
219213

220-
/// Adds an `item` to the end of the queue
214+
/// Adds an `item` to the end of the queue.
221215
///
222-
/// Returns back the `item` if the queue is full
216+
/// Returns back the `item` if the queue is full.
223217
pub fn enqueue(&self, item: T) -> Result<(), T> {
224218
unsafe {
225219
enqueue(
@@ -240,7 +234,7 @@ impl<T, const N: usize> Default for Queue<T, N> {
240234

241235
impl<T, S: Storage> Drop for QueueInner<T, S> {
242236
fn drop(&mut self) {
243-
// drop all contents currently in the queue
237+
// Drop all elements currently in the queue.
244238
while self.dequeue().is_some() {}
245239
}
246240
}
@@ -416,6 +410,8 @@ mod tests {
416410

417411
let q: Queue<u8, CAPACITY> = Queue::new();
418412

413+
assert_eq!(q.capacity(), CAPACITY);
414+
419415
for _ in 0..CAPACITY {
420416
q.enqueue(0xAA).unwrap();
421417
}

0 commit comments

Comments
 (0)