Module spsc

Module spsc 

Source
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:

MethodTime
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 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 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.
QueueInner
Base struct for Queue and QueueView, generic over the Storage.

Type Aliases§

Queue
A statically allocated single-producer, single-consumer queue with a capacity of N - 1 elements.
QueueView
A Queue with dynamic capacity.