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};
#[derive(Default)]
struct Shared {
aborted: AtomicBool,
wakers: Mutex<Vec<Waker>>,
}
#[derive(Clone, Default)]
pub struct AbortController {
shared: Arc<Shared>,
}
impl AbortController {
pub fn new() -> Self {
Self::default()
}
pub fn signal(&self) -> AbortSignal {
AbortSignal {
shared: self.shared.clone(),
}
}
pub fn abort(&self) {
if !self.shared.aborted.swap(true, Ordering::SeqCst) {
let wakers =
std::mem::take(&mut *self.shared.wakers.lock().unwrap_or_else(|e| e.into_inner()));
for waker in wakers {
waker.wake();
}
}
}
}
#[derive(Clone, Default)]
pub struct AbortSignal {
shared: Arc<Shared>,
}
impl std::fmt::Debug for AbortSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AbortSignal")
.field("aborted", &self.is_aborted())
.finish()
}
}
impl AbortSignal {
pub fn is_aborted(&self) -> bool {
self.shared.aborted.load(Ordering::SeqCst)
}
pub fn aborted(&self) -> Aborted {
Aborted {
signal: self.clone(),
}
}
fn poll_aborted(&self, cx: &mut Context<'_>) -> Poll<()> {
if self.is_aborted() {
return Poll::Ready(());
}
let mut wakers = self.shared.wakers.lock().unwrap_or_else(|e| e.into_inner());
if self.is_aborted() {
return Poll::Ready(());
}
if !wakers.iter().any(|w| w.will_wake(cx.waker())) {
wakers.push(cx.waker().clone());
}
Poll::Pending
}
}
pub struct Aborted {
signal: AbortSignal,
}
impl Future for Aborted {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
self.signal.poll_aborted(cx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn abort_wakes_a_waiting_task_and_is_idempotent() {
let controller = AbortController::new();
let signal = controller.signal();
let waiter = std::thread::spawn(move || futures_executor::block_on(signal.aborted()));
controller.abort();
controller.abort();
waiter.join().unwrap();
assert!(controller.signal().is_aborted());
}
#[test]
fn an_already_aborted_signal_resolves_immediately() {
let controller = AbortController::new();
controller.abort();
futures_executor::block_on(controller.signal().aborted());
}
}