#![cfg_attr(feature = "tokio", doc = "```rust")]
#![cfg_attr(
not(feature = "tokio"),
doc = "```rust,ignore\n\
// Note: example not compiled if `tokio` feature is not enabled.\n"
)]
#![doc = include_str!("../examples/tokio-concurrent.rs")]
#![doc = "```"]
use crate::options::Options;
use rand::Rng;
use std::cmp;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct EaseOffCore {
options: Options,
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("{n}th retry is {:?} after deadline", retry_at.duration_since(*deadline))]
pub struct RetryAfterDeadline {
pub n: u32,
pub retry_at: Instant,
pub deadline: Instant,
}
impl EaseOffCore {
#[inline(always)]
pub const fn new(options: Options) -> Self {
Self { options }
}
pub fn nth_retry_at(
&self,
n: u32,
now: Instant,
deadline: Option<Instant>,
rng: &mut (impl Rng + ?Sized),
) -> Result<Option<Instant>, RetryAfterDeadline> {
let Options {
multiplier,
jitter,
initial_jitter,
initial_delay,
max_delay,
} = self.options;
let (delay, jitter) = if let Some(powi) = n.checked_sub(1) {
let delay = cmp::min(
duration_saturating_mul_f32(
initial_delay,
multiplier.powi(powi.try_into().unwrap_or(i32::MAX)),
),
max_delay,
);
let jitter = get_jitter(delay, jitter, rng);
(delay, jitter)
} else {
#[allow(clippy::neg_cmp_op_on_partial_ord)]
if !(initial_jitter > 0f32) {
return Ok(None);
}
let jitter = get_jitter(initial_delay, initial_jitter, rng);
(initial_delay, jitter)
};
let retry_at = now + delay - jitter;
match deadline {
Some(deadline) if retry_at > deadline => Err(RetryAfterDeadline {
n,
retry_at,
deadline,
}),
_ => Ok(Some(retry_at)),
}
}
}
#[inline(always)]
fn duration_saturating_mul_f32(duration: Duration, mul: f32) -> Duration {
Duration::try_from_secs_f32(duration.as_secs_f32() * mul).unwrap_or(Duration::MAX)
}
fn get_jitter(
base_duration: Duration,
jitter_factor: f32,
rng: &mut (impl Rng + ?Sized),
) -> Duration {
let jitter_factor = if jitter_factor > 0f32 && jitter_factor < 1f32 {
jitter_factor * rng.gen::<f32>()
} else if jitter_factor >= 1f32 {
rng.gen::<f32>()
} else {
0f32
};
duration_saturating_mul_f32(base_duration, jitter_factor)
}