use crate::cell::UnsafeCell;
use crate::pin::Pin;
use crate::sync::atomic::{
fence, AtomicI8,
Ordering::{Acquire, Relaxed, Release},
};
use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId};
use crate::time::Duration;
pub struct Parker {
state: AtomicI8,
tid: UnsafeCell<Option<ThreadId>>,
}
const PARKED: i8 = -1;
const EMPTY: i8 = 0;
const NOTIFIED: i8 = 1;
impl Parker {
pub fn new() -> Parker {
Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) }
}
pub unsafe fn new_in_place(parker: *mut Parker) {
parker.write(Parker::new())
}
unsafe fn init_tid(&self) {
if self.tid.get().read().is_none() {
self.tid.get().write(Some(current()));
fence(Release);
}
}
pub unsafe fn park(self: Pin<&Self>) {
self.init_tid();
let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
if state == PARKED {
while state == PARKED {
park(self.state.as_ptr().addr());
state = self.state.load(Acquire);
}
self.state.store(EMPTY, Relaxed);
}
}
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
self.init_tid();
let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
if state == PARKED {
park_timeout(dur, self.state.as_ptr().addr());
self.state.swap(EMPTY, Acquire);
}
}
pub fn unpark(self: Pin<&Self>) {
let state = self.state.swap(NOTIFIED, Release);
if state == PARKED {
fence(Acquire);
let tid = unsafe { self.tid.get().read().unwrap_unchecked() };
unpark(tid, self.state.as_ptr().addr());
}
}
}
unsafe impl Send for Parker {}
unsafe impl Sync for Parker {}