You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fixes a pre-existing concurrency bug in `exponentialBackoff.Next`, and
folds in the modernization follow-ups that missed the #40 merge.
## Overflow race
`Next` incremented the attempt counter with `Add(1)`, and on overflow
decremented it again (`Add(^uint64(0))`) to un-count the attempt. That
increment-then-decrement is not atomic as a unit, so under concurrent
calls the counter transiently runs past the overflow point and `base <<
attempt` wraps to a bogus positive instead of saturating at `MaxInt64`.
The counter now advances with a compare-and-swap that only commits when
the shift does not overflow. Once it does, `Next` saturates without
advancing, so concurrent callers converge on `MaxInt64`
deterministically.
This flake predates the Go 1.25 work: `TestExponentialBackoff/overflow`
failed ~8/100 runs under `-race` on the merged `main`, ~14/100 on the
modernization branch (the atomic-type swap was equivalent, so the rate
is unchanged within noise). With the fix, the overflow tests pass 0
failures across 60+ `-race` runs. A new
`TestExponentialBackoff_ConcurrentOverflow` fires 100 concurrent `Next`
calls and asserts every result stays on the doubling sequence or
`MaxInt64`.
## Modernization follow-ups
These were staged during the self-review of #40 but did not land before
it merged:
- README: the randomization note still described `math/rand` seeded from
the Unix timestamp; it is now `math/rand/v2` with an automatically
seeded top-level generator.
- Removed the blank lines left behind when the `tc := tc` copies were
dropped.
- Converted the index-free goroutine loops in the concurrent tests to
range-over-int.
- Updated the jitter guard comments to reference `rand.Int64N` instead
of the removed `Int63n`.
---------
Signed-off-by: Seth Vargo <seth@sethvargo.com>
0 commit comments