Skip to content

Commit 32b797d

Browse files
committed
Improve docs.
1 parent 37b0f32 commit 32b797d

File tree

2 files changed

+53
-58
lines changed

2 files changed

+53
-58
lines changed

src/lib.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
)
5959
)
6060
),
61-
doc = "- [Arc][pool::arc::Arc] -- like `std::sync::Arc` but backed by a lock-free memory pool rather than [global_allocator]"
61+
doc = "- [`Arc`][pool::arc::Arc]: Like `std::sync::Arc` but backed by a lock-free memory pool rather than `[global_allocator]`."
6262
)]
6363
#![cfg_attr(
6464
any(
@@ -75,7 +75,7 @@
7575
)
7676
)
7777
),
78-
doc = "- [Box][pool::boxed::Box] -- like `std::boxed::Box` but backed by a lock-free memory pool rather than [global_allocator]"
78+
doc = "- [`Box`][pool::boxed::Box]: Like `std::boxed::Box` but backed by a lock-free memory pool rather than `[global_allocator]`."
7979
)]
8080
#![cfg_attr(
8181
any(
@@ -92,7 +92,7 @@
9292
)
9393
)
9494
),
95-
doc = "- [Arc][pool::arc::Arc] -- like `std::sync::Arc` but backed by a lock-free memory pool rather than [global_allocator]"
95+
doc = "- [`Arc`][pool::arc::Arc]: Like `std::sync::Arc` but backed by a lock-free memory pool rather than `[global_allocator]`."
9696
)]
9797
#![cfg_attr(
9898
any(
@@ -109,19 +109,19 @@
109109
)
110110
)
111111
),
112-
doc = "- [Object](pool::object::Object) -- objects managed by an object pool"
112+
doc = "- [`Object`](pool::object::Object): Objects managed by an object pool."
113113
)]
114-
//! - [`BinaryHeap`] -- priority queue
115-
//! - [Deque] -- double-ended queue
116-
//! - [`HistoryBuffer`] -- similar to a write-only ring buffer
117-
//! - [`IndexMap`] -- hash table
118-
//! - [`IndexSet`] -- hash set
119-
//! - [`LinearMap`]
120-
//! - [`sorted_linked_list::SortedLinkedList`]
121-
//! - [String]
122-
//! - [Vec]
123-
//! - [`mpmc::Q*`](mpmc) -- multiple producer multiple consumer lock-free queue
124-
//! - [spsc] and [`spsc::Queue`] -- single producer single consumer lock-free queue
114+
//! - [`BinaryHeap`]: A priority queue.
115+
//! - [`Deque`]: A double-ended queue.
116+
//! - [`HistoryBuffer`]: A “history buffer”, similar to a write-only ring buffer.
117+
//! - [`IndexMap`]: A hash table.
118+
//! - [`IndexSet`]: A hash set.
119+
//! - [`LinearMap`]: A linear map.
120+
//! - [`SortedLinkedList`](sorted_linked_list::SortedLinkedList): A sorted linked list.
121+
//! - [`String`]: A string.
122+
//! - [`Vec`]: A vector.
123+
//! - [`mpmc::MpMcQueue`](mpmc): A lock-free multiple-producer, multiple-consumer queue.
124+
//! - [`spsc::Queue`](spsc): A lock-free single-producer, single-consumer queue.
125125
//!
126126
//! # Minimum Supported Rust Version (MSRV)
127127
//!

src/spsc.rs

