Expand description
A fixed capacity single-producer, single-consumer (SPSC) lock-free queue.
Note: This module requires atomic load and store instructions. On
targets where they’re not natively available, they are emulated by the
portable-atomic
crate.
§Examples
Queue
can be used as a plain queue:
use heapless::spsc::Queue;
let mut queue: Queue<u8, 4> = Queue::new();
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::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.
use heapless::spsc::{Producer, Queue};
#[derive(Debug)]
enum Event {
A,
B,
}
fn main() {
let queue: &'static mut Queue<Event, 4> = {
static mut Q: Queue<Event, 4> = Queue::new();
// SAFETY: `Q` is only accessible in this scope
// and `main` is only called once.
unsafe { &mut Q }
};
let (producer, mut consumer) = queue.split();
// `producer` can be moved into `interrupt_handler` using a static mutex or the mechanism
// provided by the concurrency framework you are using, e.g. a resource in RTIC.
loop {
match consumer.dequeue() {
Some(Event::A) => { /* .. */ }
Some(Event::B) => { /* .. */ }
None => { /* Sleep. */ }
}
}
}
// This is a different execution context that can preempt `main`.
fn interrupt_handler(producer: &mut Producer<'static, Event>) {
// ..
if condition {
producer.enqueue(Event::A).unwrap();
} else {
producer.enqueue(Event::B).unwrap();
}
// ..
}
§Benchmarks
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 |
- All execution times are in clock cycles (1 clock cycle = 125 ns).
- Execution time is dependent on
mem::size_of::<T>()
, as both operations includeptr::read::<T>()
orptr::write::<T>()
in their successful path. - The numbers reported correspond to the successful path, i.e.
dequeue
returningSome
andenqueue
returningOk
.
§References
This is an implementation based on https://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular.
Structs§
- Consumer
- A consumer; it can dequeue items from the queue.
- Iter
- An iterator over the items of a queue.
- IterMut
- An iterator over the items of a queue.
- Producer
- A producer; it can enqueue items into the queue.
- Queue
Inner - Base struct for
Queue
andQueueView
, generic over theStorage
.