use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
use crate::{Stop, StopReason};
pub(crate) struct SyncStopperInner {
cancelled: AtomicBool,
}
impl Stop for SyncStopperInner {
#[inline]
fn check(&self) -> Result<(), StopReason> {
if self.cancelled.load(Ordering::Acquire) {
Err(StopReason::Cancelled)
} else {
Ok(())
}
}
#[inline]
fn should_stop(&self) -> bool {
self.cancelled.load(Ordering::Acquire)
}
}
#[derive(Debug, Clone)]
pub struct SyncStopper {
pub(crate) inner: Arc<SyncStopperInner>,
}
impl SyncStopper {
#[inline]
pub fn new() -> Self {
Self {
inner: Arc::new(SyncStopperInner {
cancelled: AtomicBool::new(false),
}),
}
}
#[inline]
pub fn cancelled() -> Self {
Self {
inner: Arc::new(SyncStopperInner {
cancelled: AtomicBool::new(true),
}),
}
}
#[inline]
pub fn cancel(&self) {
self.inner.cancelled.store(true, Ordering::Release);
}
#[inline]
pub fn is_cancelled(&self) -> bool {
self.inner.cancelled.load(Ordering::Acquire)
}
}
impl Default for SyncStopper {
fn default() -> Self {
Self::new()
}
}
impl Stop for SyncStopper {
#[inline]
fn check(&self) -> Result<(), StopReason> {
self.inner.check()
}
#[inline]
fn should_stop(&self) -> bool {
self.inner.should_stop()
}
}
impl core::fmt::Debug for SyncStopperInner {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SyncStopperInner")
.field("cancelled", &self.cancelled.load(Ordering::Relaxed))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sync_stopper_basic() {
let stop = SyncStopper::new();
assert!(!stop.is_cancelled());
assert!(!stop.should_stop());
assert!(stop.check().is_ok());
stop.cancel();
assert!(stop.is_cancelled());
assert!(stop.should_stop());
assert_eq!(stop.check(), Err(StopReason::Cancelled));
}
#[test]
fn sync_stopper_cancelled_constructor() {
let stop = SyncStopper::cancelled();
assert!(stop.is_cancelled());
assert!(stop.should_stop());
}
#[test]
fn sync_stopper_clone_shares_state() {
let stop1 = SyncStopper::new();
let stop2 = stop1.clone();
stop2.cancel();
assert!(stop1.should_stop());
assert!(stop2.should_stop());
}
#[test]
fn sync_stopper_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<SyncStopper>();
}
#[test]
fn sync_stopper_is_default() {
let stop: SyncStopper = Default::default();
assert!(!stop.is_cancelled());
}
}