use crate::prelude::*;
use crate::task::{self, Task};
use event_listener::Event;
use std::sync::atomic::{self, AtomicBool};
#[derive(Default)]
struct Inner {
event: Event,
flag: AtomicBool,
}
pub struct Canceler {
inner: Arc<Inner>,
_inherit: Option<Task<()>>,
}
impl Canceler {
pub fn new() -> Self {
Self { inner: default(), _inherit: None }
}
pub fn inherit(cancel: CancelSignal) -> Self {
let linked = Self::new();
Self {
inner: linked.inner.clone(),
_inherit: Some(task::start(async move {
cancel.listen().await;
linked.cancel();
})),
}
}
pub fn cancel(&self) {
if !self.inner.flag.swap(true, atomic::Ordering::AcqRel) {
self.inner.event.notify_relaxed(usize::MAX);
}
}
pub fn is_triggered(&self) -> bool {
self.inner.flag.load(atomic::Ordering::Relaxed)
}
pub fn signal(&self) -> CancelSignal {
CancelSignal { inner: Some(self.inner.clone()) }
}
}
impl Default for Canceler {
fn default() -> Self {
Self::new()
}
}
#[derive(Default)]
pub struct CancelSignal {
inner: Option<Arc<Inner>>,
}
impl CancelSignal {
pub async fn guard<F>(&self, future: F) -> Result<F::Output, Canceled>
where
F: Future,
{
let future = async { Ok(future.await) };
let signal = async {
self.listen().await;
Err(Canceled)
};
futures_lite::future::or(future, signal).await
}
pub fn is_triggered(&self) -> bool {
match &self.inner {
Some(inner) => inner.flag.load(atomic::Ordering::Acquire),
None => false,
}
}
pub async fn listen(&self) {
let inner = match &self.inner {
Some(inner) => inner,
None => return future::forever().await,
};
while !inner.flag.load(atomic::Ordering::Relaxed) {
let listener = inner.event.listen();
if self.is_triggered() {
return;
}
listener.await;
}
}
}
impl Clone for CancelSignal {
fn clone(&self) -> Self {
Self { inner: self.inner.clone() }
}
}
#[derive(Debug, Error)]
#[error("Canceled.")]
pub struct Canceled;