1
- //! # A fixed capacity Single Producer Single Consumer (SPSC) queue.
1
+ //! A fixed capacity single-producer, single-consumer (SPSC) queue.
2
2
//!
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>.
4
4
//!
5
- //! ## Portability
5
+ //! # Portability
6
6
//!
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
9
9
//! emulated however with [`portable-atomic`](https://crates.io/crates/portable-atomic), which is
10
10
//! enabled with the `cas` feature and is enabled by default for `thumbv6m-none-eabi` and `riscv32`
11
11
//! targets.
12
12
//!
13
- //! ## Examples
13
+ //! # Examples
14
14
//!
15
- //! - [ Queue] can be used as a plain queue
15
+ //! [` Queue` ] can be used as a plain queue.
16
16
//!
17
17
//! ```
18
18
//! use heapless::spsc::Queue;
19
19
//!
20
- //! let mut rb : Queue<u8, 4> = Queue::new();
20
+ //! let mut queue : Queue<u8, 4> = Queue::new();
21
21
//!
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.
26
26
//!
27
- //! assert_eq!(rb .dequeue(), Some(0));
27
+ //! assert_eq!(queue .dequeue(), Some(0));
28
28
//! ```
29
29
//!
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.
31
31
//!
32
32
//! "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.
40
36
//!
41
37
//! ```
42
38
//! use heapless::spsc::{Producer, Queue};
43
39
//!
40
+ //! #[derive(Debug)]
44
41
//! enum Event {
45
42
//! A,
46
43
//! B,
47
44
//! }
48
45
//!
49
46
//! fn main() {
50
- //! // Alternatively, use something like `static_cell` to create the `Queue` in the global
51
- //! // scope.
52
47
//! let queue: &'static mut Queue<Event, 4> = {
53
48
//! static mut Q: Queue<Event, 4> = Queue::new();
54
49
//! // SAFETY: `Q` is only accessible in this scope
59
54
//! let (producer, mut consumer) = queue.split();
60
55
//!
61
56
//! // `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);
63
60
//!
64
61
//! loop {
65
62
//! match consumer.dequeue() {
66
63
//! Some(Event::A) => { /* .. */ }
67
64
//! Some(Event::B) => { /* .. */ }
68
- //! None => { /* sleep */ }
65
+ //! None => { /* Sleep. */ }
69
66
//! }
70
67
//! # break
71
68
//! }
72
69
//! }
73
70
//!
74
- //! // this is a different execution context that can preempt `main`
71
+ //! // This is a different execution context that can preempt `main`.
75
72
//! fn interrupt_handler(producer: &mut Producer<'static, Event, 4>) {
76
73
//! # let condition = true;
77
74
//!
78
75
//! // ..
79
76
//!
80
77
//! if condition {
81
- //! producer.enqueue(Event::A).ok(). unwrap();
78
+ //! producer.enqueue(Event::A).unwrap();
82
79
//! } else {
83
- //! producer.enqueue(Event::B).ok(). unwrap();
80
+ //! producer.enqueue(Event::B).unwrap();
84
81
//! }
85
82
//!
86
83
//! // ..
89
86
//!
90
87
//! # Benchmarks
91
88
//!
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`:
93
90
//!
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 |
100
97
//!
101
98
//! - 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`.
107
103
108
104
use core:: { borrow:: Borrow , cell:: UnsafeCell , fmt, hash, mem:: MaybeUninit , ptr} ;
109
105
@@ -130,16 +126,15 @@ pub struct QueueInner<T, S: Storage> {
130
126
pub ( crate ) buffer : S :: Buffer < UnsafeCell < MaybeUninit < T > > > ,
131
127
}
132
128
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.
134
130
///
135
- /// >
136
131
/// <div class="warning">
137
132
///
138
133
/// To get better performance use a value for `N` that is a power of 2, e.g. 16, 32, etc.
139
134
///
140
135
/// </div>
141
136
///
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 .
143
138
pub type Queue < T , const N : usize > = QueueInner < T , OwnedStorage < N > > ;
144
139
145
140
/// A [`Queue`] with dynamic capacity.
0 commit comments