use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
use crate::{BoxedStop, Stop, StopReason};
struct TreeInner {
self_cancelled: AtomicBool,
parent: Option<BoxedStop>,
}
impl core::fmt::Debug for TreeInner {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TreeInner")
.field("self_cancelled", &self.self_cancelled)
.field("parent", &self.parent.as_ref().map(|_| "<BoxedStop>"))
.finish()
}
}
#[derive(Debug, Clone)]
pub struct ChildStopper {
inner: Arc<TreeInner>,
}
impl ChildStopper {
#[inline]
pub fn new() -> Self {
Self {
inner: Arc::new(TreeInner {
self_cancelled: AtomicBool::new(false),
parent: None,
}),
}
}
#[inline]
pub fn with_parent<T: Stop + 'static>(parent: T) -> Self {
Self {
inner: Arc::new(TreeInner {
self_cancelled: AtomicBool::new(false),
parent: Some(BoxedStop::new(parent)),
}),
}
}
#[inline]
pub fn child(&self) -> ChildStopper {
ChildStopper::with_parent(self.clone())
}
#[inline]
pub fn cancel(&self) {
self.inner.self_cancelled.store(true, Ordering::Relaxed);
}
#[inline]
pub fn is_cancelled(&self) -> bool {
if self.inner.self_cancelled.load(Ordering::Relaxed) {
return true;
}
if let Some(ref parent) = self.inner.parent {
parent.should_stop()
} else {
false
}
}
}
impl Default for ChildStopper {
fn default() -> Self {
Self::new()
}
}
impl Stop for ChildStopper {
#[inline]
fn check(&self) -> Result<(), StopReason> {
if self.inner.self_cancelled.load(Ordering::Relaxed) {
return Err(StopReason::Cancelled);
}
if let Some(ref parent) = self.inner.parent {
parent.check()
} else {
Ok(())
}
}
#[inline]
fn should_stop(&self) -> bool {
self.is_cancelled()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Stopper;
#[test]
fn tree_root_basic() {
let root = ChildStopper::new();
assert!(!root.is_cancelled());
assert!(!root.should_stop());
assert!(root.check().is_ok());
root.cancel();
assert!(root.is_cancelled());
assert!(root.should_stop());
assert_eq!(root.check(), Err(StopReason::Cancelled));
}
#[test]
fn tree_child_inherits_parent() {
let parent = ChildStopper::new();
let child = parent.child();
assert!(!child.is_cancelled());
parent.cancel();
assert!(child.is_cancelled());
}
#[test]
fn tree_child_cancel_independent() {
let parent = ChildStopper::new();
let child = parent.child();
child.cancel();
assert!(child.is_cancelled());
assert!(!parent.is_cancelled());
}
#[test]
fn tree_siblings_independent() {
let parent = ChildStopper::new();
let child_a = parent.child();
let child_b = parent.child();
child_a.cancel();
assert!(child_a.is_cancelled());
assert!(!child_b.is_cancelled());
parent.cancel();
assert!(child_b.is_cancelled());
}
#[test]
fn tree_grandchild() {
let grandparent = ChildStopper::new();
let parent = grandparent.child();
let child = parent.child();
assert!(!child.is_cancelled());
grandparent.cancel();
assert!(child.is_cancelled());
}
#[test]
fn tree_three_generations() {
let g1 = ChildStopper::new();
let g2 = g1.child();
let g3 = g2.child();
assert!(!g3.is_cancelled());
g2.cancel();
assert!(!g1.is_cancelled());
assert!(g2.is_cancelled());
assert!(g3.is_cancelled());
}
#[test]
fn tree_with_stopper_parent() {
let root = Stopper::new();
let child = ChildStopper::with_parent(root.clone());
assert!(!child.is_cancelled());
root.cancel();
assert!(child.is_cancelled());
}
#[test]
fn tree_clone_shares_state() {
let t1 = ChildStopper::new();
let t2 = t1.clone();
t2.cancel();
assert!(t1.is_cancelled());
assert!(t2.is_cancelled());
}
#[test]
fn tree_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ChildStopper>();
}
#[test]
fn tree_is_default() {
let t: ChildStopper = Default::default();
assert!(!t.is_cancelled());
}
}