Skip to content

Commit dfe1441

Browse files
authored
Merge pull request #624 from sgued/mpmc-issue-documentation
Document #583 and deprecate `mpmc`
2 parents 38c004e + 73bfcf4 commit dfe1441

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2222
- Added `FusedIterator` to `vec::IntoIter`, `deque::IntoIter`, `index_map::IntoIter` and `linear_map::IntoIter`.
2323
- Added `ExactSizeIterator` to `vec::IntoIter`, `deque::IntoIter`, `index_map::IntoIter` and `linear_map::IntoIter`.
2424
- Added `DoubleEndedIterator` to `vec::IntoIter` and `deque::IntoIter`.
25+
- Deprecate `mpmc` (see [#583](https://github.com/rust-embedded/heapless/issues/583#issuecomment-3469297720))
2526

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

src/mpmc.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
//! A fixed capacity multiple-producer, multiple-consumer (MPMC) lock-free queue.
22
//!
3+
//! # Deprecation
4+
//!
5+
//! <div class="warning">
6+
//! The current implementation of `mpmc` is marked as deprecated due to not being truly lock-free
7+
//! </div>
8+
//!
9+
//! If a thread is parked, or pre-empted for a long time by an higher-priority task
10+
//! during an `enqueue` or `dequeue` operation, it is possible that the queue ends-up
11+
//! in a state were no other task can successfully enqueue or dequeue items from it
12+
//! until the pre-empted task can finish its operation.
13+
//!
14+
//! In that case, [`enqueue`](QueueInner::dequeue) and [`dequeue`](QueueInner::enqueue)
15+
//! will return an error, but will not panic or reach undefined behaviour
16+
//!
17+
//! This makes `mpmc` unsuitable for some use cases such as using it as a pool of objects.
18+
//!
19+
//! ## When can this queue be used?
20+
//!
21+
//! This queue should be used for cross-task communication only when items sent over the queue
22+
//! can be dropped in case of concurrent operations, or when it is possible to retry
23+
//! the dequeue/enqueue operation after other tasks have had the opportunity to make progress.
24+
//!
25+
//! In that case you can safely ignore the warnings using `#[expect(deprecated)]`
26+
//! when `new` is called
27+
//!
28+
//! For more information, and possible alternative, please see
29+
//! <https://github.com/rust-embedded/heapless/issues/583>
30+
//!
31+
//!
332
//! **Note:** This module requires atomic compare-and-swap (CAS) instructions. On
433
//! targets where they're not natively available, they are emulated by the
534
//! [`portable-atomic`](https://crates.io/crates/portable-atomic) crate.
@@ -121,7 +150,38 @@ pub type Queue<T, const N: usize> = QueueInner<T, OwnedStorage<N>>;
121150
pub type QueueView<T> = QueueInner<T, ViewStorage>;
122151

123152
impl<T, const N: usize> Queue<T, N> {
153+
#[deprecated(
154+
note = "See the documentation of Queue::new() for more information: https://docs.rs/heapless/latest/heapless/mpmc/type.Queue.html#method.new"
155+
)]
124156
/// Creates an empty queue.
157+
///
158+
/// # Deprecation
159+
///
160+
/// <div class="warning">
161+
/// The current implementation of `mpmc` is marked as deprecated due to not being truly lock-free
162+
/// </div>
163+
///
164+
/// If a thread is parked, or pre-empted for a long time by an higher-priority task
165+
/// during an `enqueue` or `dequeue` operation, it is possible that the queue ends-up
166+
/// in a state were no other task can successfully enqueue or dequeue items from it
167+
/// until the pre-empted task can finish its operation.
168+
///
169+
/// In that case, [`enqueue`](QueueInner::dequeue) and [`dequeue`](QueueInner::enqueue)
170+
/// will return an error, but will not panic or reach undefined behaviour
171+
///
172+
/// This makes `mpmc` unsuitable for some use cases such as using it as a pool of objects.
173+
///
174+
/// ## When can this queue be used?
175+
///
176+
/// This queue should be used for cross-task communication only when items sent over the queue
177+
/// can be dropped in case of concurrent operations, or when it is possible to retry
178+
/// the dequeue/enqueue operation after other tasks have had the opportunity to make progress.
179+
///
180+
/// In that case you can safely ignore the warnings using `#[expect(deprecated)]`
181+
/// when `new` is called
182+
///
183+
/// For more information, and possible alternative, please see
184+
/// <https://github.com/rust-embedded/heapless/issues/583>
125185
pub const fn new() -> Self {
126186
const {
127187
assert!(N > 1);
@@ -186,6 +246,7 @@ impl<T, S: Storage> QueueInner<T, S> {
186246
///
187247
/// ```rust
188248
/// # use heapless::mpmc::{Queue, QueueView};
249+
/// ##[expect(deprecated)]
189250
/// let mut queue: Queue<u8, 2> = Queue::new();
190251
/// let view: &mut QueueView<u8> = queue.as_mut_view();
191252
/// ```
@@ -194,6 +255,7 @@ impl<T, S: Storage> QueueInner<T, S> {
194255
///
195256
/// ```rust
196257
/// # use heapless::mpmc::{Queue, QueueView};
258+
/// ##[expect(deprecated)]
197259
/// let mut queue: Queue<u8, 2> = Queue::new();
198260
/// let view: &mut QueueView<u8> = &mut queue;
199261
/// ```
@@ -228,6 +290,7 @@ impl<T, S: Storage> QueueInner<T, S> {
228290

229291
impl<T, const N: usize> Default for Queue<T, N> {
230292
fn default() -> Self {
293+
#[allow(deprecated)]
231294
Self::new()
232295
}
233296
}
@@ -355,6 +418,7 @@ mod tests {
355418
fn memory_leak() {
356419
droppable!();
357420

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

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

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

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

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

413481
assert_eq!(q.capacity(), CAPACITY);

tests/tsan.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ fn mpmc_contention() {
122122

123123
const N: u32 = 64;
124124

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

127128
let (s, r) = mpsc::channel();

0 commit comments

Comments
 (0)