use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, }; /// A small, cloneable counter that can be safely shared across threads. #[derive(Clone, Debug, Default)] pub struct Counter { value: Arc, } impl Counter { /// Creates a counter starting at zero. pub fn new() -> Self { Self::with_value(0) } /// Creates a counter with an initial value. pub fn with_value(value: usize) -> Self { Self { value: Arc::new(AtomicUsize::new(value)), } } /// Increments the counter by one and returns the new value. pub fn increment(&self) -> usize { self.add(1) } /// Adds `amount` and returns the new value. pub fn add(&self, amount: usize) -> usize { self.value.fetch_add(amount, Ordering::Relaxed) + amount } /// Returns the current value. pub fn get(&self) -> usize { self.value.load(Ordering::Relaxed) } /// Sets the counter back to zero. pub fn reset(&self) { self.value.store(0, Ordering::Relaxed); } } #[cfg(test)] mod tests { use super::Counter; use std::thread; #[test] fn increments_and_resets() { let counter = Counter::new(); assert_eq!(counter.increment(), 1); assert_eq!(counter.add(4), 5); assert_eq!(counter.get(), 5); counter.reset(); assert_eq!(counter.get(), 0); } #[test] fn counts_across_threads() { let counter = Counter::new(); let mut handles = Vec::new(); for _ in 0..8 { let counter = counter.clone(); handles.push(thread::spawn(move || { for _ in 0..1_000 { counter.increment(); } })); } for handle in handles { handle.join().unwrap(); } assert_eq!(counter.get(), 8_000); } }