use core::sync::atomic::{AtomicBool, Ordering};
use crate::{Stop, StopReason};
#[derive(Debug)]
pub struct StopSource {
cancelled: AtomicBool,
}
impl StopSource {
#[inline]
pub const fn new() -> Self {
Self {
cancelled: AtomicBool::new(false),
}
}
#[inline]
pub const fn cancelled() -> Self {
Self {
cancelled: AtomicBool::new(true),
}
}
#[inline]
pub fn cancel(&self) {
self.cancelled.store(true, Ordering::Relaxed);
}
#[inline]
pub fn is_cancelled(&self) -> bool {
self.cancelled.load(Ordering::Relaxed)
}
#[inline]
pub fn as_ref(&self) -> StopRef<'_> {
StopRef {
cancelled: &self.cancelled,
}
}
#[inline]
#[deprecated(since = "0.1.0", note = "use as_ref() instead")]
pub fn token(&self) -> StopRef<'_> {
self.as_ref()
}
}
impl Default for StopSource {
fn default() -> Self {
Self::new()
}
}
impl Stop for StopSource {
#[inline]
fn check(&self) -> Result<(), StopReason> {
if self.cancelled.load(Ordering::Relaxed) {
Err(StopReason::Cancelled)
} else {
Ok(())
}
}
#[inline]
fn should_stop(&self) -> bool {
self.cancelled.load(Ordering::Relaxed)
}
}
#[derive(Debug, Clone, Copy)]
pub struct StopRef<'a> {
cancelled: &'a AtomicBool,
}
impl Stop for StopRef<'_> {
#[inline]
fn check(&self) -> Result<(), StopReason> {
if self.cancelled.load(Ordering::Relaxed) {
Err(StopReason::Cancelled)
} else {
Ok(())
}
}
#[inline]
fn should_stop(&self) -> bool {
self.cancelled.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stop_source_basic() {
let source = StopSource::new();
assert!(!source.is_cancelled());
assert!(!source.should_stop());
assert!(source.check().is_ok());
source.cancel();
assert!(source.is_cancelled());
assert!(source.should_stop());
assert_eq!(source.check(), Err(StopReason::Cancelled));
}
#[test]
fn stop_source_cancelled_constructor() {
let source = StopSource::cancelled();
assert!(source.is_cancelled());
assert!(source.should_stop());
}
#[test]
fn stop_ref_basic() {
let source = StopSource::new();
let stop = source.as_ref();
assert!(!stop.should_stop());
assert!(stop.check().is_ok());
source.cancel();
assert!(stop.should_stop());
assert_eq!(stop.check(), Err(StopReason::Cancelled));
}
#[test]
fn stop_ref_is_copy() {
let source = StopSource::new();
let r1 = source.as_ref();
let r2 = r1; let _ = r1; let _ = r2;
}
#[test]
fn stop_source_is_default() {
let source: StopSource = Default::default();
assert!(!source.is_cancelled());
}
#[test]
fn stop_source_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<StopSource>();
assert_send_sync::<StopRef<'_>>();
}
#[test]
fn cancel_is_idempotent() {
let source = StopSource::new();
source.cancel();
source.cancel();
source.cancel();
assert!(source.is_cancelled());
}
#[test]
fn const_construction() {
static SOURCE: StopSource = StopSource::new();
assert!(!SOURCE.is_cancelled());
}
}