#![cfg(loom)]
use loom::sync::Arc;
use loom::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use loom::thread;
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
#[allow(dead_code)]
type _Unused = AtomicBool;
const EMPTY: u8 = 0;
const READY: u8 = 1;
const TAKEN: u8 = 2;
struct Completion {
state: AtomicU8,
result: UnsafeCell<MaybeUninit<u32>>,
signal: AtomicBool,
}
unsafe impl Send for Completion {}
unsafe impl Sync for Completion {}
impl Completion {
fn new() -> Self {
Self {
state: AtomicU8::new(EMPTY),
result: UnsafeCell::new(MaybeUninit::uninit()),
signal: AtomicBool::new(false),
}
}
fn complete(&self, value: u32) {
unsafe {
(*self.result.get()).write(value);
}
self.state.store(READY, Ordering::Release);
self.signal.store(true, Ordering::Release);
}
fn try_take(&self) -> Option<u32> {
let _ = self.signal.load(Ordering::Acquire);
match self.state.load(Ordering::Acquire) {
READY => {
let v = unsafe { (*self.result.get()).assume_init_read() };
self.state.store(TAKEN, Ordering::Release);
Some(v)
}
_ => None,
}
}
}
impl Drop for Completion {
fn drop(&mut self) {
if self.state.load(Ordering::Relaxed) == READY {
unsafe {
(*self.result.get()).assume_init_drop();
}
}
}
}
#[test]
fn loom_spawn_completion_release_acquire() {
loom::model(|| {
let c = Arc::new(Completion::new());
let c2 = c.clone();
let producer = thread::spawn(move || {
c2.complete(0xdead_beef);
});
loop {
if let Some(v) = c.try_take() {
assert_eq!(v, 0xdead_beef);
break;
}
thread::yield_now();
}
producer.join().unwrap();
});
}
#[test]
fn loom_completion_drop_after_release_frees_payload() {
loom::model(|| {
let c = Arc::new(Completion::new());
let c2 = c.clone();
let producer = thread::spawn(move || {
c2.complete(99);
});
drop(c);
producer.join().unwrap();
});
}