use core::{
fmt, hint,
sync::atomic::{
AtomicUsize,
Ordering::{Acquire, Relaxed, Release},
},
};
#[cfg(feature = "std")]
use std::time::{Duration, Instant};
#[cfg(all(not(feature = "std"), not(feature = "atomic-wait")))]
compile_error!("`sync` requires `std` or `atomic-wait` feature for Condvar");
#[cfg(feature = "std")]
use crate::{lock::EmptyCondvar, macros, WaitTimeoutResult};
#[cfg(not(feature = "std"))]
use crate::{lock::EmptyCondvar, macros};
#[cfg(test)]
mod tests;
pub struct Latch {
stat: AtomicUsize,
cvar: EmptyCondvar,
}
impl Latch {
#[must_use]
#[inline]
pub const fn new(count: usize) -> Self {
Self {
stat: AtomicUsize::new(count),
cvar: EmptyCondvar::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)
}
pub fn wait(&self) {
if self.spin() {
return;
}
let mut m = self.cvar.monitor();
while self.stat.load(Acquire) != 0 {
m = self.cvar.wait(m);
}
}
#[cfg(feature = "std")]
pub fn wait_timeout(&self, dur: Duration) -> WaitTimeoutResult<()> {
if self.spin() {
return WaitTimeoutResult::Reached;
}
let start = Instant::now();
let mut m = self.cvar.monitor();
loop {
if self.stat.load(Acquire) == 0 {
break WaitTimeoutResult::Reached;
}
let timeout = match dur.checked_sub(start.elapsed()) {
Some(t) => t,
None => break WaitTimeoutResult::TimedOut(()),
};
m = self.cvar.wait_timeout(m, timeout);
}
}
fn spin(&self) -> bool {
macros::spin_try_wait!(self, s, true, s == 0);
}
#[cold]
fn done(&self) {
self.cvar.notify_all();
}
}
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()
}
}