#[cfg(feature = "std")]
use std::{
fmt,
future::Future,
hint,
pin::Pin,
sync::atomic::{
AtomicUsize,
Ordering::{Acquire, Relaxed, Release},
},
task::{Context, Poll},
};
#[cfg(not(feature = "std"))]
use core::{
fmt,
future::Future,
hint,
pin::Pin,
sync::atomic::{
AtomicUsize,
Ordering::{Acquire, Relaxed, Release},
},
task::{Context, Poll},
};
use crate::{lock::Mutex, macros, WaitTimeoutResult};
use self::waiters::Waiters;
#[cfg(test)]
mod tests;
mod waiters;
pub struct Latch {
stat: AtomicUsize,
lock: Mutex<Waiters>,
}
impl Latch {
#[cfg(not(latches_no_const_sync))]
#[must_use]
#[inline]
pub const fn new(count: usize) -> Self {
Self {
stat: AtomicUsize::new(count),
lock: Mutex::new(Waiters::new()),
}
}
#[cfg(latches_no_const_sync)]
#[must_use]
#[inline]
pub fn new(count: usize) -> Self {
Self {
stat: AtomicUsize::new(count),
lock: Mutex::new(Waiters::new()),
}
}
pub fn count_down(&self) {
macros::decrement!(self, 1);
}
pub fn arrive(&self, n: usize) {
if n == 0 {
return;
}
macros::decrement!(self, n);
}
#[must_use]
#[inline]
pub fn count(&self) -> usize {
self.stat.load(Acquire)
}
#[inline]
pub fn try_wait(&self) -> Result<(), usize> {
macros::once_try_wait!(self)
}
#[inline]
pub const fn wait(&self) -> LatchWait<'_> {
LatchWait {
id: None,
latch: self,
}
}
#[inline]
pub const fn watch<T>(&self, timer: T) -> LatchWatch<'_, T> {
LatchWatch {
id: None,
latch: self,
timer,
}
}
fn spin(&self) -> bool {
macros::spin_try_wait!(self, s, true, s == 0);
}
#[cold]
fn done(&self) {
Waiters::wake_all(&self.lock);
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct LatchWait<'a> {
id: Option<usize>,
latch: &'a Latch,
}
impl Future for LatchWait<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let Self { latch, id } = self.get_mut();
if latch.spin() {
Poll::Ready(())
} else {
let mut lock = latch.lock.lock();
if latch.stat.load(Acquire) == 0 {
Poll::Ready(())
} else {
lock.upsert(id, cx.waker());
Poll::Pending
}
}
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct LatchWatch<'a, T> {
id: Option<usize>,
latch: &'a Latch,
timer: T,
}
impl<T> LatchWatch<'_, T> {
#[must_use]
#[inline]
pub fn timer(self: Pin<&mut Self>) -> Pin<&mut T> {
unsafe {
let Self { timer, .. } = self.get_unchecked_mut();
Pin::new_unchecked(timer)
}
}
}
impl<T: Future> Future for LatchWatch<'_, T> {
type Output = WaitTimeoutResult<T::Output>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let Self { id, latch, timer } = unsafe { self.get_unchecked_mut() };
let timer = unsafe { Pin::new_unchecked(timer) };
if latch.spin() {
Poll::Ready(WaitTimeoutResult::Reached)
} else {
let out = timer.poll(cx);
let mut lock = latch.lock.lock();
if latch.stat.load(Acquire) == 0 {
Poll::Ready(WaitTimeoutResult::Reached)
} else {
match out {
Poll::Ready(t) => {
lock.remove(id);
Poll::Ready(WaitTimeoutResult::TimedOut(t))
}
Poll::Pending => {
lock.upsert(id, cx.waker());
Poll::Pending
}
}
}
}
}
}
impl fmt::Debug for Latch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Latch")
.field("count", &self.stat)
.finish_non_exhaustive()
}
}
impl fmt::Debug for LatchWait<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LatchWait").finish_non_exhaustive()
}
}
impl<T> fmt::Debug for LatchWatch<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LatchWatch").finish_non_exhaustive()
}
}