use std::time::{Duration, Instant};
use crate::sync::{Condvar, Mutex};
#[derive(Debug)]
pub struct Relay<T> {
mutex: Mutex<Option<T>>,
condvar: Condvar,
}
impl<T> Relay<T> {
#[must_use]
#[cfg(not(loom))]
pub const fn new() -> Self {
Self {
mutex: Mutex::new(None),
condvar: Condvar::new(),
}
}
#[must_use]
#[cfg(loom)]
pub fn new() -> Self {
Self {
mutex: Mutex::new(None),
condvar: Condvar::new(),
}
}
#[must_use]
#[cfg(not(loom))]
pub const fn with_value(value: T) -> Self {
Self {
mutex: Mutex::new(Some(value)),
condvar: Condvar::new(),
}
}
#[must_use]
#[cfg(loom)]
pub fn with_value(value: T) -> Self {
Self {
mutex: Mutex::new(Some(value)),
condvar: Condvar::new(),
}
}
}
impl<T> Default for Relay<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Relay<T> {
pub fn replace_notify_one(&self, value: T) -> Option<T> {
let mut guard = self.mutex.lock().expect("not poisoned");
let replaced = guard.replace(value);
drop(guard);
if replaced.is_none() {
self.condvar.notify_one();
}
replaced
}
pub fn replace_notify_all(&self, value: T) -> Option<T> {
let mut guard = self.mutex.lock().expect("not poisoned");
let replaced = guard.replace(value);
drop(guard);
if replaced.is_none() {
self.condvar.notify_all();
}
replaced
}
pub fn take(&self) -> Option<T> {
let mut guard = self.mutex.lock().expect("not poisoned");
guard.take()
}
pub fn wait(&self) -> T {
let mut guard = self.mutex.lock().expect("not poisoned");
loop {
if let Some(value) = guard.take() {
return value;
}
guard = self.condvar.wait(guard).expect("not poisoned");
}
}
pub fn wait_for(&self, timeout: Duration) -> Option<T> {
if timeout.is_zero() {
return self.take();
}
if let Some(deadline) = Instant::now().checked_add(timeout) {
self.wait_until(deadline)
} else {
Some(self.wait())
}
}
pub fn wait_until(&self, deadline: Instant) -> Option<T> {
let mut guard = self.mutex.lock().expect("not poisoned");
while guard.is_none() {
let now = Instant::now();
if now >= deadline {
break;
}
let timeout = deadline.duration_since(now);
let (replaced_guard, wait_result) = self
.condvar
.wait_timeout(guard, timeout)
.expect("not poisoned");
guard = replaced_guard;
if wait_result.timed_out() {
break;
}
}
guard.take()
}
}
#[cfg(test)]
mod tests;