use std::sync::atomic::{AtomicU64, Ordering}; /// A thread-safe, lock-free counter. #[derive(Debug, Default)] pub struct Counter { value: AtomicU64, } impl Counter { pub const fn new(initial: u64) -> Self { Self { value: AtomicU64::new(initial), } } /// Returns the current value. pub fn get(&self) -> u64 { self.value.load(Ordering::Relaxed) } /// 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 } /// Sets the counter to zero and returns its previous value. pub fn reset(&self) -> u64 { self.value.swap(0, Ordering::Relaxed) } } #[cfg(test)] mod tests { use super::Counter; use std::sync::Arc; use std::thread; #[test] fn basic_operations() { let counter = Counter::new(2); assert_eq!(counter.increment(), 3); assert_eq!(counter.add(4), 7); assert_eq!(counter.reset(), 7); assert_eq!(counter.get(), 0); } #[test] fn increments_across_threads() { let counter = Arc::new(Counter::default()); let mut handles = Vec::new(); for _ in 0..8 { let counter = Arc::clone(&counter); handles.push(thread::spawn(move || { for _ in 0..10_000 { counter.increment(); } })); } for handle in handles { handle.join().expect("worker thread panicked"); } assert_eq!(counter.get(), 80_000); } }