Module mpmc

Module mpmc 

Source
Expand description

A fixed capacity multiple-producer, multiple-consumer (MPMC) lock-free queue.

§Deprecation

The current implementation of `mpmc` is marked as deprecated due to not being truly lock-free

If a thread is parked, or pre-empted for a long time by an higher-priority task during an enqueue or dequeue operation, it is possible that the queue ends-up in a state were no other task can successfully enqueue or dequeue items from it until the pre-empted task can finish its operation.

In that case, enqueue and dequeue will return an error, but will not panic or reach undefined behaviour

This makes mpmc unsuitable for some use cases such as using it as a pool of objects.

§When can this queue be used?

This queue should be used for cross-task communication only when items sent over the queue can be dropped in case of concurrent operations, or when it is possible to retry the dequeue/enqueue operation after other tasks have had the opportunity to make progress.

In that case you can safely ignore the warnings using #[expect(deprecated)] when new is called

For more information, and possible alternative, please see https://github.com/rust-embedded/heapless/issues/583

Note: This module requires atomic compare-and-swap (CAS) instructions. On targets where they’re not natively available, they are emulated by the portable-atomic crate.

§Example

This queue can be constructed in const context. Placing it in a static variable lets all contexts (interrupts/threads/main) safely enqueue and dequeue items.

use core::sync::atomic::{AtomicU8, Ordering};

use heapless::mpmc::Queue;

static Q: Queue<u8, 2> = Queue::new();

fn main() {
    // Configure systick interrupt.

    loop {
        if let Some(x) = Q.dequeue() {
            println!("{}", x);
        } else {
            // Wait for interrupt.
        }
    }
}

fn systick() {
    static COUNT: AtomicU8 = AtomicU8::new(0);
    let count = COUNT.fetch_add(1, Ordering::SeqCst);

    Q.enqueue(count);
}

§Benchmark

Measured on an ARM Cortex-M3 core running at 8 MHz and with zero flash wait cycles, compiled with -C opt-level=z:

MethodTimeN
Queue::<u8, 8>::enqueue()340
Queue::<u8, 8>::enqueue()521
Queue::<u8, 8>::enqueue()692
Queue::<u8, 8>::dequeue()350
Queue::<u8, 8>::dequeue()531
Queue::<u8, 8>::dequeue()712
  • N denotes the number of interruptions. On Cortex-M, an interruption consists of an interrupt handler preempting the would-be atomic section of the enqueue/dequeue operation. Note that it does not matter if the higher priority handler uses the queue or not.
  • 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 of Dmitry Vyukov’s bounded MPMC queue, minus the cache padding.

Structs§

QueueInner
Base struct for Queue and QueueView, generic over the Storage.

Type Aliases§

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