Skip to content

Commit c4f9b25

Browse files
committed
mpmc: add loom test for enqueue contention
Adds a `loom` permutation test for `mpmmc::Queue::enqueue` contention across threads. Related: #583 Co-authored-by: NODA Kai <https://github.com/nodakai> Co-authored-by: Sosthène Guédon <https://github.com/sosthene-nitrokey>
1 parent dfeb3dd commit c4f9b25

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ stable_deref_trait = { version = "1", default-features = false }
7575
[dev-dependencies]
7676
critical-section = { version = "1.1", features = ["std"] }
7777
static_assertions = "1.1.0"
78+
loom = "0.7"
7879

7980
[package.metadata.docs.rs]
8081
features = [

src/mpmc.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,20 @@ unsafe fn enqueue<T>(
346346
mod tests {
347347
use static_assertions::assert_not_impl_any;
348348

349-
use super::Queue;
349+
use super::{Queue, QueueView};
350350

351351
// Ensure a `Queue` containing `!Send` values stays `!Send` itself.
352352
assert_not_impl_any!(Queue<*const (), 4>: Send);
353353

354+
fn to_vec<T>(q: &QueueView<T>) -> Vec<T> {
355+
// inaccurate
356+
let mut ret = vec![];
357+
while let Some(v) = q.dequeue() {
358+
ret.push(v);
359+
}
360+
ret
361+
}
362+
354363
#[test]
355364
fn memory_leak() {
356365
droppable!();
@@ -419,4 +428,83 @@ mod tests {
419428
// Queue is full, this should not block forever.
420429
q.enqueue(0x55).unwrap_err();
421430
}
431+
432+
#[test]
433+
fn issue_583_enqueue() {
434+
const N: usize = 4;
435+
436+
let q0 = Queue::<u8, N>::new();
437+
for i in 0..N {
438+
q0.enqueue(i as u8).expect("new enqueue");
439+
}
440+
eprintln!("start!");
441+
442+
std::thread::scope(|sc| {
443+
for _ in 0..2 {
444+
sc.spawn(|| {
445+
for k in 0..1000_000 {
446+
if let Some(v) = q0.dequeue() {
447+
q0.enqueue(v).unwrap_or_else(|v| {
448+
panic!("{k}: q0 -> q0: {v}, {:?}", to_vec(&q0))
449+
});
450+
}
451+
}
452+
});
453+
}
454+
});
455+
}
456+
457+
#[test]
458+
fn issue_583_dequeue() {
459+
const N: usize = 4;
460+
461+
let q0 = Queue::<u8, N>::new();
462+
eprintln!("start!");
463+
std::thread::scope(|sc| {
464+
for _ in 0..2 {
465+
sc.spawn(|| {
466+
for k in 0..1000_000 {
467+
q0.enqueue(k as u8).unwrap();
468+
if q0.dequeue().is_none() {
469+
panic!("{k}");
470+
}
471+
}
472+
});
473+
}
474+
});
475+
}
476+
477+
#[test]
478+
fn issue_583_enqueue_loom() {
479+
const N: usize = 4;
480+
481+
loom::model(|| {
482+
let q0 = loom::sync::Arc::new(Queue::<u8, N>::new());
483+
for i in 0..N {
484+
q0.enqueue(i as u8).expect("new enqueue");
485+
}
486+
eprintln!("start!");
487+
488+
let q1 = q0.clone();
489+
loom::thread::spawn(move || {
490+
for k in 0..1000_000 {
491+
if let Some(v) = q0.dequeue() {
492+
q0.enqueue(v).unwrap_or_else(|v| {
493+
panic!("{k}: q0 -> q0: {v}, {:?}", to_vec(&*q0))
494+
});
495+
}
496+
}
497+
});
498+
499+
loom::thread::spawn(move || {
500+
for k in 0..1000_000 {
501+
if let Some(v) = q1.dequeue() {
502+
q1.enqueue(v).unwrap_or_else(|v| {
503+
panic!("{k}: q0 -> q0: {v}, {:?}", to_vec(&*q1))
504+
});
505+
}
506+
}
507+
});
508+
});
509+
}
422510
}

0 commit comments

Comments
 (0)