qubit_atomic/atomic/atomic_counter.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic Counter
11//!
12//! Provides a non-negative atomic counter for values whose transitions are
13//! used as synchronization signals.
14//!
15//! # Author
16//!
17//! Haixing Hu
18
19use std::fmt;
20use std::sync::atomic::{
21 AtomicUsize as StdAtomicUsize,
22 Ordering,
23};
24
25/// A non-negative atomic counter with synchronization-oriented operations.
26///
27/// Use this type when the counter value is part of a concurrent state machine,
28/// such as active task counts, in-flight request counts, resource usage counts,
29/// or shutdown and termination checks. Its operations return the value after
30/// the update, which makes zero-transition logic straightforward.
31///
32/// For pure metrics, statistics, event counters, or ID generation, prefer the
33/// regular atomic integer types such as [`AtomicUsize`](crate::AtomicUsize).
34/// Those types keep arithmetic operations lightweight for pure counting.
35///
36/// This counter never wraps. Incrementing past [`usize::MAX`] panics, and
37/// decrementing below zero panics. Use [`try_add`](Self::try_add),
38/// [`try_dec`](Self::try_dec), or [`try_sub`](Self::try_sub) when overflow or
39/// underflow is a normal business outcome.
40///
41/// # Example
42///
43/// ```rust
44/// use qubit_atomic::AtomicCounter;
45///
46/// let active_tasks = AtomicCounter::zero();
47///
48/// active_tasks.inc();
49/// assert!(!active_tasks.is_zero());
50///
51/// if active_tasks.dec() == 0 {
52/// // The last active task finished; notify termination waiters here.
53/// }
54/// ```
55///
56/// # Author
57///
58/// Haixing Hu
59#[repr(transparent)]
60pub struct AtomicCounter {
61 inner: StdAtomicUsize,
62}
63
64impl AtomicCounter {
65 /// Creates a new non-negative atomic counter.
66 ///
67 /// # Parameters
68 ///
69 /// * `value` - The initial counter value.
70 ///
71 /// # Returns
72 ///
73 /// A counter initialized to `value`.
74 ///
75 /// # Example
76 ///
77 /// ```rust
78 /// use qubit_atomic::AtomicCounter;
79 ///
80 /// let counter = AtomicCounter::new(3);
81 /// assert_eq!(counter.get(), 3);
82 /// ```
83 #[inline]
84 pub const fn new(value: usize) -> Self {
85 Self {
86 inner: StdAtomicUsize::new(value),
87 }
88 }
89
90 /// Creates a new counter initialized to zero.
91 ///
92 /// # Returns
93 ///
94 /// A counter whose current value is zero.
95 ///
96 /// # Example
97 ///
98 /// ```rust
99 /// use qubit_atomic::AtomicCounter;
100 ///
101 /// let counter = AtomicCounter::zero();
102 /// assert!(counter.is_zero());
103 /// ```
104 #[inline]
105 pub const fn zero() -> Self {
106 Self::new(0)
107 }
108
109 /// Gets the current counter value.
110 ///
111 /// # Returns
112 ///
113 /// The current counter value.
114 ///
115 /// # Example
116 ///
117 /// ```rust
118 /// use qubit_atomic::AtomicCounter;
119 ///
120 /// let counter = AtomicCounter::new(7);
121 /// assert_eq!(counter.get(), 7);
122 /// ```
123 #[inline]
124 pub fn get(&self) -> usize {
125 self.inner.load(Ordering::Acquire)
126 }
127
128 /// Returns whether the current counter value is zero.
129 ///
130 /// # Returns
131 ///
132 /// `true` if the current value is zero, otherwise `false`.
133 ///
134 /// # Example
135 ///
136 /// ```rust
137 /// use qubit_atomic::AtomicCounter;
138 ///
139 /// let counter = AtomicCounter::zero();
140 /// assert!(counter.is_zero());
141 /// ```
142 #[inline]
143 pub fn is_zero(&self) -> bool {
144 self.get() == 0
145 }
146
147 /// Returns whether the current counter value is greater than zero.
148 ///
149 /// # Returns
150 ///
151 /// `true` if the current value is greater than zero, otherwise `false`.
152 ///
153 /// # Example
154 ///
155 /// ```rust
156 /// use qubit_atomic::AtomicCounter;
157 ///
158 /// let counter = AtomicCounter::new(1);
159 /// assert!(counter.is_positive());
160 /// ```
161 #[inline]
162 pub fn is_positive(&self) -> bool {
163 self.get() > 0
164 }
165
166 /// Increments the counter by one and returns the new value.
167 ///
168 /// # Returns
169 ///
170 /// The counter value after the increment.
171 ///
172 /// # Panics
173 ///
174 /// Panics if the increment would overflow [`usize::MAX`].
175 ///
176 /// # Example
177 ///
178 /// ```rust
179 /// use qubit_atomic::AtomicCounter;
180 ///
181 /// let counter = AtomicCounter::zero();
182 /// assert_eq!(counter.inc(), 1);
183 /// ```
184 #[inline]
185 pub fn inc(&self) -> usize {
186 self.add(1)
187 }
188
189 /// Adds `delta` to the counter and returns the new value.
190 ///
191 /// # Parameters
192 ///
193 /// * `delta` - The amount to add.
194 ///
195 /// # Returns
196 ///
197 /// The counter value after the addition.
198 ///
199 /// # Panics
200 ///
201 /// Panics if the addition would overflow [`usize::MAX`].
202 ///
203 /// # Example
204 ///
205 /// ```rust
206 /// use qubit_atomic::AtomicCounter;
207 ///
208 /// let counter = AtomicCounter::new(2);
209 /// assert_eq!(counter.add(3), 5);
210 /// ```
211 #[inline]
212 pub fn add(&self, delta: usize) -> usize {
213 self.try_add(delta).expect("atomic counter overflow")
214 }
215
216 /// Tries to add `delta` to the counter.
217 ///
218 /// # Parameters
219 ///
220 /// * `delta` - The amount to add.
221 ///
222 /// # Returns
223 ///
224 /// `Some(new_value)` if the addition succeeds, or `None` if it would
225 /// overflow [`usize::MAX`]. On `None`, the counter is left unchanged.
226 ///
227 /// # Example
228 ///
229 /// ```rust
230 /// use qubit_atomic::AtomicCounter;
231 ///
232 /// let counter = AtomicCounter::new(2);
233 /// assert_eq!(counter.try_add(3), Some(5));
234 /// ```
235 #[inline]
236 pub fn try_add(&self, delta: usize) -> Option<usize> {
237 self.try_update(|current| current.checked_add(delta))
238 }
239
240 /// Decrements the counter by one and returns the new value.
241 ///
242 /// This method is useful for detecting the transition to zero:
243 ///
244 /// ```rust
245 /// use qubit_atomic::AtomicCounter;
246 ///
247 /// let counter = AtomicCounter::new(1);
248 /// if counter.dec() == 0 {
249 /// // This call consumed the final counted item.
250 /// }
251 /// ```
252 ///
253 /// # Returns
254 ///
255 /// The counter value after the decrement.
256 ///
257 /// # Panics
258 ///
259 /// Panics if the current value is zero.
260 #[inline]
261 pub fn dec(&self) -> usize {
262 self.try_dec().expect("atomic counter underflow")
263 }
264
265 /// Tries to decrement the counter by one.
266 ///
267 /// # Returns
268 ///
269 /// `Some(new_value)` if the decrement succeeds, or `None` if the current
270 /// value is zero. On `None`, the counter is left unchanged.
271 ///
272 /// # Example
273 ///
274 /// ```rust
275 /// use qubit_atomic::AtomicCounter;
276 ///
277 /// let counter = AtomicCounter::new(1);
278 /// assert_eq!(counter.try_dec(), Some(0));
279 /// assert_eq!(counter.try_dec(), None);
280 /// ```
281 #[inline]
282 pub fn try_dec(&self) -> Option<usize> {
283 self.try_sub(1)
284 }
285
286 /// Subtracts `delta` from the counter and returns the new value.
287 ///
288 /// # Parameters
289 ///
290 /// * `delta` - The amount to subtract.
291 ///
292 /// # Returns
293 ///
294 /// The counter value after the subtraction.
295 ///
296 /// # Panics
297 ///
298 /// Panics if the subtraction would make the counter negative.
299 ///
300 /// # Example
301 ///
302 /// ```rust
303 /// use qubit_atomic::AtomicCounter;
304 ///
305 /// let counter = AtomicCounter::new(5);
306 /// assert_eq!(counter.sub(2), 3);
307 /// ```
308 #[inline]
309 pub fn sub(&self, delta: usize) -> usize {
310 self.try_sub(delta).expect("atomic counter underflow")
311 }
312
313 /// Tries to subtract `delta` from the counter.
314 ///
315 /// # Parameters
316 ///
317 /// * `delta` - The amount to subtract.
318 ///
319 /// # Returns
320 ///
321 /// `Some(new_value)` if the subtraction succeeds, or `None` if it would
322 /// make the counter negative. On `None`, the counter is left unchanged.
323 ///
324 /// # Example
325 ///
326 /// ```rust
327 /// use qubit_atomic::AtomicCounter;
328 ///
329 /// let counter = AtomicCounter::new(3);
330 /// assert_eq!(counter.try_sub(2), Some(1));
331 /// assert_eq!(counter.try_sub(2), None);
332 /// ```
333 #[inline]
334 pub fn try_sub(&self, delta: usize) -> Option<usize> {
335 self.try_update(|current| current.checked_sub(delta))
336 }
337
338 /// Applies a checked update with synchronization semantics.
339 ///
340 /// # Parameters
341 ///
342 /// * `update` - A function that maps the current value to the next value,
343 /// or returns `None` to reject the update.
344 ///
345 /// # Returns
346 ///
347 /// `Some(new_value)` if the update succeeds, or `None` if `update`
348 /// rejects the current value. A rejected update leaves the counter
349 /// unchanged.
350 #[inline]
351 fn try_update<F>(&self, update: F) -> Option<usize>
352 where
353 F: Fn(usize) -> Option<usize>,
354 {
355 let mut current = self.get();
356 loop {
357 let next = update(current)?;
358 match self.inner.compare_exchange_weak(
359 current,
360 next,
361 Ordering::AcqRel,
362 Ordering::Acquire,
363 ) {
364 Ok(_) => return Some(next),
365 Err(actual) => current = actual,
366 }
367 }
368 }
369}
370
371impl Default for AtomicCounter {
372 #[inline]
373 fn default() -> Self {
374 Self::zero()
375 }
376}
377
378impl From<usize> for AtomicCounter {
379 #[inline]
380 fn from(value: usize) -> Self {
381 Self::new(value)
382 }
383}
384
385impl fmt::Debug for AtomicCounter {
386 #[inline]
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 f.debug_struct("AtomicCounter")
389 .field("value", &self.get())
390 .finish()
391 }
392}
393
394impl fmt::Display for AtomicCounter {
395 #[inline]
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 write!(f, "{}", self.get())
398 }
399}