use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::fmt;
use std::pin::pin;
use tokio::sync::Notify;
#[derive(Clone)]
pub struct Shutdowner {
inner: Arc<Inner>,
}
struct Inner {
notify: Notify,
requested: AtomicBool,
from_failure: AtomicBool,
}
impl Shutdowner {
pub(crate) fn new() -> Self {
Self {
inner: Arc::new(Inner {
notify: Notify::new(),
requested: AtomicBool::new(false),
from_failure: AtomicBool::new(false),
}),
}
}
pub fn shutdown(&self) {
self.inner.requested.store(true, Ordering::Release);
self.inner.notify.notify_waiters();
}
pub(crate) fn fail(&self) {
self.inner.from_failure.store(true, Ordering::Release);
self.shutdown();
}
#[must_use]
pub(crate) fn is_failure(&self) -> bool {
self.inner.from_failure.load(Ordering::Acquire)
}
#[must_use]
pub fn is_requested(&self) -> bool {
self.inner.requested.load(Ordering::Acquire)
}
pub async fn wait(&self) {
loop {
let mut notified = pin!(self.inner.notify.notified());
notified.as_mut().enable();
if self.inner.requested.load(Ordering::Acquire) {
return;
}
notified.await;
}
}
}
impl fmt::Debug for Shutdowner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Shutdowner")
.field("requested", &self.inner.requested.load(Ordering::Acquire))
.field(
"from_failure",
&self.inner.from_failure.load(Ordering::Acquire),
)
.finish()
}
}