use crate::loom::sync::atomic::{AtomicUsize, Ordering};
use crate::loom::sync::{Condvar, Mutex};
use crate::loom::thread;
const EMPTY: usize = 0;
const PARKED_CONDVAR: usize = 1;
const NOTIFIED: usize = 2;
#[repr(transparent)]
struct State(AtomicUsize);
impl State {
#[inline]
fn compare_exchange(&self, current: usize, new: usize) -> Result<usize, usize> {
self.0
.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst)
}
#[inline]
fn swap(&self, val: usize) -> usize {
self.0.swap(val, Ordering::SeqCst)
}
}
struct Inner {
state: State,
mutex: Mutex<()>,
condvar: Condvar,
}
pub struct Parker {
inner: Inner,
}
impl Parker {
pub(crate) fn new() -> Self {
Self {
inner: Inner {
state: State(AtomicUsize::new(EMPTY)),
mutex: Mutex::new(()),
condvar: Condvar::new(),
},
}
}
pub(crate) fn park(&self) {
self.inner.park()
}
pub(crate) fn unpark(&self) {
self.inner.unpark()
}
}
impl Inner {
fn park(&self) {
for _ in 0..3 {
if self.state.compare_exchange(NOTIFIED, EMPTY).is_ok() {
return;
}
thread::yield_now();
}
self.park_condvar();
}
fn park_condvar(&self) {
let mut m = self.mutex.lock().unwrap();
match self.state.compare_exchange(EMPTY, PARKED_CONDVAR) {
Ok(_) => {}
Err(NOTIFIED) => {
let old = self.state.swap(EMPTY);
debug_assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
return;
}
Err(actual) => panic!("inconsistent park state; actual = {}", actual),
}
loop {
m = self.condvar.wait(m).unwrap();
if self.state.compare_exchange(NOTIFIED, EMPTY).is_ok() {
return;
}
}
}
fn unpark(&self) {
match self.state.swap(NOTIFIED) {
EMPTY => {} NOTIFIED => {} PARKED_CONDVAR => self.unpark_condvar(),
actual => panic!("inconsistent state in unpark; actual = {}", actual),
}
}
fn unpark_condvar(&self) {
drop(self.mutex.lock());
self.condvar.notify_one()
}
}