use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
struct Inner {
canceled: AtomicBool,
wakers: Mutex<Vec<Waker>>,
parent: Option<CancelSignal>,
}
#[derive(Clone)]
pub struct CancelSignal(Arc<Inner>);
impl CancelSignal {
#[must_use]
pub fn new() -> Self {
Self(Arc::new(Inner {
canceled: AtomicBool::new(false),
wakers: Mutex::new(Vec::new()),
parent: None,
}))
}
#[must_use]
pub fn linked(parent: &CancelSignal) -> Self {
Self(Arc::new(Inner {
canceled: AtomicBool::new(false),
wakers: Mutex::new(Vec::new()),
parent: Some(parent.clone()),
}))
}
pub fn cancel(&self) {
if !self.0.canceled.swap(true, Ordering::AcqRel) {
let wakers = {
let mut guard = self.0.wakers.lock().unwrap_or_else(|e| e.into_inner());
std::mem::take(&mut *guard)
};
for waker in wakers {
waker.wake();
}
}
}
#[must_use]
pub fn is_canceled(&self) -> bool {
self.0.canceled.load(Ordering::Acquire)
|| self.0.parent.as_ref().is_some_and(CancelSignal::is_canceled)
}
pub async fn cancelled(&self) {
Cancelled { signal: self }.await;
}
fn register(&self, waker: &Waker) {
self.0
.wakers
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(waker.clone());
if let Some(parent) = &self.0.parent {
parent.register(waker);
}
}
}
impl Default for CancelSignal {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for CancelSignal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CancelSignal")
.field("canceled", &self.is_canceled())
.field("linked", &self.0.parent.is_some())
.finish()
}
}
struct Cancelled<'a> {
signal: &'a CancelSignal,
}
impl Future for Cancelled<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if self.signal.is_canceled() {
return Poll::Ready(());
}
self.signal.register(cx.waker());
if self.signal.is_canceled() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}