use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use futures::Stream;
pub(crate) type Cancel = Arc<AtomicBool>;
pub(crate) fn new_cancel() -> Cancel {
Arc::new(AtomicBool::new(false))
}
pub(crate) fn as_cancel_flag(cancel: &Cancel) -> impl Fn() -> bool + '_ {
move || cancel.load(Ordering::Relaxed)
}
pub(crate) struct CancelOnDrop<S> {
inner: S,
cancel: Cancel,
}
impl<S> CancelOnDrop<S> {
pub(crate) fn new(inner: S, cancel: Cancel) -> Self {
Self { inner, cancel }
}
}
impl<S: Stream + Unpin> Stream for CancelOnDrop<S> {
type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.inner).poll_next(cx)
}
}
impl<S> Drop for CancelOnDrop<S> {
fn drop(&mut self) {
self.cancel.store(true, Ordering::Relaxed);
}
}
pub(crate) struct CancelGuard {
cancel: Cancel,
defused: bool,
}
impl CancelGuard {
pub(crate) fn new(cancel: Cancel) -> Self {
Self {
cancel,
defused: false,
}
}
pub(crate) fn defuse(&mut self) {
self.defused = true;
}
}
impl Drop for CancelGuard {
fn drop(&mut self) {
if !self.defused {
self.cancel.store(true, Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::stream;
use futures::StreamExt;
#[test]
fn as_cancel_flag_reads_the_live_value() {
let cancel = new_cancel();
let flag = as_cancel_flag(&cancel);
assert!(!flag());
cancel.store(true, Ordering::Relaxed);
assert!(flag());
}
#[tokio::test]
async fn dropping_the_wrapped_stream_sets_the_flag() {
let cancel = new_cancel();
let inner = stream::iter(vec![1, 2, 3]);
let wrapped = CancelOnDrop::new(inner, cancel.clone());
assert!(!cancel.load(Ordering::Relaxed));
drop(wrapped);
assert!(cancel.load(Ordering::Relaxed));
}
#[tokio::test]
async fn draining_the_wrapped_stream_still_yields_every_item() {
let cancel = new_cancel();
let inner = stream::iter(vec![1, 2, 3]);
let mut wrapped = CancelOnDrop::new(inner, cancel);
let mut items = Vec::new();
while let Some(item) = wrapped.next().await {
items.push(item);
}
assert_eq!(items, vec![1, 2, 3]);
}
#[test]
fn a_defused_guard_does_not_set_the_flag_on_drop() {
let cancel = new_cancel();
let mut guard = CancelGuard::new(cancel.clone());
guard.defuse();
drop(guard);
assert!(!cancel.load(Ordering::Relaxed));
}
#[test]
fn an_undefused_guard_sets_the_flag_on_drop() {
let cancel = new_cancel();
let guard = CancelGuard::new(cancel.clone());
drop(guard);
assert!(cancel.load(Ordering::Relaxed));
}
}