qubit_atomic/atomic/atomic_count.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # Atomic Count
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 [`Atomic<usize>`](crate::Atomic).
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::AtomicCount;
45///
46/// let active_tasks = AtomicCount::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 AtomicCount {
61 /// Standard-library atomic storage for the non-negative counter value.
62 inner: StdAtomicUsize,
63}
64
65impl AtomicCount {
66 /// Creates a new non-negative atomic counter.
67 ///
68 /// # Parameters
69 ///
70 /// * `value` - The initial counter value.
71 ///
72 /// # Returns
73 ///
74 /// A counter initialized to `value`.
75 ///
76 /// # Example
77 ///
78 /// ```rust
79 /// use qubit_atomic::AtomicCount;
80 ///
81 /// let counter = AtomicCount::new(3);
82 /// assert_eq!(counter.get(), 3);
83 /// ```
84 #[inline]
85 pub const fn new(value: usize) -> Self {
86 Self {
87 inner: StdAtomicUsize::new(value),
88 }
89 }
90
91 /// Creates a new counter initialized to zero.
92 ///
93 /// # Returns
94 ///
95 /// A counter whose current value is zero.
96 ///
97 /// # Example
98 ///
99 /// ```rust
100 /// use qubit_atomic::AtomicCount;
101 ///
102 /// let counter = AtomicCount::zero();
103 /// assert!(counter.is_zero());
104 /// ```
105 #[inline]
106 pub const fn zero() -> Self {
107 Self::new(0)
108 }
109
110 /// Gets the current counter value.
111 ///
112 /// # Returns
113 ///
114 /// The current counter value.
115 ///
116 /// # Example
117 ///
118 /// ```rust
119 /// use qubit_atomic::AtomicCount;
120 ///
121 /// let counter = AtomicCount::new(7);
122 /// assert_eq!(counter.get(), 7);
123 /// ```
124 #[inline]
125 pub fn get(&self) -> usize {
126 self.inner.load(Ordering::Acquire)
127 }
128
129 /// Returns whether the current counter value is zero.
130 ///
131 /// # Returns
132 ///
133 /// `true` if the current value is zero, otherwise `false`.
134 ///
135 /// # Example
136 ///
137 /// ```rust
138 /// use qubit_atomic::AtomicCount;
139 ///
140 /// let counter = AtomicCount::zero();
141 /// assert!(counter.is_zero());
142 /// ```
143 #[inline]
144 pub fn is_zero(&self) -> bool {
145 self.get() == 0
146 }
147
148 /// Returns whether the current counter value is greater than zero.
149 ///
150 /// # Returns
151 ///
152 /// `true` if the current value is greater than zero, otherwise `false`.
153 ///
154 /// # Example
155 ///
156 /// ```rust
157 /// use qubit_atomic::AtomicCount;
158 ///
159 /// let counter = AtomicCount::new(1);
160 /// assert!(counter.is_positive());
161 /// ```
162 #[inline]
163 pub fn is_positive(&self) -> bool {
164 self.get() > 0
165 }
166
167 /// Increments the counter by one and returns the new value.
168 ///
169 /// # Returns
170 ///
171 /// The counter value after the increment.
172 ///
173 /// # Panics
174 ///
175 /// Panics if the increment would overflow [`usize::MAX`].
176 ///
177 /// # Example
178 ///
179 /// ```rust
180 /// use qubit_atomic::AtomicCount;
181 ///
182 /// let counter = AtomicCount::zero();
183 /// assert_eq!(counter.inc(), 1);
184 /// ```
185 #[inline]
186 pub fn inc(&self) -> usize {
187 self.add(1)
188 }
189
190 /// Adds `delta` to the counter and returns the new value.
191 ///
192 /// # Parameters
193 ///
194 /// * `delta` - The amount to add.
195 ///
196 /// # Returns
197 ///
198 /// The counter value after the addition.
199 ///
200 /// # Panics
201 ///
202 /// Panics if the addition would overflow [`usize::MAX`].
203 ///
204 /// # Example
205 ///
206 /// ```rust
207 /// use qubit_atomic::AtomicCount;
208 ///
209 /// let counter = AtomicCount::new(2);
210 /// assert_eq!(counter.add(3), 5);
211 /// ```
212 #[inline]
213 pub fn add(&self, delta: usize) -> usize {
214 self.try_add(delta).expect("atomic counter overflow")
215 }
216
217 /// Tries to add `delta` to the counter.
218 ///
219 /// # Parameters
220 ///
221 /// * `delta` - The amount to add.
222 ///
223 /// # Returns
224 ///
225 /// `Some(new_value)` if the addition succeeds, or `None` if it would
226 /// overflow [`usize::MAX`]. On `None`, the counter is left unchanged.
227 ///
228 /// # Example
229 ///
230 /// ```rust
231 /// use qubit_atomic::AtomicCount;
232 ///
233 /// let counter = AtomicCount::new(2);
234 /// assert_eq!(counter.try_add(3), Some(5));
235 /// ```
236 #[inline]
237 pub fn try_add(&self, delta: usize) -> Option<usize> {
238 self.try_update(|current| current.checked_add(delta))
239 }
240
241 /// Decrements the counter by one and returns the new value.
242 ///
243 /// This method is useful for detecting the transition to zero:
244 ///
245 /// ```rust
246 /// use qubit_atomic::AtomicCount;
247 ///
248 /// let counter = AtomicCount::new(1);
249 /// if counter.dec() == 0 {
250 /// // This call consumed the final counted item.
251 /// }
252 /// ```
253 ///
254 /// # Returns
255 ///
256 /// The counter value after the decrement.
257 ///
258 /// # Panics
259 ///
260 /// Panics if the current value is zero.
261 #[inline]
262 pub fn dec(&self) -> usize {
263 self.try_dec().expect("atomic counter underflow")
264 }
265
266 /// Tries to decrement the counter by one.
267 ///
268 /// # Returns
269 ///
270 /// `Some(new_value)` if the decrement succeeds, or `None` if the current
271 /// value is zero. On `None`, the counter is left unchanged.
272 ///
273 /// # Example
274 ///
275 /// ```rust
276 /// use qubit_atomic::AtomicCount;
277 ///
278 /// let counter = AtomicCount::new(1);
279 /// assert_eq!(counter.try_dec(), Some(0));
280 /// assert_eq!(counter.try_dec(), None);
281 /// ```
282 #[inline]
283 pub fn try_dec(&self) -> Option<usize> {
284 self.try_sub(1)
285 }
286
287 /// Subtracts `delta` from the counter and returns the new value.
288 ///
289 /// # Parameters
290 ///
291 /// * `delta` - The amount to subtract.
292 ///
293 /// # Returns
294 ///
295 /// The counter value after the subtraction.
296 ///
297 /// # Panics
298 ///
299 /// Panics if the subtraction would make the counter negative.
300 ///
301 /// # Example
302 ///
303 /// ```rust
304 /// use qubit_atomic::AtomicCount;
305 ///
306 /// let counter = AtomicCount::new(5);
307 /// assert_eq!(counter.sub(2), 3);
308 /// ```
309 #[inline]
310 pub fn sub(&self, delta: usize) -> usize {
311 self.try_sub(delta).expect("atomic counter underflow")
312 }
313
314 /// Tries to subtract `delta` from the counter.
315 ///
316 /// # Parameters
317 ///
318 /// * `delta` - The amount to subtract.
319 ///
320 /// # Returns
321 ///
322 /// `Some(new_value)` if the subtraction succeeds, or `None` if it would
323 /// make the counter negative. On `None`, the counter is left unchanged.
324 ///
325 /// # Example
326 ///
327 /// ```rust
328 /// use qubit_atomic::AtomicCount;
329 ///
330 /// let counter = AtomicCount::new(3);
331 /// assert_eq!(counter.try_sub(2), Some(1));
332 /// assert_eq!(counter.try_sub(2), None);
333 /// ```
334 #[inline]
335 pub fn try_sub(&self, delta: usize) -> Option<usize> {
336 self.try_update(|current| current.checked_sub(delta))
337 }
338
339 /// Applies a checked update with synchronization semantics.
340 ///
341 /// # Parameters
342 ///
343 /// * `update` - A function that maps the current value to the next value,
344 /// or returns `None` to reject the update.
345 ///
346 /// # Returns
347 ///
348 /// `Some(new_value)` if the update succeeds, or `None` if `update`
349 /// rejects the current value. A rejected update leaves the counter
350 /// unchanged.
351 #[inline]
352 fn try_update<F>(&self, update: F) -> Option<usize>
353 where
354 F: Fn(usize) -> Option<usize>,
355 {
356 let mut current = self.get();
357 loop {
358 let next = update(current)?;
359 match self.inner.compare_exchange_weak(
360 current,
361 next,
362 Ordering::AcqRel,
363 Ordering::Acquire,
364 ) {
365 Ok(_) => return Some(next),
366 Err(actual) => current = actual,
367 }
368 }
369 }
370}
371
372impl Default for AtomicCount {
373 /// Creates a zero-valued atomic counter.
374 ///
375 /// # Returns
376 ///
377 /// A counter whose current value is zero.
378 #[inline]
379 fn default() -> Self {
380 Self::zero()
381 }
382}
383
384impl From<usize> for AtomicCount {
385 /// Converts an initial counter value into an [`AtomicCount`].
386 ///
387 /// # Parameters
388 ///
389 /// * `value` - The initial counter value.
390 ///
391 /// # Returns
392 ///
393 /// A counter initialized to `value`.
394 #[inline]
395 fn from(value: usize) -> Self {
396 Self::new(value)
397 }
398}
399
400impl fmt::Debug for AtomicCount {
401 /// Formats the current counter value for debugging.
402 ///
403 /// # Parameters
404 ///
405 /// * `f` - The formatter receiving the debug representation.
406 ///
407 /// # Returns
408 ///
409 /// A formatting result from the formatter.
410 #[inline]
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 f.debug_struct("AtomicCount")
413 .field("value", &self.get())
414 .finish()
415 }
416}
417
418impl fmt::Display for AtomicCount {
419 /// Formats the current counter value with decimal display formatting.
420 ///
421 /// # Parameters
422 ///
423 /// * `f` - The formatter receiving the displayed value.
424 ///
425 /// # Returns
426 ///
427 /// A formatting result from the formatter.
428 #[inline]
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 write!(f, "{}", self.get())
431 }
432}