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