use crate::ShutdownToken;
use std::time::Duration;
pub enum Step {
Stop,
Reconnect,
}
#[derive(Debug, Clone)]
pub struct Backoff {
current: Duration,
max: Duration,
}
impl Backoff {
pub fn new(initial: Duration, max: Duration) -> Self {
Self { current: initial, max }
}
pub fn current(&self) -> Duration {
self.current
}
pub fn advance(&mut self) {
self.current = (self.current * 2).min(self.max);
}
}
pub async fn run<C, Fut, J>(
shutdown: &ShutdownToken,
mut backoff: Backoff,
mut jitter: J,
mut connect_once: C,
) -> nagisa_types::error::Result<()>
where
C: FnMut() -> Fut,
Fut: std::future::Future<Output = Step>,
J: FnMut(u64) -> u64,
{
loop {
if shutdown.is_cancelled() {
return Ok(());
}
match connect_once().await {
Step::Stop => return Ok(()),
Step::Reconnect => {
if shutdown.is_cancelled() {
return Ok(());
}
let base = backoff.current();
let extra = jitter(base.as_millis() as u64);
let sleep = base + Duration::from_millis(extra);
tokio::select! {
biased;
_ = shutdown.cancelled() => return Ok(()),
_ = tokio::time::sleep(sleep) => {}
}
backoff.advance();
}
}
}
}