Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `FusedIterator` to `vec::IntoIter`, `deque::IntoIter`, `index_map::IntoIter` and `linear_map::IntoIter`.
- Added `ExactSizeIterator` to `vec::IntoIter`, `deque::IntoIter`, `index_map::IntoIter` and `linear_map::IntoIter`.
- Added `DoubleEndedIterator` to `vec::IntoIter` and `deque::IntoIter`.
- Deprecate `mpmc` (see [#583](https://github.com/rust-embedded/heapless/issues/583#issuecomment-3469297720))

## [v0.9.1] - 2025-08-19

Expand Down
68 changes: 68 additions & 0 deletions src/mpmc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
//! A fixed capacity multiple-producer, multiple-consumer (MPMC) lock-free queue.
//!
//! # Deprecation
//!
//! <div class="warning">
//! The current implementation of `mpmc` is marked as deprecated due to not being truly lock-free
//! </div>
//!
//! 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`](QueueInner::dequeue) and [`dequeue`](QueueInner::enqueue)
//! 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`](https://crates.io/crates/portable-atomic) crate.
Expand Down Expand Up @@ -121,7 +150,38 @@ pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
pub type QueueView<T> = QueueInner<T, ViewStorage>;

impl<T, const N: usize> Queue<T, N> {
#[deprecated(
note = "See the documentation of Queue::new() for more information: https://docs.rs/heapless/latest/heapless/mpmc/type.Queue.html#method.new"
)]
/// Creates an empty queue.
///
/// # Deprecation
///
/// <div class="warning">
/// The current implementation of `mpmc` is marked as deprecated due to not being truly lock-free
/// </div>
///
/// 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`](QueueInner::dequeue) and [`dequeue`](QueueInner::enqueue)
/// 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>
pub const fn new() -> Self {
const {
assert!(N > 1);
Expand Down Expand Up @@ -186,6 +246,7 @@ impl<T, S: Storage> QueueInner<T, S> {
///
/// ```rust
/// # use heapless::mpmc::{Queue, QueueView};
/// ##[expect(deprecated)]
/// let mut queue: Queue<u8, 2> = Queue::new();
/// let view: &mut QueueView<u8> = queue.as_mut_view();
/// ```
Expand All @@ -194,6 +255,7 @@ impl<T, S: Storage> QueueInner<T, S> {
///
/// ```rust
/// # use heapless::mpmc::{Queue, QueueView};
/// ##[expect(deprecated)]
/// let mut queue: Queue<u8, 2> = Queue::new();
/// let view: &mut QueueView<u8> = &mut queue;
/// ```
Expand Down Expand Up @@ -228,6 +290,7 @@ impl<T, S: Storage> QueueInner<T, S> {

impl<T, const N: usize> Default for Queue<T, N> {
fn default() -> Self {
#[allow(deprecated)]
Self::new()
}
}
Expand Down Expand Up @@ -355,6 +418,7 @@ mod tests {
fn memory_leak() {
droppable!();

#[expect(deprecated)]
let q = Queue::<_, 2>::new();
q.enqueue(Droppable::new()).unwrap_or_else(|_| panic!());
q.enqueue(Droppable::new()).unwrap_or_else(|_| panic!());
Expand All @@ -365,6 +429,7 @@ mod tests {

#[test]
fn sanity() {
#[expect(deprecated)]
let q = Queue::<_, 2>::new();
q.enqueue(0).unwrap();
q.enqueue(1).unwrap();
Expand All @@ -377,6 +442,7 @@ mod tests {

#[test]
fn drain_at_pos255() {
#[expect(deprecated)]
let q = Queue::<_, 2>::new();
for _ in 0..255 {
assert!(q.enqueue(0).is_ok());
Expand All @@ -389,6 +455,7 @@ mod tests {

#[test]
fn full_at_wrapped_pos0() {
#[expect(deprecated)]
let q = Queue::<_, 2>::new();
for _ in 0..254 {
assert!(q.enqueue(0).is_ok());
Expand All @@ -408,6 +475,7 @@ mod tests {
#[cfg(feature = "mpmc_large")]
const CAPACITY: usize = 256;

#[expect(deprecated)]
let q: Queue<u8, CAPACITY> = Queue::new();

assert_eq!(q.capacity(), CAPACITY);
Expand Down
1 change: 1 addition & 0 deletions tests/tsan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ fn mpmc_contention() {

const N: u32 = 64;

#[expect(deprecated)]
static Q: Queue<u32, 64> = Queue::new();

let (s, r) = mpsc::channel();
Expand Down