#![cfg(loom)]
use loom::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use loom::sync::Arc;
use loom::thread;
const NIL: u32 = u32::MAX;
struct Seg {
fl_head: AtomicU32,
word: [AtomicU32; 2],
chan: AtomicU32,
handed: [AtomicBool; 2],
}
impl Seg {
fn new() -> Arc<Self> {
Arc::new(Seg {
fl_head: AtomicU32::new(NIL),
word: [AtomicU32::new(NIL), AtomicU32::new(NIL)],
chan: AtomicU32::new(NIL),
handed: [AtomicBool::new(false), AtomicBool::new(false)],
})
}
fn fl_push(&self, off: u32) {
assert!(
!self.handed[off as usize].load(Ordering::Acquire),
"I-BLOCK-1 VIOLATED: block {off} pushed to free list while LIVE (handed out)"
);
let old = self.fl_head.load(Ordering::Relaxed);
self.word[off as usize].store(old, Ordering::Relaxed);
self.fl_head.store(off, Ordering::Relaxed);
}
fn fl_pop(&self) -> u32 {
let h = self.fl_head.load(Ordering::Relaxed);
if h == NIL {
return NIL;
}
let next = self.word[h as usize].load(Ordering::Relaxed);
self.fl_head.store(next, Ordering::Relaxed);
self.handed[h as usize].store(true, Ordering::Release);
h
}
fn drain(&self) {
let off = self.chan.swap(NIL, Ordering::AcqRel);
if off != NIL {
self.fl_push(off);
}
}
fn remote_free(&self, off: u32) {
self.handed[off as usize].store(false, Ordering::Release);
let _ = self
.chan
.compare_exchange(NIL, off, Ordering::AcqRel, Ordering::Relaxed);
}
}
#[test]
fn protocol_single_owner_never_resurrects() {
let mut b = loom::model::Builder::new();
b.preemption_bound = Some(3);
b.check(|| {
let seg = Seg::new();
seg.handed[0].store(true, Ordering::Release);
let owner_seg = Arc::clone(&seg);
let owner = thread::spawn(move || {
for _ in 0..2 {
owner_seg.drain();
let got = owner_seg.fl_pop();
if got != NIL {
owner_seg.handed[got as usize].store(false, Ordering::Release);
owner_seg.fl_push(got);
}
}
});
let remote_seg = Arc::clone(&seg);
let remote = thread::spawn(move || {
remote_seg.remote_free(0);
});
owner.join().unwrap();
remote.join().unwrap();
});
}
#[test]
#[should_panic(expected = "I-BLOCK-1 VIOLATED")]
fn broken_publish_before_release_resurrects() {
let mut b = loom::model::Builder::new();
b.preemption_bound = Some(3);
b.check(|| {
let seg = Seg::new();
seg.handed[0].store(true, Ordering::Release);
let owner_seg = Arc::clone(&seg);
let owner = thread::spawn(move || {
for _ in 0..2 {
owner_seg.drain();
let _ = owner_seg.fl_pop();
}
});
let remote_seg = Arc::clone(&seg);
let remote = thread::spawn(move || {
let _ = remote_seg
.chan
.compare_exchange(NIL, 0, Ordering::AcqRel, Ordering::Relaxed);
remote_seg.handed[0].store(false, Ordering::Release);
});
owner.join().unwrap();
remote.join().unwrap();
});
}