use core::cell::RefCell;
use core::fmt;
use crate::compat::Duration;
use crate::state::RetryState;
use super::Wait;
#[cfg(target_has_atomic = "ptr")]
use core::sync::atomic::{AtomicUsize, Ordering};
const DEFAULT_JITTER_SEED: u64 = 0x5A5A_5A5A_5A5A_5A5A;
#[cfg(target_has_atomic = "ptr")]
static JITTER_NONCE_COUNTER: AtomicUsize = AtomicUsize::new(1);
struct SplitMix64 {
state: u64,
}
impl SplitMix64 {
fn new(seed: u64) -> Self {
Self { state: seed }
}
fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^ (z >> 31)
}
fn next_bounded(&mut self, max: u64) -> u64 {
if max == u64::MAX {
return self.next_u64();
}
let range = max + 1;
self.next_u64() % range
}
}
impl Clone for SplitMix64 {
fn clone(&self) -> Self {
Self { state: self.state }
}
}
impl fmt::Debug for SplitMix64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SplitMix64").finish_non_exhaustive()
}
}
fn seeded_rng(seed: u64, nonce: u64) -> SplitMix64 {
SplitMix64::new(seed ^ nonce)
}
#[derive(Debug, Clone, Copy)]
enum JitterKind {
Additive(Duration),
Full,
Equal,
}
#[derive(Debug)]
pub struct Jittered<W> {
inner: W,
kind: JitterKind,
seed: u64,
nonce: u64,
rng: RefCell<SplitMix64>,
}
impl<W> Jittered<W> {
fn new(inner: W, kind: JitterKind) -> Self {
let nonce = next_jitter_nonce();
Self {
inner,
kind,
seed: DEFAULT_JITTER_SEED,
nonce,
rng: RefCell::new(seeded_rng(DEFAULT_JITTER_SEED, nonce)),
}
}
pub(super) fn additive(inner: W, max_jitter: Duration) -> Self {
Self::new(inner, JitterKind::Additive(max_jitter))
}
pub(super) fn full(inner: W) -> Self {
Self::new(inner, JitterKind::Full)
}
pub(super) fn equal(inner: W) -> Self {
Self::new(inner, JitterKind::Equal)
}
#[must_use]
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = seed;
self.rng = RefCell::new(seeded_rng(seed, self.nonce));
self
}
#[must_use]
pub fn with_nonce(mut self, nonce: u64) -> Self {
self.nonce = nonce;
self.rng = RefCell::new(seeded_rng(self.seed, nonce));
self
}
}
impl<W: Clone> Clone for Jittered<W> {
fn clone(&self) -> Self {
let nonce = next_jitter_nonce();
Self {
inner: self.inner.clone(),
kind: self.kind,
seed: self.seed,
nonce,
rng: RefCell::new(seeded_rng(self.seed, nonce)),
}
}
}
impl<W: Wait> Wait for Jittered<W> {
fn next_wait(&self, state: &RetryState) -> Duration {
let base = self.inner.next_wait(state);
match self.kind {
JitterKind::Additive(max_jitter) => {
let jitter = random_jitter_duration(max_jitter, &self.rng);
base.saturating_add(jitter)
}
JitterKind::Full => random_jitter_duration(base, &self.rng),
JitterKind::Equal => {
let half = base / 2;
let jitter = random_jitter_duration(half, &self.rng);
half.saturating_add(jitter)
}
}
}
}
#[derive(Debug)]
pub struct WaitDecorrelatedJitter {
base: Duration,
last_sleep: core::cell::Cell<Duration>,
seed: u64,
nonce: u64,
rng: RefCell<SplitMix64>,
}
#[must_use]
pub fn decorrelated_jitter(base: Duration) -> WaitDecorrelatedJitter {
let nonce = next_jitter_nonce();
WaitDecorrelatedJitter {
base,
last_sleep: core::cell::Cell::new(base),
seed: DEFAULT_JITTER_SEED,
nonce,
rng: RefCell::new(seeded_rng(DEFAULT_JITTER_SEED, nonce)),
}
}
impl WaitDecorrelatedJitter {
#[must_use]
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = seed;
self.rng = RefCell::new(seeded_rng(seed, self.nonce));
self
}
#[must_use]
pub fn with_nonce(mut self, nonce: u64) -> Self {
self.nonce = nonce;
self.rng = RefCell::new(seeded_rng(self.seed, nonce));
self
}
}
impl Clone for WaitDecorrelatedJitter {
fn clone(&self) -> Self {
let nonce = next_jitter_nonce();
Self {
base: self.base,
last_sleep: self.last_sleep.clone(),
seed: self.seed,
nonce,
rng: RefCell::new(seeded_rng(self.seed, nonce)),
}
}
}
impl Wait for WaitDecorrelatedJitter {
fn next_wait(&self, _state: &RetryState) -> Duration {
let last = self.last_sleep.get();
let upper = last.saturating_mul(3);
let lower = self.base;
let delay = if upper <= lower {
lower
} else {
let range_nanos = upper.as_nanos().saturating_sub(lower.as_nanos());
if range_nanos == 0 {
lower
} else {
let max_nanos = range_nanos.min(u128::from(u64::MAX)) as u64;
let random = self.rng.borrow_mut().next_bounded(max_nanos);
lower.saturating_add(Duration::from_nanos(random))
}
};
self.last_sleep.set(delay);
delay
}
}
fn random_jitter_duration(max_jitter: Duration, rng: &RefCell<SplitMix64>) -> Duration {
if max_jitter.is_zero() {
return Duration::ZERO;
}
const MAX_JITTER_NANOS: u128 = u64::MAX as u128;
let upper = max_jitter.as_nanos().min(MAX_JITTER_NANOS) as u64;
let random = rng.borrow_mut().next_bounded(upper);
Duration::from_nanos(random)
}
#[cfg(target_has_atomic = "ptr")]
fn next_jitter_nonce() -> u64 {
let counter = JITTER_NONCE_COUNTER.fetch_add(1, Ordering::Relaxed) as u64;
#[cfg(feature = "std")]
{
use std::time::{Duration as StdDuration, SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(StdDuration::ZERO);
counter ^ (now.as_nanos() as u64)
}
#[cfg(not(feature = "std"))]
{
counter
}
}
#[cfg(not(target_has_atomic = "ptr"))]
fn next_jitter_nonce() -> u64 {
1
}