use core::future::{Future, poll_fn};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::StartError;
use core::task::Poll;
use futures::task::AtomicWaker;
pub struct Lifecycle {
started: AtomicBool,
running: AtomicUsize,
shutdown_requested: AtomicBool,
waker: AtomicWaker,
}
impl Lifecycle {
pub const fn new() -> Self {
Self {
started: AtomicBool::new(false),
running: AtomicUsize::new(0),
shutdown_requested: AtomicBool::new(false),
waker: AtomicWaker::new(),
}
}
pub fn start(&self) -> core::result::Result<(), StartError> {
self.started
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.map(|_| ())
.map_err(|_| StartError::AlreadyStarted)
}
pub fn is_started(&self) -> bool {
self.started.load(Ordering::Acquire)
}
pub fn request_shutdown(&self) -> bool {
self.shutdown_requested
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
}
pub fn begin(&self) {
self.running.fetch_add(1, Ordering::AcqRel);
}
pub fn finish(&self) {
if self.running.fetch_sub(1, Ordering::AcqRel) == 1 {
self.waker.wake();
}
}
pub fn wait(&self) -> impl Future<Output = ()> + '_ {
poll_fn(|cx| {
if self.running.load(Ordering::Acquire) == 0 {
return Poll::Ready(());
}
self.waker.register(cx.waker());
if self.running.load(Ordering::Acquire) == 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
}
}
impl Default for Lifecycle {
fn default() -> Self {
Self::new()
}
}