use crate::store::error::{StorageError, StorageResult};
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::task::{Context, Poll, Waker};
pub struct Durable<'a, T>(pub(crate) &'a T);
#[derive(Default)]
pub struct CommitSignal {
generation: AtomicU64,
last_failed: AtomicBool,
waiters: Mutex<Vec<Waker>>,
}
impl CommitSignal {
pub(crate) fn generation(&self) -> u64 {
self.generation.load(Ordering::Acquire)
}
pub(crate) fn finished(&self, ok: bool) {
self.last_failed.store(!ok, Ordering::Release);
self.generation.fetch_add(1, Ordering::AcqRel);
let woken = std::mem::take(&mut *self.waiters.lock().unwrap());
for waker in woken {
waker.wake();
}
}
fn failed(&self) -> bool {
self.last_failed.load(Ordering::Acquire)
}
fn park(&self, cx: &Context<'_>) {
self.waiters.lock().unwrap().push(cx.waker().clone());
}
}
pub struct Commit {
signal: Arc<CommitSignal>,
start: u64,
}
impl Commit {
pub(crate) fn awaiting(signal: Arc<CommitSignal>) -> Self {
let start = signal.generation();
Self { signal, start }
}
}
impl Future for Commit {
type Output = StorageResult<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let signal = &self.signal;
if signal.generation() > self.start {
return Poll::Ready(outcome(signal));
}
signal.park(cx);
if signal.generation() > self.start {
Poll::Ready(outcome(signal))
} else {
Poll::Pending
}
}
}
fn outcome(signal: &CommitSignal) -> StorageResult<()> {
if signal.failed() {
Err(StorageError::CommitFailed)
} else {
Ok(())
}
}