use crate::deadline::Deadline;
use crate::remedy;
use std::ops::{Deref};
use std::sync::{Condvar, Mutex, MutexGuard};
use std::time::Duration;
use crate::remedy::Remedy;
#[derive(Default, Debug)]
pub struct Completable<T> {
cond: Condvar,
data: Mutex<Option<T>>,
}
#[derive(Debug)]
pub struct Completed<'a, T> {
guard: MutexGuard<'a, Option<T>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Outcome<T> {
Abort,
Success(T)
}
impl<T> Outcome<T> {
#[inline]
pub fn is_abort(&self) -> bool {
matches!(self, Outcome::Abort)
}
#[inline]
pub fn is_success(&self) -> bool {
matches!(self, Outcome::Success(_))
}
#[inline]
pub fn into_option(self) -> Option<T> {
match self {
Outcome::Abort => None,
Outcome::Success(val) => Some(val)
}
}
}
impl<T> Default for Outcome<T> {
#[inline]
fn default() -> Self {
Outcome::Abort
}
}
impl<T> Deref for Completed<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.guard.as_ref().unwrap()
}
}
impl<T> Completable<T> {
#[inline]
pub fn new(val: T) -> Self {
Self {
cond: Condvar::default(),
data: Mutex::new(Some(val))
}
}
#[inline]
pub fn complete_exclusive(&self, f: impl FnOnce() -> T) -> bool {
let mut data = self.data.lock().remedy();
if data.is_none() {
*data = Some(f());
drop(data);
self.cond.notify_all();
true
} else {
false
}
}
#[inline]
pub fn complete(&self, val: T) -> bool {
self.complete_exclusive(|| val)
}
#[inline]
pub fn is_complete(&self) -> bool {
self.data.lock().remedy().is_some()
}
#[inline]
pub fn get(&self) -> Completed<T> {
Completed {
guard: self.__try_get(Duration::MAX),
}
}
#[inline]
pub fn peek<'a>(&'a self) -> impl Deref<Target = Option<T>> + 'a {
self.__try_get(Duration::ZERO)
}
#[inline]
pub fn try_get<'a>(&'a self, duration: Duration) -> impl Deref<Target = Option<T>> + 'a {
self.__try_get(duration)
}
#[inline]
fn __try_get(&self, duration: Duration) -> MutexGuard<Option<T>> {
let mut deadline = Deadline::lazy_after(duration);
let mut data = self.data.lock().remedy();
while data.is_none() {
let (guard, timed_out) =
remedy::cond_wait_remedy(&self.cond, data, deadline.remaining());
if timed_out {
return guard;
}
data = guard;
}
data
}
pub fn into_inner(self) -> Option<T> {
self.data.into_inner().remedy()
}
}
#[cfg(test)]
mod tests;