use std::sync::{ atomic::{AtomicU64, Ordering}, Arc, }; /// A cloneable, thread-safe counter. #[derive(Clone, Debug)] pub struct Counter { value: Arc, } impl Counter { pub fn new(initial: u64) -> Self { Self { value: Arc::new(AtomicU64::new(initial)), } } /// Increments the counter and returns the new value. pub fn increment(&self) -> u64 { self.add(1) } /// Adds `amount` and returns the new value. pub fn add(&self, amount: u64) -> u64 { self.value.fetch_add(amount, Ordering::Relaxed) + amount } /// Returns the current value. pub fn get(&self) -> u64 { self.value.load(Ordering::Relaxed) } } impl Default for Counter { fn default() -> Self { Self::new(0) } } #[cfg(test)] mod tests { use super::Counter; use std::thread; #[test] fn increments_and_adds() { let counter = Counter::default(); assert_eq!(counter.increment(), 1); assert_eq!(counter.add(4), 5); assert_eq!(counter.get(), 5); } #[test] fn counts_across_threads() { let counter = Counter::default(); 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); } }