use super::{Instant, ServerError, failure};
use std::sync::{Condvar, Mutex};
#[derive(Debug, Default)]
struct State {
stopping: bool,
ready: bool,
}
#[derive(Debug, Default)]
pub(super) struct Wake {
state: Mutex<State>,
changed: Condvar,
}
impl Wake {
pub(super) fn stopping(&self) -> Result<bool, ServerError> {
Ok(self
.state
.lock()
.map_err(|_| failure("configured retry wake poisoned"))?
.stopping)
}
pub(super) fn stop(&self) -> Result<(), ServerError> {
let stopped = self
.state
.lock()
.map(|mut state| state.stopping = true)
.map_err(|_| failure("configured retry stop poisoned"));
self.changed.notify_all();
stopped
}
pub(super) fn notify(&self) {
if let Ok(mut state) = self.state.lock() {
state.ready = true;
drop(state);
self.changed.notify_all();
} else {
self.changed.notify_all();
tracing::error!("configured retry notification poisoned");
}
}
pub(super) fn wait(&self, deadline: Option<Instant>) -> Result<(), ServerError> {
let mut state = self
.state
.lock()
.map_err(|_| failure("configured retry wait poisoned"))?;
while !state.ready && !state.stopping {
if let Some(deadline) = deadline {
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
break;
}
let (next, timeout) = self
.changed
.wait_timeout(state, remaining)
.map_err(|_| failure("configured retry timed wait poisoned"))?;
state = next;
if timeout.timed_out() {
break;
}
} else {
state = self
.changed
.wait(state)
.map_err(|_| failure("configured retry wait poisoned"))?;
}
}
state.ready = false;
drop(state);
Ok(())
}
}