Skip to main content

vyre_driver/
accounting.rs

1//! Backend-neutral checked arithmetic and atomic accounting primitives.
2
3use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
4
5use crate::BackendError;
6
7/// Add two `u64` values without wraparound.
8pub fn checked_add_u64_value<E>(lhs: u64, rhs: u64, error: E) -> Result<u64, E> {
9    lhs.checked_add(rhs).ok_or(error)
10}
11
12/// Add two `u64` values without wraparound, constructing the error lazily.
13///
14/// # Errors
15///
16/// Returns `E` from `error` when the addition would overflow.
17pub fn checked_add_u64_lazy<E>(lhs: u64, rhs: u64, error: impl FnOnce() -> E) -> Result<u64, E> {
18    lhs.checked_add(rhs).ok_or_else(error)
19}
20
21/// Multiply two `u64` values without wraparound.
22pub fn checked_mul_u64_value<E>(lhs: u64, rhs: u64, error: E) -> Result<u64, E> {
23    lhs.checked_mul(rhs).ok_or(error)
24}
25
26/// Multiply two `u64` values without wraparound, constructing the error lazily.
27///
28/// # Errors
29///
30/// Returns `E` from `error` when the multiplication would overflow.
31pub fn checked_mul_u64_lazy<E>(lhs: u64, rhs: u64, error: impl FnOnce() -> E) -> Result<u64, E> {
32    lhs.checked_mul(rhs).ok_or_else(error)
33}
34
35/// Subtract two `u64` values without underflow.
36pub fn checked_sub_u64_value<E>(lhs: u64, rhs: u64, error: E) -> Result<u64, E> {
37    lhs.checked_sub(rhs).ok_or(error)
38}
39
40/// Subtract two `u64` values without underflow, constructing the error lazily.
41///
42/// # Errors
43///
44/// Returns `E` from `error` when the subtraction would underflow.
45pub fn checked_sub_u64_lazy<E>(lhs: u64, rhs: u64, error: impl FnOnce() -> E) -> Result<u64, E> {
46    lhs.checked_sub(rhs).ok_or_else(error)
47}
48
49/// Subtract two `usize` values without underflow, constructing the error lazily.
50///
51/// # Errors
52///
53/// Returns `E` from `error` when the subtraction would underflow.
54pub fn checked_sub_usize_lazy<E>(
55    lhs: usize,
56    rhs: usize,
57    error: impl FnOnce() -> E,
58) -> Result<usize, E> {
59    lhs.checked_sub(rhs).ok_or_else(error)
60}
61
62/// Add two `usize` values without wraparound.
63pub fn checked_add_usize_value<E>(lhs: usize, rhs: usize, error: E) -> Result<usize, E> {
64    lhs.checked_add(rhs).ok_or(error)
65}
66
67/// Add two `usize` values without wraparound, constructing the error lazily.
68///
69/// # Errors
70///
71/// Returns `E` from `error` when the addition would overflow.
72pub fn checked_add_usize_lazy<E>(
73    lhs: usize,
74    rhs: usize,
75    error: impl FnOnce() -> E,
76) -> Result<usize, E> {
77    lhs.checked_add(rhs).ok_or_else(error)
78}
79
80/// Multiply two `usize` values without wraparound, constructing the error lazily.
81///
82/// # Errors
83///
84/// Returns `E` from `error` when the multiplication would overflow.
85pub fn checked_mul_usize_lazy<E>(
86    lhs: usize,
87    rhs: usize,
88    error: impl FnOnce() -> E,
89) -> Result<usize, E> {
90    lhs.checked_mul(rhs).ok_or_else(error)
91}
92
93/// Convert `usize` to `u64`, constructing the error lazily on overflow.
94///
95/// # Errors
96///
97/// Returns `E` from `error` when `value` cannot fit in `u64`.
98pub fn checked_usize_to_u64_lazy<E>(value: usize, error: impl FnOnce() -> E) -> Result<u64, E> {
99    u64::try_from(value).map_err(|_| error())
100}
101
102/// Validate a `usize` byte range and return its exclusive end.
103///
104/// # Errors
105///
106/// Returns `E` from `overflow_error` when `start + len` would overflow, or
107/// `E` from `out_of_bounds_error` when the range end exceeds `limit`.
108pub fn checked_usize_byte_range_end_lazy<E>(
109    start: usize,
110    len: usize,
111    limit: usize,
112    overflow_error: impl FnOnce() -> E,
113    out_of_bounds_error: impl FnOnce(usize) -> E,
114) -> Result<usize, E> {
115    let end = start.checked_add(len).ok_or_else(overflow_error)?;
116    if end > limit {
117        return Err(out_of_bounds_error(end));
118    }
119    Ok(end)
120}
121
122/// Add a `usize` byte offset to a `u64` base pointer/counter without wraparound.
123///
124/// # Errors
125///
126/// Returns `E` from `conversion_error` when `offset` cannot be represented as
127/// `u64`, or `E` from `overflow_error` when `base + offset` would overflow.
128pub fn checked_add_u64_usize_offset_lazy<E>(
129    base: u64,
130    offset: usize,
131    conversion_error: impl FnOnce() -> E,
132    overflow_error: impl FnOnce() -> E,
133) -> Result<u64, E> {
134    let offset = u64::try_from(offset).map_err(|_| conversion_error())?;
135    base.checked_add(offset).ok_or_else(overflow_error)
136}
137
138#[cfg(test)]
139mod byte_range_accounting_tests {
140    use std::cell::Cell;
141
142    use super::{
143        checked_add_u64_usize_offset_lazy, checked_mul_u32_value, checked_mul_u64_lazy,
144        checked_sub_u64_lazy, checked_sub_usize_lazy, checked_usize_byte_range_end_lazy,
145        checked_usize_to_u64_lazy,
146    };
147
148    #[test]
149    fn checked_mul_u64_lazy_is_lazy_on_success() {
150        let overflow_called = Cell::new(false);
151
152        let value = checked_mul_u64_lazy(8, 4, || {
153            overflow_called.set(true);
154            "overflow"
155        });
156
157        assert_eq!(value, Ok(32));
158        assert!(!overflow_called.get());
159    }
160
161    #[test]
162    fn checked_mul_u64_lazy_reports_overflow() {
163        let value = checked_mul_u64_lazy(u64::MAX, 2, || "overflow");
164
165        assert_eq!(value, Err("overflow"));
166    }
167
168    #[test]
169    fn checked_mul_u32_value_multiplies_without_wraparound() {
170        let value = checked_mul_u32_value(128, 8, "overflow");
171
172        assert_eq!(value, Ok(1024));
173    }
174
175    #[test]
176    fn checked_mul_u32_value_reports_overflow() {
177        let value = checked_mul_u32_value(u32::MAX, 2, "overflow");
178
179        assert_eq!(value, Err("overflow"));
180    }
181
182    #[test]
183    fn checked_sub_u64_lazy_reports_underflow() {
184        let value = checked_sub_u64_lazy(1, 2, || "underflow");
185
186        assert_eq!(value, Err("underflow"));
187    }
188
189    #[test]
190    fn checked_sub_usize_lazy_reports_underflow() {
191        let value = checked_sub_usize_lazy(4, 8, || "underflow");
192
193        assert_eq!(value, Err("underflow"));
194    }
195
196    #[test]
197    fn checked_usize_to_u64_lazy_converts_host_width() {
198        let value = checked_usize_to_u64_lazy(64, || "overflow");
199
200        assert_eq!(value, Ok(64));
201    }
202
203    #[test]
204    fn checked_usize_byte_range_end_lazy_is_lazy_on_success() {
205        let overflow_called = Cell::new(false);
206        let bounds_called = Cell::new(false);
207
208        let end = checked_usize_byte_range_end_lazy(
209            8,
210            4,
211            16,
212            || {
213                overflow_called.set(true);
214                "overflow"
215            },
216            |_| {
217                bounds_called.set(true);
218                "bounds"
219            },
220        );
221
222        assert_eq!(end, Ok(12));
223        assert!(!overflow_called.get());
224        assert!(!bounds_called.get());
225    }
226
227    #[test]
228    fn checked_usize_byte_range_end_lazy_passes_computed_end_to_bounds_error() {
229        let end = checked_usize_byte_range_end_lazy(8, 5, 12, || usize::MAX, |end| end);
230
231        assert_eq!(end, Err(13));
232    }
233
234    #[test]
235    fn checked_add_u64_usize_offset_lazy_is_lazy_on_success() {
236        let conversion_called = Cell::new(false);
237        let overflow_called = Cell::new(false);
238
239        let value = checked_add_u64_usize_offset_lazy(
240            64,
241            8,
242            || {
243                conversion_called.set(true);
244                "conversion"
245            },
246            || {
247                overflow_called.set(true);
248                "overflow"
249            },
250        );
251
252        assert_eq!(value, Ok(72));
253        assert!(!conversion_called.get());
254        assert!(!overflow_called.get());
255    }
256
257    #[test]
258    fn checked_add_u64_usize_offset_lazy_reports_pointer_overflow() {
259        let value = checked_add_u64_usize_offset_lazy(u64::MAX, 1, || "conversion", || "overflow");
260
261        assert_eq!(value, Err("overflow"));
262    }
263}
264
265/// Add two `u32` values without wraparound.
266pub fn checked_add_u32_value<E>(lhs: u32, rhs: u32, error: E) -> Result<u32, E> {
267    lhs.checked_add(rhs).ok_or(error)
268}
269
270/// Multiply two `u32` values without wraparound.
271pub fn checked_mul_u32_value<E>(lhs: u32, rhs: u32, error: E) -> Result<u32, E> {
272    lhs.checked_mul(rhs).ok_or(error)
273}
274
275/// Domain error adapter for planner-specific arithmetic overflow fields.
276pub trait ArithmeticOverflow: Sized {
277    /// Build the planner-specific overflow error for `field`.
278    fn arithmetic_overflow(field: &'static str) -> Self;
279}
280
281/// Add two `u64` counters and map overflow into the caller domain.
282///
283/// # Errors
284///
285/// Returns `E` when the addition would overflow.
286pub fn checked_add_u64_count<E>(lhs: u64, rhs: u64, field: &'static str) -> Result<u64, E>
287where
288    E: ArithmeticOverflow,
289{
290    checked_add_u64_value(lhs, rhs, E::arithmetic_overflow(field))
291}
292
293/// Multiply two `u64` counters and map overflow into the caller domain.
294///
295/// # Errors
296///
297/// Returns `E` when the multiplication would overflow.
298pub fn checked_mul_u64_count<E>(lhs: u64, rhs: u64, field: &'static str) -> Result<u64, E>
299where
300    E: ArithmeticOverflow,
301{
302    checked_mul_u64_value(lhs, rhs, E::arithmetic_overflow(field))
303}
304
305/// Subtract two `u64` counters and map underflow into the caller domain.
306///
307/// # Errors
308///
309/// Returns `E` when the subtraction would underflow.
310pub fn checked_sub_u64_count<E>(lhs: u64, rhs: u64, field: &'static str) -> Result<u64, E>
311where
312    E: ArithmeticOverflow,
313{
314    checked_sub_u64_value(lhs, rhs, E::arithmetic_overflow(field))
315}
316
317/// Add two `usize` counters and map overflow into the caller domain.
318///
319/// # Errors
320///
321/// Returns `E` when the addition would overflow.
322pub fn checked_add_usize_count<E>(lhs: usize, rhs: usize, field: &'static str) -> Result<usize, E>
323where
324    E: ArithmeticOverflow,
325{
326    checked_add_usize_value(lhs, rhs, E::arithmetic_overflow(field))
327}
328
329/// Add two `u32` counters and map overflow into the caller domain.
330///
331/// # Errors
332///
333/// Returns `E` when the addition would overflow.
334pub fn checked_add_u32_count<E>(lhs: u32, rhs: u32, field: &'static str) -> Result<u32, E>
335where
336    E: ArithmeticOverflow,
337{
338    checked_add_u32_value(lhs, rhs, E::arithmetic_overflow(field))
339}
340
341/// Add `value` to a `u64` counter without allowing wraparound or saturation.
342///
343/// # Errors
344///
345/// Returns [`BackendError`] from `overflow` when the addition would overflow.
346pub fn checked_atomic_add_u64(
347    counter: &AtomicU64,
348    value: u64,
349    overflow: impl Fn(u64, u64) -> BackendError,
350) -> Result<(), BackendError> {
351    checked_atomic_add_u64_with_order(
352        counter,
353        value,
354        Ordering::Relaxed,
355        Ordering::Relaxed,
356        Ordering::Relaxed,
357        overflow,
358    )
359}
360
361/// Add `value` to a `u64` counter with caller-selected atomic orderings.
362///
363/// # Errors
364///
365/// Returns `E` from `overflow` when the addition would overflow.
366pub fn checked_atomic_add_u64_with_order<E>(
367    counter: &AtomicU64,
368    value: u64,
369    load_order: Ordering,
370    success_order: Ordering,
371    failure_order: Ordering,
372    overflow: impl Fn(u64, u64) -> E,
373) -> Result<(), E> {
374    checked_atomic_add_u64_guarded_with_order(
375        counter,
376        value,
377        load_order,
378        success_order,
379        failure_order,
380        overflow,
381        |_| Ok(()),
382    )
383}
384
385/// Add `value` to a `u64` counter with overflow checking and a pre-CAS next-value guard.
386///
387/// # Errors
388///
389/// Returns `E` from `overflow` when the addition would overflow, or from
390/// `validate_next` when the computed next value violates a caller invariant.
391pub fn checked_atomic_add_u64_guarded_with_order<E>(
392    counter: &AtomicU64,
393    value: u64,
394    load_order: Ordering,
395    success_order: Ordering,
396    failure_order: Ordering,
397    overflow: impl Fn(u64, u64) -> E,
398    mut validate_next: impl FnMut(u64) -> Result<(), E>,
399) -> Result<(), E> {
400    let mut observed = counter.load(load_order);
401    loop {
402        let next = observed
403            .checked_add(value)
404            .ok_or_else(|| overflow(observed, value))?;
405        validate_next(next)?;
406        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
407            Ok(_) => return Ok(()),
408            Err(actual) => observed = actual,
409        }
410    }
411}
412
413/// Add `value` to a `usize` counter without allowing wraparound.
414///
415/// # Errors
416///
417/// Returns [`BackendError`] from `overflow` when the addition would overflow.
418pub fn checked_atomic_add_usize(
419    counter: &AtomicUsize,
420    value: usize,
421    overflow: impl Fn(usize, usize) -> BackendError,
422) -> Result<(), BackendError> {
423    checked_atomic_add_usize_with_order(
424        counter,
425        value,
426        Ordering::Acquire,
427        Ordering::AcqRel,
428        Ordering::Acquire,
429        overflow,
430    )
431}
432
433/// Add `value` to a `usize` counter with caller-selected atomic orderings.
434///
435/// # Errors
436///
437/// Returns `E` from `overflow` when the addition would overflow.
438pub fn checked_atomic_add_usize_with_order<E>(
439    counter: &AtomicUsize,
440    value: usize,
441    load_order: Ordering,
442    success_order: Ordering,
443    failure_order: Ordering,
444    overflow: impl Fn(usize, usize) -> E,
445) -> Result<(), E> {
446    checked_atomic_add_usize_guarded_with_order(
447        counter,
448        value,
449        load_order,
450        success_order,
451        failure_order,
452        overflow,
453        |_| Ok(()),
454    )
455}
456
457/// Add `value` to a `usize` counter with overflow checking and a pre-CAS next-value guard.
458///
459/// # Errors
460///
461/// Returns `E` from `overflow` when the addition would overflow, or from
462/// `validate_next` when the computed next value violates a caller invariant.
463
464pub fn checked_atomic_add_usize_guarded_with_order<E>(
465    counter: &AtomicUsize,
466    value: usize,
467    load_order: Ordering,
468    success_order: Ordering,
469    failure_order: Ordering,
470    overflow: impl Fn(usize, usize) -> E,
471    mut validate_next: impl FnMut(usize) -> Result<(), E>,
472) -> Result<(), E> {
473    let mut observed = counter.load(load_order);
474    loop {
475        let next = observed
476            .checked_add(value)
477            .ok_or_else(|| overflow(observed, value))?;
478        validate_next(next)?;
479        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
480            Ok(_) => return Ok(()),
481            Err(actual) => observed = actual,
482        }
483    }
484}
485
486/// Subtract `value` from a `u64` counter without allowing underflow.
487///
488/// # Errors
489///
490/// Returns [`BackendError`] from `underflow` when the subtraction would underflow.
491pub fn checked_atomic_sub_u64(
492    counter: &AtomicU64,
493    value: u64,
494    underflow: impl Fn(u64, u64) -> BackendError,
495) -> Result<(), BackendError> {
496    checked_atomic_sub_u64_with_order(
497        counter,
498        value,
499        Ordering::Acquire,
500        Ordering::AcqRel,
501        Ordering::Acquire,
502        underflow,
503    )
504}
505
506/// Subtract `value` from a `u64` counter with caller-selected atomic orderings.
507///
508/// # Errors
509///
510/// Returns `E` from `underflow` when the subtraction would underflow.
511pub fn checked_atomic_sub_u64_with_order<E>(
512    counter: &AtomicU64,
513    value: u64,
514    load_order: Ordering,
515    success_order: Ordering,
516    failure_order: Ordering,
517    underflow: impl Fn(u64, u64) -> E,
518) -> Result<(), E> {
519    if value == 0 {
520        return Ok(());
521    }
522    let mut observed = counter.load(load_order);
523    loop {
524        let next = observed
525            .checked_sub(value)
526            .ok_or_else(|| underflow(observed, value))?;
527        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
528            Ok(_) => return Ok(()),
529            Err(actual) => observed = actual,
530        }
531    }
532}
533
534/// Subtract `value` from a `usize` counter without allowing underflow.
535///
536/// # Errors
537///
538/// Returns [`BackendError`] from `underflow` when the subtraction would underflow.
539pub fn checked_atomic_sub_usize(
540    counter: &AtomicUsize,
541    value: usize,
542    underflow: impl Fn(usize, usize) -> BackendError,
543) -> Result<(), BackendError> {
544    checked_atomic_sub_usize_with_order(
545        counter,
546        value,
547        Ordering::Acquire,
548        Ordering::AcqRel,
549        Ordering::Acquire,
550        underflow,
551    )
552}
553
554/// Subtract `value` from a `usize` counter with caller-selected atomic orderings.
555///
556/// # Errors
557///
558/// Returns `E` from `underflow` when the subtraction would underflow.
559pub fn checked_atomic_sub_usize_with_order<E>(
560    counter: &AtomicUsize,
561    value: usize,
562    load_order: Ordering,
563    success_order: Ordering,
564    failure_order: Ordering,
565    underflow: impl Fn(usize, usize) -> E,
566) -> Result<(), E> {
567    if value == 0 {
568        return Ok(());
569    }
570    let mut observed = counter.load(load_order);
571    loop {
572        let next = observed
573            .checked_sub(value)
574            .ok_or_else(|| underflow(observed, value))?;
575        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
576            Ok(_) => return Ok(()),
577            Err(actual) => observed = actual,
578        }
579    }
580}
581
582/// Apply a checked update to a `u64` atomic counter with caller-selected
583/// orderings.
584///
585/// Returns the value observed before the successful publish. `update` receives
586/// each observed value and must return the next value to publish. `on_retry`
587/// runs after a failed CAS and may abort the update by returning `Err`.
588pub fn checked_atomic_update_u64_with_order<E>(
589    counter: &AtomicU64,
590    load_order: Ordering,
591    success_order: Ordering,
592    failure_order: Ordering,
593    mut update: impl FnMut(u64) -> Result<u64, E>,
594    mut on_retry: impl FnMut(u64, u64) -> Result<(), E>,
595) -> Result<u64, E> {
596    let mut observed = counter.load(load_order);
597    loop {
598        let next = update(observed)?;
599        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
600            Ok(previous) => return Ok(previous),
601            Err(actual) => {
602                on_retry(observed, actual)?;
603                observed = actual;
604            }
605        }
606    }
607}
608
609/// Apply a checked update to a `u32` atomic counter with caller-selected
610/// orderings.
611///
612/// Returns the value observed before the successful publish. This keeps
613/// bounded sequence allocators from copying CAS retry loops into consumers.
614pub fn checked_atomic_update_u32_with_order<E>(
615    counter: &AtomicU32,
616    load_order: Ordering,
617    success_order: Ordering,
618    failure_order: Ordering,
619    mut update: impl FnMut(u32) -> Result<u32, E>,
620    mut on_retry: impl FnMut(u32, u32) -> Result<(), E>,
621) -> Result<u32, E> {
622    let mut observed = counter.load(load_order);
623    loop {
624        let next = update(observed)?;
625        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
626            Ok(previous) => return Ok(previous),
627            Err(actual) => {
628                on_retry(observed, actual)?;
629                observed = actual;
630            }
631        }
632    }
633}
634
635#[cfg(test)]
636mod checked_atomic_update_with_order_tests {
637    use super::*;
638
639    #[test]
640    fn checked_atomic_update_u64_publishes_checked_next_and_returns_observed() {
641        let counter = AtomicU64::new(41);
642
643        let previous = checked_atomic_update_u64_with_order(
644            &counter,
645            Ordering::Acquire,
646            Ordering::AcqRel,
647            Ordering::Acquire,
648            |observed| observed.checked_add(1).ok_or("overflow"),
649            |_, _| Ok(()),
650        )
651        .expect("Fix: reject accounting updates that overflow the tracked counter range - update should fit");
652
653        assert_eq!(previous, 41);
654        assert_eq!(counter.load(Ordering::Acquire), 42);
655    }
656
657    #[test]
658    fn checked_atomic_update_u32_rejects_without_publishing() {
659        let counter = AtomicU32::new(u32::MAX);
660
661        let error = checked_atomic_update_u32_with_order(
662            &counter,
663            Ordering::Acquire,
664            Ordering::AcqRel,
665            Ordering::Acquire,
666            |observed| observed.checked_add(1).ok_or("overflow"),
667            |_, _| Ok(()),
668        )
669        .expect_err("overflow should be surfaced");
670
671        assert_eq!(error, "overflow");
672        assert_eq!(counter.load(Ordering::Acquire), u32::MAX);
673    }
674}
675
676/// Subtract `value` from a `usize` counter, repairing underflow to zero.
677///
678/// This is only for release-path accounting where the caller has already
679/// decided that a corrupt counter must be repaired rather than propagated as a
680/// dispatch error. `on_repair` is called after a successful repair CAS.
681pub fn repair_atomic_sub_usize_with_order(
682    counter: &AtomicUsize,
683    value: usize,
684    load_order: Ordering,
685    success_order: Ordering,
686    failure_order: Ordering,
687    on_repair: impl FnMut(usize, usize),
688) {
689    let _ = repair_atomic_sub_usize_fetch_with_order(
690        counter,
691        value,
692        load_order,
693        success_order,
694        failure_order,
695        on_repair,
696    );
697}
698
699/// Subtract `value` from a `usize` counter, repairing underflow to zero and
700/// returning the observed value before the successful publish.
701///
702/// This preserves `fetch_sub`-style previous-value semantics for padded or
703/// wrapper counters while keeping the underflow repair policy single-sourced.
704pub fn repair_atomic_sub_usize_fetch_with_order(
705    counter: &AtomicUsize,
706    value: usize,
707    load_order: Ordering,
708    success_order: Ordering,
709    failure_order: Ordering,
710    mut on_repair: impl FnMut(usize, usize),
711) -> usize {
712    if value == 0 {
713        return counter.load(load_order);
714    }
715    let mut observed = counter.load(load_order);
716    loop {
717        let Some(next) = observed.checked_sub(value) else {
718            match counter.compare_exchange_weak(observed, 0, success_order, failure_order) {
719                Ok(_) => {
720                    on_repair(observed, value);
721                    return observed;
722                }
723                Err(actual) => {
724                    observed = actual;
725                    continue;
726                }
727            }
728        };
729        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
730            Ok(_) => return observed,
731            Err(actual) => observed = actual,
732        }
733    }
734}
735
736/// Add `value` to a `usize` atomic counter, pinning it at `usize::MAX` instead
737/// of wrapping, and return the observed value before the successful publish.
738///
739/// `on_pinned` is called exactly once when a successful publish moves a
740/// non-pinned counter to `usize::MAX`.
741pub fn pinning_atomic_add_usize_with_order(
742    counter: &AtomicUsize,
743    value: usize,
744    success_order: Ordering,
745    failure_order: Ordering,
746    on_pinned: impl FnOnce(usize, usize),
747) -> usize {
748    if value == 0 {
749        return counter.load(failure_order);
750    }
751    let mut current = counter.load(failure_order);
752    loop {
753        let next = current.checked_add(value).unwrap_or(usize::MAX);
754        match counter.compare_exchange_weak(current, next, success_order, failure_order) {
755            Ok(previous) => {
756                if next == usize::MAX && previous != usize::MAX {
757                    on_pinned(previous, value);
758                }
759                return previous;
760            }
761            Err(observed) => current = observed,
762        }
763    }
764}
765
766#[cfg(test)]
767mod pinning_atomic_add_usize_with_order_tests {
768    use super::*;
769
770    #[test]
771    fn pinning_atomic_add_usize_pins_without_wrapping_and_returns_previous() {
772        let counter = AtomicUsize::new(usize::MAX - 1);
773        let mut pinned = None;
774
775        let previous = pinning_atomic_add_usize_with_order(
776            &counter,
777            2,
778            Ordering::AcqRel,
779            Ordering::Acquire,
780            |observed, value| pinned = Some((observed, value)),
781        );
782
783        assert_eq!(previous, usize::MAX - 1);
784        assert_eq!(counter.load(Ordering::Acquire), usize::MAX);
785        assert_eq!(pinned, Some((usize::MAX - 1, 2)));
786
787        let mut called_again = false;
788        let previous = pinning_atomic_add_usize_with_order(
789            &counter,
790            1,
791            Ordering::AcqRel,
792            Ordering::Acquire,
793            |_, _| called_again = true,
794        );
795
796        assert_eq!(previous, usize::MAX);
797        assert_eq!(counter.load(Ordering::Acquire), usize::MAX);
798        assert!(!called_again);
799    }
800
801    #[test]
802    fn repair_atomic_sub_usize_fetch_repairs_and_returns_observed() {
803        let counter = AtomicUsize::new(3);
804        let mut repair = None;
805
806        let previous = repair_atomic_sub_usize_fetch_with_order(
807            &counter,
808            5,
809            Ordering::Acquire,
810            Ordering::AcqRel,
811            Ordering::Acquire,
812            |observed, value| repair = Some((observed, value)),
813        );
814
815        assert_eq!(previous, 3);
816        assert_eq!(counter.load(Ordering::Acquire), 0);
817        assert_eq!(repair, Some((3, 5)));
818    }
819}
820
821/// Increment a `u64` atomic counter, pinning it at `u64::MAX` instead of wrapping.
822///
823/// Returns `true` when the counter was incremented and `false` when it was
824/// already pinned. `on_pinned` is called exactly once on the pinned path.
825pub fn pinning_atomic_increment_u64(
826    counter: &AtomicU64,
827    success_order: Ordering,
828    failure_order: Ordering,
829    on_pinned: impl FnOnce(),
830) -> bool {
831    let mut current = counter.load(failure_order);
832    loop {
833        let Some(next) = current.checked_add(1) else {
834            on_pinned();
835            return false;
836        };
837        match counter.compare_exchange_weak(current, next, success_order, failure_order) {
838            Ok(_) => return true,
839            Err(observed) => current = observed,
840        }
841    }
842}
843
844/// Increment a `u32` atomic counter, pinning it at `u32::MAX` instead of wrapping.
845///
846/// Returns `true` when the counter was incremented and `false` when it was
847/// already pinned. `on_pinned` is called exactly once on the pinned path.
848pub fn pinning_atomic_increment_u32(
849    counter: &AtomicU32,
850    success_order: Ordering,
851    failure_order: Ordering,
852    on_pinned: impl FnOnce(),
853) -> bool {
854    let mut current = counter.load(failure_order);
855    loop {
856        let Some(next) = current.checked_add(1) else {
857            on_pinned();
858            return false;
859        };
860        match counter.compare_exchange_weak(current, next, success_order, failure_order) {
861            Ok(_) => return true,
862            Err(observed) => current = observed,
863        }
864    }
865}
866
867/// Allocate the current `u64` atomic sequence value and publish the next value.
868///
869/// When incrementing would overflow, publishes `rebase_to` instead of wrapping.
870/// Returns the allocated value observed before the publish. `on_rebase` is
871/// called exactly once for each successful overflow rebase.
872pub fn rebasing_atomic_next_u64(
873    counter: &AtomicU64,
874    rebase_to: u64,
875    load_order: Ordering,
876    success_order: Ordering,
877    failure_order: Ordering,
878    mut on_rebase: impl FnMut(u64, u64),
879) -> u64 {
880    let mut observed = counter.load(load_order);
881    loop {
882        let next = match observed.checked_add(1) {
883            Some(next) => next,
884            None => rebase_to,
885        };
886        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
887            Ok(_) => {
888                if next == rebase_to && observed == u64::MAX {
889                    on_rebase(observed, rebase_to);
890                }
891                return observed;
892            }
893            Err(actual) => observed = actual,
894        }
895    }
896}
897
898/// Allocate the current `u64` atomic sequence value and publish `current + 1`.
899///
900/// # Errors
901///
902/// Returns `E` from `overflow` when the sequence cannot advance without
903/// wrapping.
904pub fn checked_atomic_next_u64_with_order<E>(
905    counter: &AtomicU64,
906    load_order: Ordering,
907    success_order: Ordering,
908    failure_order: Ordering,
909    overflow: impl Fn(u64) -> E,
910) -> Result<u64, E> {
911    let mut observed = counter.load(load_order);
912    loop {
913        let next = observed.checked_add(1).ok_or_else(|| overflow(observed))?;
914        match counter.compare_exchange_weak(observed, next, success_order, failure_order) {
915            Ok(_) => return Ok(observed),
916            Err(actual) => observed = actual,
917        }
918    }
919}
920
921/// Raise a `u64` atomic counter to at least `value` using one atomic max update.
922///
923/// Returns the previous value observed by the atomic operation.
924
925pub fn atomic_max_u64(counter: &AtomicU64, value: u64, order: Ordering) -> u64 {
926    counter.fetch_max(value, order)
927}
928
929/// Increment a `u64` scalar counter, pinning it at `u64::MAX` instead of wrapping.
930///
931/// Returns `true` when the counter was incremented and `false` when it was
932/// already pinned. `on_pinned` is called exactly once on the pinned path.
933pub fn pinning_increment_u64(counter: &mut u64, on_pinned: impl FnOnce()) -> bool {
934    match counter.checked_add(1) {
935        Some(next) => {
936            *counter = next;
937            true
938        }
939        None => {
940            on_pinned();
941            *counter = u64::MAX;
942            false
943        }
944    }
945}
946
947#[cfg(test)]
948mod tests {
949    use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
950
951    use super::{
952        atomic_max_u64, checked_add_u32_count, checked_add_u32_value, checked_add_u64_count,
953        checked_add_u64_lazy, checked_add_u64_value, checked_add_usize_count,
954        checked_add_usize_lazy, checked_add_usize_value, checked_atomic_add_u64,
955        checked_atomic_add_u64_guarded_with_order, checked_atomic_add_u64_with_order,
956        checked_atomic_add_usize, checked_atomic_add_usize_guarded_with_order,
957        checked_atomic_add_usize_with_order, checked_atomic_next_u64_with_order,
958        checked_atomic_sub_u64, checked_atomic_sub_u64_with_order, checked_atomic_sub_usize,
959        checked_atomic_sub_usize_with_order, checked_mul_u64_count, checked_mul_u64_value,
960        checked_mul_usize_lazy, checked_sub_u64_count, checked_sub_u64_value,
961        pinning_atomic_increment_u32, pinning_atomic_increment_u64, pinning_increment_u64,
962        rebasing_atomic_next_u64, repair_atomic_sub_usize_with_order, ArithmeticOverflow,
963    };
964
965    #[derive(Debug, Eq, PartialEq)]
966    enum ArithmeticError {
967        Overflow(&'static str),
968    }
969
970    impl ArithmeticOverflow for ArithmeticError {
971        fn arithmetic_overflow(field: &'static str) -> Self {
972            Self::Overflow(field)
973        }
974    }
975
976    #[test]
977    fn checked_value_helpers_preserve_domain_errors() {
978        assert_eq!(checked_add_u64_value(2, 3, "overflow"), Ok(5));
979        assert_eq!(checked_mul_u64_value(2, 3, "overflow"), Ok(6));
980        assert_eq!(checked_sub_u64_value(5, 3, "underflow"), Ok(2));
981        assert_eq!(checked_add_usize_value(2, 3, "overflow"), Ok(5));
982        assert_eq!(checked_add_u32_value(2, 3, "overflow"), Ok(5));
983
984        assert_eq!(
985            checked_add_u64_value(u64::MAX, 1, "overflow"),
986            Err("overflow")
987        );
988        assert_eq!(
989            checked_mul_u64_value(u64::MAX, 2, "overflow"),
990            Err("overflow")
991        );
992        assert_eq!(checked_sub_u64_value(0, 1, "underflow"), Err("underflow"));
993        assert_eq!(
994            checked_add_usize_value(usize::MAX, 1, "overflow"),
995            Err("overflow")
996        );
997        assert_eq!(
998            checked_add_u32_value(u32::MAX, 1, "overflow"),
999            Err("overflow")
1000        );
1001    }
1002
1003    #[test]
1004    fn checked_add_usize_lazy_does_not_build_success_error() {
1005        let mut constructed = false;
1006
1007        assert_eq!(
1008            checked_add_usize_lazy(2, 3, || {
1009                constructed = true;
1010                "overflow"
1011            }),
1012            Ok(5)
1013        );
1014        assert!(
1015            !constructed,
1016            "Fix: hot-path checked usize accounting must not construct error strings on success."
1017        );
1018        assert_eq!(
1019            checked_add_usize_lazy(usize::MAX, 1, || "overflow"),
1020            Err("overflow")
1021        );
1022    }
1023
1024    #[test]
1025    fn checked_add_u64_lazy_does_not_build_success_error() {
1026        let mut constructed = false;
1027
1028        assert_eq!(
1029            checked_add_u64_lazy(2, 3, || {
1030                constructed = true;
1031                "overflow"
1032            }),
1033            Ok(5)
1034        );
1035        assert!(
1036            !constructed,
1037            "Fix: hot-path checked u64 accounting must not construct error strings on success."
1038        );
1039        assert_eq!(
1040            checked_add_u64_lazy(u64::MAX, 1, || "overflow"),
1041            Err("overflow")
1042        );
1043    }
1044
1045    #[test]
1046    fn checked_mul_usize_lazy_does_not_build_success_error() {
1047        let mut constructed = false;
1048
1049        assert_eq!(
1050            checked_mul_usize_lazy(2, 3, || {
1051                constructed = true;
1052                "overflow"
1053            }),
1054            Ok(6)
1055        );
1056        assert!(
1057            !constructed,
1058            "Fix: hot-path checked usize multiplication must not construct error strings on success."
1059        );
1060        assert_eq!(
1061            checked_mul_usize_lazy(usize::MAX, 2, || "overflow"),
1062            Err("overflow")
1063        );
1064    }
1065
1066    #[test]
1067    fn typed_checked_arithmetic_helpers_preserve_domain_error_fields() {
1068        assert_eq!(
1069            checked_add_u64_count::<ArithmeticError>(u64::MAX, 1, "u64 add"),
1070            Err(ArithmeticError::Overflow("u64 add"))
1071        );
1072        assert_eq!(
1073            checked_mul_u64_count::<ArithmeticError>(u64::MAX, 2, "u64 mul"),
1074            Err(ArithmeticError::Overflow("u64 mul"))
1075        );
1076        assert_eq!(
1077            checked_sub_u64_count::<ArithmeticError>(0, 1, "u64 sub"),
1078            Err(ArithmeticError::Overflow("u64 sub"))
1079        );
1080        assert_eq!(
1081            checked_add_usize_count::<ArithmeticError>(usize::MAX, 1, "usize add"),
1082            Err(ArithmeticError::Overflow("usize add"))
1083        );
1084        assert_eq!(
1085            checked_add_u32_count::<ArithmeticError>(u32::MAX, 1, "u32 add"),
1086            Err(ArithmeticError::Overflow("u32 add"))
1087        );
1088    }
1089
1090    #[test]
1091    fn generated_checked_arithmetic_matrix_matches_primitive_semantics() {
1092        const VALUES: [u64; 12] = [
1093            0,
1094            1,
1095            2,
1096            3,
1097            7,
1098            31,
1099            255,
1100            1024,
1101            u32::MAX as u64,
1102            u64::MAX / 2,
1103            u64::MAX - 1,
1104            u64::MAX,
1105        ];
1106
1107        for lhs in VALUES {
1108            for rhs in VALUES {
1109                assert_eq!(
1110                    checked_add_u64_value(lhs, rhs, "overflow").ok(),
1111                    lhs.checked_add(rhs)
1112                );
1113                assert_eq!(
1114                    checked_mul_u64_value(lhs, rhs, "overflow").ok(),
1115                    lhs.checked_mul(rhs)
1116                );
1117                assert_eq!(
1118                    checked_sub_u64_value(lhs, rhs, "underflow").ok(),
1119                    lhs.checked_sub(rhs)
1120                );
1121            }
1122        }
1123    }
1124
1125    #[test]
1126    fn checked_atomic_accounting_reports_overflow_and_underflow_without_saturation() {
1127        let add_counter = AtomicU64::new(u64::MAX - 1);
1128        checked_atomic_add_u64(&add_counter, 1, |_, _| unreachable!("one fits"))
1129            .expect("Fix: atomic add should accept exact non-overflow");
1130        assert_eq!(add_counter.load(Ordering::Relaxed), u64::MAX);
1131        let add_error = checked_atomic_add_u64(&add_counter, 1, |current, attempted| {
1132            crate::BackendError::InvalidProgram {
1133                fix: format!("Fix: overflow {current} {attempted}"),
1134            }
1135        })
1136        .expect_err("overflowing atomic add should fail");
1137        assert!(add_error.to_string().contains("overflow"));
1138        assert_eq!(add_counter.load(Ordering::Relaxed), u64::MAX);
1139
1140        let sub_counter = AtomicU64::new(1);
1141        checked_atomic_sub_u64(&sub_counter, 1, |_, _| unreachable!("one fits"))
1142            .expect("Fix: atomic sub should accept exact subtraction");
1143        assert_eq!(sub_counter.load(Ordering::Acquire), 0);
1144        let sub_error = checked_atomic_sub_u64(&sub_counter, 1, |current, attempted| {
1145            crate::BackendError::InvalidProgram {
1146                fix: format!("Fix: underflow {current} {attempted}"),
1147            }
1148        })
1149        .expect_err("underflowing atomic sub should fail");
1150        assert!(sub_error.to_string().contains("underflow"));
1151        assert_eq!(sub_counter.load(Ordering::Acquire), 0);
1152
1153        let usize_add_counter = AtomicUsize::new(usize::MAX - 1);
1154        checked_atomic_add_usize(&usize_add_counter, 1, |_, _| unreachable!("one fits"))
1155            .expect("Fix: usize atomic add should accept exact non-overflow");
1156        assert_eq!(usize_add_counter.load(Ordering::Acquire), usize::MAX);
1157        let usize_add_error =
1158            checked_atomic_add_usize(&usize_add_counter, 1, |current, attempted| {
1159                crate::BackendError::InvalidProgram {
1160                    fix: format!("Fix: usize overflow {current} {attempted}"),
1161                }
1162            })
1163            .expect_err("overflowing usize atomic add should fail");
1164        assert!(usize_add_error.to_string().contains("usize overflow"));
1165        assert_eq!(usize_add_counter.load(Ordering::Acquire), usize::MAX);
1166
1167        let usize_counter = AtomicUsize::new(0);
1168        let usize_error = checked_atomic_sub_usize(&usize_counter, 1, |current, attempted| {
1169            crate::BackendError::InvalidProgram {
1170                fix: format!("Fix: usize underflow {current} {attempted}"),
1171            }
1172        })
1173        .expect_err("underflowing usize atomic sub should fail");
1174        assert!(usize_error.to_string().contains("usize underflow"));
1175        assert_eq!(usize_counter.load(Ordering::Acquire), 0);
1176    }
1177
1178    #[test]
1179    fn ordered_atomic_helpers_preserve_domain_errors() {
1180        let add_counter = AtomicU64::new(40);
1181        checked_atomic_add_u64_with_order(
1182            &add_counter,
1183            2,
1184            Ordering::Acquire,
1185            Ordering::AcqRel,
1186            Ordering::Acquire,
1187            |_, _| "overflow",
1188        )
1189        .expect("Fix: reject adds that would overflow; use checked accounting API on hostile sizes - ordered atomic add should accept non-overflow");
1190        assert_eq!(add_counter.load(Ordering::Acquire), 42);
1191
1192        let sub_counter = AtomicU64::new(42);
1193        checked_atomic_sub_u64_with_order(
1194            &sub_counter,
1195            2,
1196            Ordering::Acquire,
1197            Ordering::AcqRel,
1198            Ordering::Acquire,
1199            |_, _| "underflow",
1200        )
1201        .expect("Fix: reject subs that would underflow; use checked accounting API on hostile sizes - ordered atomic sub should accept non-underflow");
1202        assert_eq!(sub_counter.load(Ordering::Acquire), 40);
1203
1204        let usize_counter = AtomicUsize::new(10);
1205        checked_atomic_add_usize_with_order(
1206            &usize_counter,
1207            5,
1208            Ordering::Acquire,
1209            Ordering::AcqRel,
1210            Ordering::Acquire,
1211            |_, _| "usize overflow",
1212        )
1213        .expect("Fix: reject usize atomics that overflow/underflow; return Err from guarded helpers - ordered usize atomic add should accept non-overflow");
1214        assert_eq!(usize_counter.load(Ordering::Acquire), 15);
1215        checked_atomic_sub_usize_with_order(
1216            &usize_counter,
1217            3,
1218            Ordering::Acquire,
1219            Ordering::AcqRel,
1220            Ordering::Acquire,
1221            |_, _| "usize underflow",
1222        )
1223        .expect("Fix: reject usize atomics that overflow/underflow; return Err from guarded helpers - ordered usize atomic sub should accept non-underflow");
1224        assert_eq!(usize_counter.load(Ordering::Acquire), 12);
1225    }
1226
1227    #[test]
1228    fn guarded_atomic_add_helpers_validate_next_value_before_publish() {
1229        let u64_counter = AtomicU64::new(8);
1230        let u64_error = checked_atomic_add_u64_guarded_with_order(
1231            &u64_counter,
1232            5,
1233            Ordering::Acquire,
1234            Ordering::AcqRel,
1235            Ordering::Acquire,
1236            |_, _| "overflow",
1237            |next| {
1238                if next <= 12 {
1239                    Ok(())
1240                } else {
1241                    Err("budget")
1242                }
1243            },
1244        )
1245        .expect_err("guarded u64 add should reject over-budget next value");
1246        assert_eq!(u64_error, "budget");
1247        assert_eq!(u64_counter.load(Ordering::Acquire), 8);
1248
1249        checked_atomic_add_u64_guarded_with_order(
1250            &u64_counter,
1251            4,
1252            Ordering::Acquire,
1253            Ordering::AcqRel,
1254            Ordering::Acquire,
1255            |_, _| "overflow",
1256            |next| if next <= 12 { Ok(()) } else { Err("budget") },
1257        )
1258        .expect("Fix: reject guarded adds that overflow; surface Err to caller instead of panicking - guarded u64 add should publish accepted next value");
1259        assert_eq!(u64_counter.load(Ordering::Acquire), 12);
1260
1261        let usize_counter = AtomicUsize::new(3);
1262        let usize_error = checked_atomic_add_usize_guarded_with_order(
1263            &usize_counter,
1264            2,
1265            Ordering::Acquire,
1266            Ordering::AcqRel,
1267            Ordering::Acquire,
1268            |_, _| "overflow",
1269            |next| {
1270                if next < 5 {
1271                    Ok(())
1272                } else {
1273                    Err("usize budget")
1274                }
1275            },
1276        )
1277        .expect_err("guarded usize add should reject over-budget next value");
1278        assert_eq!(usize_error, "usize budget");
1279        assert_eq!(usize_counter.load(Ordering::Acquire), 3);
1280    }
1281
1282    #[test]
1283    fn pinning_atomic_increment_helpers_never_wrap() {
1284        let u64_counter = AtomicU64::new(u64::MAX - 1);
1285        assert!(pinning_atomic_increment_u64(
1286            &u64_counter,
1287            Ordering::Relaxed,
1288            Ordering::Relaxed,
1289            || unreachable!("first increment should fit")
1290        ));
1291        assert_eq!(u64_counter.load(Ordering::Relaxed), u64::MAX);
1292        let mut u64_pinned = false;
1293        assert!(!pinning_atomic_increment_u64(
1294            &u64_counter,
1295            Ordering::Relaxed,
1296            Ordering::Relaxed,
1297            || u64_pinned = true
1298        ));
1299        assert!(u64_pinned);
1300        assert_eq!(u64_counter.load(Ordering::Relaxed), u64::MAX);
1301
1302        let u32_counter = AtomicU32::new(u32::MAX - 1);
1303        assert!(pinning_atomic_increment_u32(
1304            &u32_counter,
1305            Ordering::Relaxed,
1306            Ordering::Relaxed,
1307            || unreachable!("first increment should fit")
1308        ));
1309        assert_eq!(u32_counter.load(Ordering::Relaxed), u32::MAX);
1310        let mut u32_pinned = false;
1311        assert!(!pinning_atomic_increment_u32(
1312            &u32_counter,
1313            Ordering::Relaxed,
1314            Ordering::Relaxed,
1315            || u32_pinned = true
1316        ));
1317        assert!(u32_pinned);
1318        assert_eq!(u32_counter.load(Ordering::Relaxed), u32::MAX);
1319
1320        let mut scalar_counter = u64::MAX - 1;
1321        assert!(pinning_increment_u64(&mut scalar_counter, || {
1322            unreachable!("first scalar increment should fit")
1323        }));
1324        assert_eq!(scalar_counter, u64::MAX);
1325        let mut scalar_pinned = false;
1326        assert!(!pinning_increment_u64(&mut scalar_counter, || {
1327            scalar_pinned = true;
1328        }));
1329        assert!(scalar_pinned);
1330        assert_eq!(scalar_counter, u64::MAX);
1331    }
1332
1333    #[test]
1334    fn atomic_max_helper_raises_without_lowering() {
1335        let counter = AtomicU64::new(10);
1336        assert_eq!(atomic_max_u64(&counter, 42, Ordering::Relaxed), 10);
1337        assert_eq!(counter.load(Ordering::Relaxed), 42);
1338        assert_eq!(atomic_max_u64(&counter, 7, Ordering::Relaxed), 42);
1339        assert_eq!(counter.load(Ordering::Relaxed), 42);
1340    }
1341
1342    #[test]
1343    fn rebasing_atomic_next_returns_observed_and_rebases_on_overflow() {
1344        let counter = AtomicU64::new(7);
1345        let mut rebase_count = 0;
1346        assert_eq!(
1347            rebasing_atomic_next_u64(
1348                &counter,
1349                1,
1350                Ordering::Acquire,
1351                Ordering::AcqRel,
1352                Ordering::Acquire,
1353                |_, _| rebase_count += 1,
1354            ),
1355            7
1356        );
1357        assert_eq!(counter.load(Ordering::Acquire), 8);
1358        assert_eq!(rebase_count, 0);
1359
1360        counter.store(u64::MAX, Ordering::Release);
1361        assert_eq!(
1362            rebasing_atomic_next_u64(
1363                &counter,
1364                1,
1365                Ordering::Acquire,
1366                Ordering::AcqRel,
1367                Ordering::Acquire,
1368                |observed, rebase_to| {
1369                    assert_eq!(observed, u64::MAX);
1370                    assert_eq!(rebase_to, 1);
1371                    rebase_count += 1;
1372                },
1373            ),
1374            u64::MAX
1375        );
1376        assert_eq!(counter.load(Ordering::Acquire), 1);
1377        assert_eq!(rebase_count, 1);
1378    }
1379
1380    #[test]
1381    fn checked_atomic_next_returns_observed_and_rejects_wraparound() {
1382        let counter = AtomicU64::new(41);
1383        assert_eq!(
1384            checked_atomic_next_u64_with_order(
1385                &counter,
1386                Ordering::Acquire,
1387                Ordering::AcqRel,
1388                Ordering::Acquire,
1389                |_| "overflow",
1390            )
1391            .expect("Fix: allocation of next atomic value must not overflow; return None/Err on hostile input - checked atomic next should allocate non-overflowing value"),
1392            41
1393        );
1394        assert_eq!(counter.load(Ordering::Acquire), 42);
1395
1396        counter.store(u64::MAX, Ordering::Release);
1397        let error = checked_atomic_next_u64_with_order(
1398            &counter,
1399            Ordering::Acquire,
1400            Ordering::AcqRel,
1401            Ordering::Acquire,
1402            |observed| {
1403                assert_eq!(observed, u64::MAX);
1404                "overflow"
1405            },
1406        )
1407        .expect_err("checked atomic next should reject u64 wraparound");
1408        assert_eq!(error, "overflow");
1409        assert_eq!(counter.load(Ordering::Acquire), u64::MAX);
1410    }
1411
1412    #[test]
1413    fn repair_atomic_sub_usize_repairs_underflow_to_zero_once() {
1414        let counter = AtomicUsize::new(10);
1415        let mut repairs = 0;
1416        repair_atomic_sub_usize_with_order(
1417            &counter,
1418            4,
1419            Ordering::Acquire,
1420            Ordering::AcqRel,
1421            Ordering::Acquire,
1422            |_, _| repairs += 1,
1423        );
1424        assert_eq!(counter.load(Ordering::Acquire), 6);
1425        assert_eq!(repairs, 0);
1426
1427        repair_atomic_sub_usize_with_order(
1428            &counter,
1429            99,
1430            Ordering::Acquire,
1431            Ordering::AcqRel,
1432            Ordering::Acquire,
1433            |observed, attempted| {
1434                assert_eq!(observed, 6);
1435                assert_eq!(attempted, 99);
1436                repairs += 1;
1437            },
1438        );
1439        assert_eq!(counter.load(Ordering::Acquire), 0);
1440        assert_eq!(repairs, 1);
1441    }
1442}