use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone, Debug, Default)]
pub struct Completion(Arc<AtomicBool>);
impl Completion {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn done() -> Self {
Self(Arc::new(AtomicBool::new(true)))
}
pub fn signal(&self) {
self.0.store(true, Ordering::Release);
}
#[must_use]
pub fn is_complete(&self) -> bool {
self.0.load(Ordering::Acquire)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_is_pending() {
assert!(!Completion::new().is_complete());
}
#[test]
fn done_is_already_complete() {
assert!(Completion::done().is_complete());
}
#[test]
fn clones_share_state() {
let a = Completion::new();
let b = a.clone();
b.signal();
assert!(a.is_complete());
}
#[test]
fn signal_is_idempotent() {
let c = Completion::new();
c.signal();
c.signal();
assert!(c.is_complete());
}
#[test]
fn signal_from_another_thread_is_seen() {
let c = Completion::new();
let handle = c.clone();
std::thread::spawn(move || handle.signal()).join().unwrap();
assert!(c.is_complete());
}
#[test]
fn completion_crosses_threads() {
const fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Completion>();
}
#[test]
fn orphaned_handle_signal_is_harmless() {
let c = Completion::new();
let orphan = c.clone();
drop(c);
orphan.signal();
assert!(orphan.is_complete());
}
}