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