+38-43
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,49 @@
1-
//! # A fixed capacity Single Producer Single Consumer (SPSC) queue.
1+
//! A fixed capacity single-producer, single-consumer (SPSC) queue.
22
//!
3-
//! Implementation based on <https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular>
3+
//! Implementation based on <https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular>.
44
//!
5-
//! ## Portability
5+
//! # Portability
66
//!
7-
//! This module requires CAS atomic instructions which are not available on all architectures
8-
//! (e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`)). These atomics can be
7+
//! This module requires CAS atomic instructions which are not available on all architectures,
8+
//! e.g. ARMv6-M (`thumbv6m-none-eabi`) and MSP430 (`msp430-none-elf`). These atomics can be
99
//! emulated however with [`portable-atomic`](https://crates.io/crates/portable-atomic), which is
1010
//! enabled with the `cas` feature and is enabled by default for `thumbv6m-none-eabi` and `riscv32`
1111
//! targets.
1212
//!
13-
//! ## Examples
13+
//! # Examples
1414
//!
15-
//! - [Queue] can be used as a plain queue
15+
//! [`Queue`] can be used as a plain queue.
1616
//!
1717
//! ```
1818
//! use heapless::spsc::Queue;
1919
//!
20-
//! let mut rb: Queue<u8, 4> = Queue::new();
20+
//! let mut queue: Queue<u8, 4> = Queue::new();
2121
//!
22-
//! assert!(rb.enqueue(0).is_ok());
23-
//! assert!(rb.enqueue(1).is_ok());
24-
//! assert!(rb.enqueue(2).is_ok());
25-
//! assert!(rb.enqueue(3).is_err()); // full
22+
//! assert!(queue.enqueue(0).is_ok());
23+
//! assert!(queue.enqueue(1).is_ok());
24+
//! assert!(queue.enqueue(2).is_ok());
25+
//! assert!(queue.enqueue(3).is_err()); // Queue is full.
2626
//!
27-
//! assert_eq!(rb.dequeue(), Some(0));
27+
//! assert_eq!(queue.dequeue(), Some(0));
2828
//! ```
2929
//!
30-
//! - [Queue] can be [`Queue::split`] and then be used in Single Producer Single Consumer mode.
30+
//! [`Queue`] can be [`split`](QueueInner::split) and then be used in single-producer, single-consumer mode.
3131
//!
3232
//! "no alloc" applications can create a `&'static mut` reference to a `Queue` -- using a static
33-
//! variable -- and then `split` it: this consumes the static reference. The resulting `Consumer`
34-
//! and `Producer` can then be moved into different execution contexts (threads, interrupt handlers,
35-
//! etc.).
36-
//!
37-
//! Alternatively, you can also create the Queue statically in the global scope by wrapping it with
38-
//! a [static_cell](https://docs.rs/static_cell/latest/static_cell/)
39-
//!
33+
//! variable and then `split` it, which consumes the static reference. The resulting `Producer`
34+
//! and `Consumer` can then be moved into different execution contexts, e.g. threads, interrupt handlers,
35+
//! etc.
4036
//!
4137
//! ```
4238
//! use heapless::spsc::{Producer, Queue};
4339
//!
40+
//! #[derive(Debug)]
4441
//! enum Event {
4542
//! A,
4643
//! B,
4744
//! }
4845
//!
4946
//! fn main() {
50-
//! // Alternatively, use something like `static_cell` to create the `Queue` in the global
51-
//! // scope.
5247
//! let queue: &'static mut Queue<Event, 4> = {
5348
//! static mut Q: Queue<Event, 4> = Queue::new();
5449
//! // SAFETY: `Q` is only accessible in this scope
@@ -59,28 +54,30 @@
5954
//! let (producer, mut consumer) = queue.split();
6055
//!
6156
//! // `producer` can be moved into `interrupt_handler` using a static mutex or the mechanism
62-
//! // provided by the concurrency framework you are using (e.g. a resource in RTIC)
57+
//! // provided by the concurrency framework you are using, e.g. a resource in RTIC.
58+
//! # let mut producer = producer;
59+
//! # interrupt_handler(&mut producer);
6360
//!
6461
//! loop {
6562
//! match consumer.dequeue() {
6663
//! Some(Event::A) => { /* .. */ }
6764
//! Some(Event::B) => { /* .. */ }
68-
//! None => { /* sleep */ }
65+
//! None => { /* Sleep. */ }
6966
//! }
7067
//! # break
7168
//! }
7269
//! }
7370
//!
74-
//! // this is a different execution context that can preempt `main`
71+
//! // This is a different execution context that can preempt `main`.
7572
//! fn interrupt_handler(producer: &mut Producer<'static, Event, 4>) {
7673
//! # let condition = true;
7774
//!
7875
//! // ..
7976
//!
8077
//! if condition {
81-
//! producer.enqueue(Event::A).ok().unwrap();
78+
//! producer.enqueue(Event::A).unwrap();
8279
//! } else {
83-
//! producer.enqueue(Event::B).ok().unwrap();
80+
//! producer.enqueue(Event::B).unwrap();
8481
//! }
8582
//!
8683
//! // ..
@@ -89,21 +86,20 @@
8986
//!
9087
//! # Benchmarks
9188
//!
92-
//! Measured on a ARM Cortex-M3 core running at 8 MHz and with zero Flash wait cycles
89+
//! Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with `-C opt-level=3`:
9390
//!
94-
//! `-C opt-level` |`3`|
95-
//! -----------------------|---|
96-
//! `Consumer<u8>::dequeue`| 15|
97-
//! `Queue<u8>::dequeue` | 12|
98-
//! `Producer<u8>::enqueue`| 16|
99-
//! `Queue<u8>::enqueue` | 14|
91+
//! Method | Time |
92+
//! ------------------------|-----:|
93+
//! `Producer<u8>::enqueue` | 16|
94+
//! `Queue<u8>::enqueue` | 14|
95+
//! `Consumer<u8>::dequeue` | 15|
96+
//! `Queue<u8>::dequeue` | 12|
10097
//!
10198
//! - All execution times are in clock cycles. 1 clock cycle = 125 ns.
102-
//! - Execution time is *dependent* of `mem::size_of::<T>()`. Both operations include one
103-
//! `memcpy(T)` in their successful path.
104-
//! - The optimization level is indicated in the first row.
105-
//! - The numbers reported correspond to the successful path (i.e. `Some` is returned by `dequeue`
106-
//! and `Ok` is returned by `enqueue`).
99+
//! - Execution time is *dependent* on `mem::size_of::<T>()`, as both operations include
100+
//! `ptr::read::<T>()` or `ptr::write::<T>()` in their successful path.
101+
//! - The numbers reported correspond to the successful path, i.e. `Some` is returned by `dequeue`
102+
//! and `Ok` is returned by `enqueue`.
107103
108104
use core::{borrow::Borrow, cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
109105

@@ -130,16 +126,15 @@ pub struct QueueInner<T, S: Storage> {
130126
pub(crate) buffer: S::Buffer<UnsafeCell<MaybeUninit<T>>>,
131127
}
132128

133-
/// A statically allocated single producer, single consumer queue with a capacity of `N - 1` elements.
129+
/// A statically allocated single-producer, single-consumer queue with a capacity of `N - 1` elements.
134130
///
135-
/// >
136131
/// <div class="warning">
137132
///
138133
/// To get better performance use a value for `N` that is a power of 2, e.g. 16, 32, etc.
139134
///
140135
/// </div>
141136
///
142-
/// You will likely want to use [`split`](QueueInner::split) to create a producer and consumer handle.
137+
/// You will likely want to use [`split`](QueueInner::split) to create a producer-consumer pair.
143138
pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
144139

145140
/// A [`Queue`] with dynamic capacity.

0 commit comments

Comments
 (0)