use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
use std::task::{Context, Poll, Waker};
#[doc(inline)]
pub use crate::error::{Disconnected, NoReceivers, TryWaitError};
const PENDING: u8 = 0;
const FIRED: u8 = 1;
const DISCONNECTED: u8 = 2;
#[derive(Clone, Copy)]
enum Terminal {
Fired,
Disconnected,
}
#[derive(Default)]
struct Registry {
next_key: u64,
wakers: HashMap<u64, Waker>,
}
struct Shared {
state: AtomicU8,
signalers: AtomicUsize,
waiters: AtomicUsize,
registry: Mutex<Registry>,
}
impl Shared {
fn new() -> Self {
Shared {
state: AtomicU8::new(PENDING),
signalers: AtomicUsize::new(1),
waiters: AtomicUsize::new(1),
registry: Mutex::new(Registry::default()),
}
}
#[inline]
fn signaler_count(&self) -> usize {
self.signalers.load(Ordering::Acquire)
}
#[inline]
fn waiter_count(&self) -> usize {
self.waiters.load(Ordering::Acquire)
}
#[inline]
fn load_terminal(&self) -> Option<Terminal> {
match self.state.load(Ordering::Acquire) {
FIRED => Some(Terminal::Fired),
DISCONNECTED => Some(Terminal::Disconnected),
_ => None,
}
}
fn signal(&self) -> Result<bool, NoReceivers> {
if self.state.load(Ordering::Acquire) == FIRED {
return Ok(false);
}
if self.waiters.load(Ordering::Acquire) == 0 {
return Err(NoReceivers);
}
match self
.state
.compare_exchange(PENDING, FIRED, Ordering::Release, Ordering::Relaxed)
{
Ok(_) => {
self.wake_all();
Ok(true)
}
Err(_) => Ok(false),
}
}
fn signaler_dropped(&self) {
if self.signalers.fetch_sub(1, Ordering::AcqRel) != 1 {
return;
}
if self
.state
.compare_exchange(PENDING, DISCONNECTED, Ordering::Release, Ordering::Relaxed)
.is_ok()
{
self.wake_all();
}
}
fn wake_all(&self) {
let wakers: Vec<Waker> = {
let mut reg = self.lock_registry();
reg.wakers.drain().map(|(_, w)| w).collect()
};
for w in wakers {
w.wake();
}
}
fn lock_registry(&self) -> MutexGuard<'_, Registry> {
match self.registry.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
}
pub struct Signaler {
shared: Arc<Shared>,
}
pub struct Waiter {
shared: Arc<Shared>,
}
pub fn create() -> (Signaler, Waiter) {
let shared = Arc::new(Shared::new());
(
Signaler {
shared: shared.clone(),
},
Waiter { shared },
)
}
impl Signaler {
pub fn signal(&self) -> Result<bool, NoReceivers> {
self.shared.signal()
}
pub fn is_signaled(&self) -> bool {
self.shared.state.load(Ordering::Acquire) == FIRED
}
pub fn signaler_count(&self) -> usize {
self.shared.signaler_count()
}
pub fn waiter_count(&self) -> usize {
self.shared.waiter_count()
}
}
impl Clone for Signaler {
fn clone(&self) -> Self {
self.shared.signalers.fetch_add(1, Ordering::Relaxed);
Signaler {
shared: self.shared.clone(),
}
}
}
impl Drop for Signaler {
fn drop(&mut self) {
self.shared.signaler_dropped();
}
}
impl Waiter {
pub fn wait(&self) -> Wait<'_> {
Wait {
waiter: self,
key: None,
}
}
pub fn try_wait(&self) -> Result<(), TryWaitError> {
match self.shared.load_terminal() {
Some(Terminal::Fired) => Ok(()),
Some(Terminal::Disconnected) => Err(TryWaitError::Disconnected),
None => Err(TryWaitError::Pending),
}
}
pub fn is_signaled(&self) -> bool {
self.shared.state.load(Ordering::Acquire) == FIRED
}
pub fn is_disconnected(&self) -> bool {
self.shared.state.load(Ordering::Acquire) == DISCONNECTED
}
pub fn signaler_count(&self) -> usize {
self.shared.signaler_count()
}
pub fn waiter_count(&self) -> usize {
self.shared.waiter_count()
}
}
impl Clone for Waiter {
fn clone(&self) -> Self {
self.shared.waiters.fetch_add(1, Ordering::Relaxed);
Waiter {
shared: self.shared.clone(),
}
}
}
impl Drop for Waiter {
fn drop(&mut self) {
self.shared.waiters.fetch_sub(1, Ordering::AcqRel);
}
}
pub struct Wait<'a> {
waiter: &'a Waiter,
key: Option<u64>,
}
impl Future for Wait<'_> {
type Output = Result<(), Disconnected>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut(); let shared = &this.waiter.shared;
match shared.load_terminal() {
Some(Terminal::Fired) => return Poll::Ready(Ok(())),
Some(Terminal::Disconnected) => return Poll::Ready(Err(Disconnected)),
None => {}
}
let mut reg = shared.lock_registry();
match shared.load_terminal() {
Some(Terminal::Fired) => return Poll::Ready(Ok(())),
Some(Terminal::Disconnected) => return Poll::Ready(Err(Disconnected)),
None => {}
}
let key = match this.key {
Some(k) => k,
None => {
let k = reg.next_key;
reg.next_key += 1;
this.key = Some(k);
k
}
};
reg.wakers.insert(key, cx.waker().clone());
Poll::Pending
}
}
impl Drop for Wait<'_> {
fn drop(&mut self) {
if let Some(key) = self.key {
let mut reg = self.waiter.shared.lock_registry();
reg.wakers.remove(&key);
}
}
}