use std::sync::{ atomic::{AtomicI64, Ordering}, Arc, }; /// A cloneable, thread-safe counter backed by an atomic integer. #[derive(Clone, Debug, Default)] pub struct ThreadSafeCounter { value: Arc, } impl ThreadSafeCounter { /// Creates a counter starting at zero. pub fn new() -> Self { Self::with_value(0) } /// Creates a counter with a specific initial value. pub fn with_value(value: i64) -> Self { Self { value: Arc::new(AtomicI64::new(value)), } } /// Returns the current counter value. pub fn get(&self) -> i64 { self.value.load(Ordering::SeqCst) } /// Adds `amount` and returns the updated value. pub fn add(&self, amount: i64) -> i64 { self.value.fetch_add(amount, Ordering::SeqCst) + amount } /// Increments the counter by one and returns the updated value. pub fn increment(&self) -> i64 { self.add(1) } /// Decrements the counter by one and returns the updated value. pub fn decrement(&self) -> i64 { self.add(-1) } /// Sets the counter to `value`. pub fn set(&self, value: i64) { self.value.store(value, Ordering::SeqCst); } /// Resets the counter to zero. pub fn reset(&self) { self.set(0); } } #[cfg(test)] mod tests { use super::ThreadSafeCounter; use std::thread; #[test] fn supports_basic_operations() { let counter = ThreadSafeCounter::with_value(10); assert_eq!(counter.get(), 10); assert_eq!(counter.increment(), 11); assert_eq!(counter.add(5), 16); assert_eq!(counter.decrement(), 15); counter.reset(); assert_eq!(counter.get(), 0); } #[test] fn is_safe_to_share_across_threads() { let counter = ThreadSafeCounter::new(); let mut handles = Vec::new(); for _ in 0..8 { let shared = counter.clone(); handles.push(thread::spawn(move || { for _ in 0..1_000 { shared.increment(); } })); } for handle in handles { handle.join().expect("thread should finish cleanly"); } assert_eq!(counter.get(), 8_000); } }