#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![forbid(unsafe_code)]
use std::{
sync::{Arc, Condvar, Mutex, PoisonError},
time::{Duration, Instant},
};
pub struct CondSync<T>(Arc<I<T>>);
struct I<T> {
mtx: Mutex<T>,
cvar: Condvar,
}
impl<T> CondSync<T> {
pub fn new(value: T) -> Self {
Self(Arc::new(I {
mtx: Mutex::new(value),
cvar: Condvar::new(),
}))
}
pub fn wait_until<F>(&self, condition: F) -> Result<Reason, PoisonedError>
where
F: Fn(&T) -> bool,
{
let mut mtx_guard = self.0.mtx.lock()?;
while !condition(&*mtx_guard) {
mtx_guard = self.0.cvar.wait(mtx_guard)?;
}
Ok(Reason::Condition)
}
pub fn wait_until_or_timeout<F>(
&self,
condition: F,
duration: Duration,
) -> Result<Reason, PoisonedError>
where
F: Fn(&T) -> bool,
{
let mut mtx_guard = self.0.mtx.lock()?;
let end = Instant::now() + duration;
while !condition(&*mtx_guard) {
let now = Instant::now();
match self.0.cvar.wait_timeout(mtx_guard, end - now) {
Ok((mtxg, wtr)) => {
if wtr.timed_out() {
return Ok(Reason::Timeout);
}
mtx_guard = mtxg;
}
Err(_) => return Err(PoisonedError),
}
}
Ok(Reason::Condition)
}
pub fn wait_timeout(&self, duration: Duration) -> Result<Reason, PoisonedError> {
let mtx_guard = self.0.mtx.lock()?;
let end = Instant::now() + duration;
Ok(self
.0
.cvar
.wait_timeout(mtx_guard, end - Instant::now())
.map(|(_, wtr)| {
if wtr.timed_out() {
Reason::Timeout
} else {
Reason::Notification
}
})?)
}
pub fn modify_and_notify<F>(&self, modify: F, other: Other) -> Result<(), PoisonedError>
where
F: Fn(&mut T),
{
let mut mtx_guard = self.0.mtx.lock()?;
modify(&mut *mtx_guard);
match other {
Other::One => self.0.cvar.notify_one(),
Other::All => self.0.cvar.notify_all(),
}
Ok(())
}
}
impl<T> Clone for CondSync<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T> CondSync<T>
where
T: Clone,
{
#[must_use]
pub fn clone_inner(&self) -> T {
self.0
.mtx
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
}
#[derive(Copy, Clone)]
pub enum Other {
One,
All,
}
#[derive(Copy, Clone)]
pub enum Reason {
Timeout,
Condition,
Notification,
}
impl Reason {
#[must_use]
pub fn is_timeout(&self) -> bool {
matches!(&self, Self::Timeout)
}
#[must_use]
pub fn is_condition(&self) -> bool {
matches!(&self, Self::Condition)
}
#[must_use]
pub fn is_notification(&self) -> bool {
matches!(&self, Self::Notification)
}
}
#[derive(Debug)]
pub struct PoisonedError;
impl<T> From<PoisonError<T>> for PoisonedError {
fn from(_e: PoisonError<T>) -> PoisonedError {
PoisonedError
}
}