diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a68c31b51..c24f2f6ff9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/mpmc.rs b/src/mpmc.rs index f676c7c93a..ec36c84be2 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -1,5 +1,34 @@ //! 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`](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 +//! +//! +//! //! **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. @@ -121,7 +150,38 @@ pub type Queue = QueueInner>; pub type QueueView = QueueInner; impl Queue { + #[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 + /// + ///
+ /// 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`](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 + /// pub const fn new() -> Self { const { assert!(N > 1); @@ -186,6 +246,7 @@ impl QueueInner { /// /// ```rust /// # use heapless::mpmc::{Queue, QueueView}; + /// ##[expect(deprecated)] /// let mut queue: Queue = Queue::new(); /// let view: &mut QueueView = queue.as_mut_view(); /// ``` @@ -194,6 +255,7 @@ impl QueueInner { /// /// ```rust /// # use heapless::mpmc::{Queue, QueueView}; + /// ##[expect(deprecated)] /// let mut queue: Queue = Queue::new(); /// let view: &mut QueueView = &mut queue; /// ``` @@ -228,6 +290,7 @@ impl QueueInner { impl Default for Queue { fn default() -> Self { + #[allow(deprecated)] Self::new() } } @@ -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!()); @@ -365,6 +429,7 @@ mod tests { #[test] fn sanity() { + #[expect(deprecated)] let q = Queue::<_, 2>::new(); q.enqueue(0).unwrap(); q.enqueue(1).unwrap(); @@ -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()); @@ -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()); @@ -408,6 +475,7 @@ mod tests { #[cfg(feature = "mpmc_large")] const CAPACITY: usize = 256; + #[expect(deprecated)] let q: Queue = Queue::new(); assert_eq!(q.capacity(), CAPACITY); diff --git a/tests/tsan.rs b/tests/tsan.rs index 14391e2435..1f01cb013b 100644 --- a/tests/tsan.rs +++ b/tests/tsan.rs @@ -122,6 +122,7 @@ fn mpmc_contention() { const N: u32 = 64; + #[expect(deprecated)] static Q: Queue = Queue::new(); let (s, r) = mpsc::channel();