use std::sync::{Arc, Weak};
use std::time::Duration;
use crate::sync::atomic::{AtomicBool, Ordering};
#[must_use = "dropping the handle stops the watch; bind it, or call `.detach()` \
to watch for the rest of the process"]
#[derive(Debug)]
pub struct RemoteWatch {
running: Arc<AtomicBool>,
}
impl RemoteWatch {
pub fn new() -> Self {
Self {
running: Arc::new(AtomicBool::new(true)),
}
}
#[must_use]
pub fn watching(&self) -> Watching {
Watching {
running: Arc::downgrade(&self.running),
}
}
pub fn stop(&self) {
self.running.store(false, Ordering::Release);
}
#[must_use]
pub fn is_stopped(&self) -> bool {
!self.running.load(Ordering::Acquire)
}
pub fn detach(self) {
std::mem::forget(self);
}
}
impl Default for RemoteWatch {
fn default() -> Self {
Self::new()
}
}
impl Drop for RemoteWatch {
fn drop(&mut self) {
self.stop();
}
}
#[derive(Debug, Clone)]
pub struct Watching {
running: Weak<AtomicBool>,
}
impl Watching {
#[must_use]
pub fn keep_going(&self) -> bool {
self.running
.upgrade()
.is_some_and(|running| running.load(Ordering::Acquire))
}
pub fn sleep_for(&self, total: Duration) {
const SLICE: Duration = Duration::from_millis(250);
let mut slept = Duration::ZERO;
while slept < total && self.keep_going() {
std::thread::sleep(SLICE.min(total - slept));
slept += SLICE;
}
}
pub fn sleep_jittered(&self, total: Duration, pace: &mut Pace) {
self.sleep_for(pace.spread(total));
}
#[must_use]
pub fn forever() -> Self {
let running = Box::leak(Box::new(Arc::new(AtomicBool::new(true))));
Self {
running: Arc::downgrade(running),
}
}
}
#[derive(Debug, Clone)]
pub struct Pace {
interval: Duration,
ceiling: Duration,
failures: u32,
entropy: u64,
}
impl Pace {
const CEILING: Duration = Duration::from_secs(300);
#[must_use]
pub fn new(interval: Duration) -> Self {
Self {
interval,
ceiling: Self::CEILING,
failures: 0,
entropy: seed(),
}
}
#[must_use]
pub fn with_ceiling(mut self, ceiling: Duration) -> Self {
self.ceiling = ceiling;
self
}
pub fn succeeded(&mut self) {
self.failures = 0;
}
pub fn failed(&mut self) {
self.failures = self.failures.saturating_add(1);
}
#[must_use]
pub fn next_wait(&mut self) -> Duration {
let base = if self.failures == 0 {
self.interval
} else {
let factor = 1u32.checked_shl(self.failures.min(16)).unwrap_or(u32::MAX);
self.interval
.checked_mul(factor)
.unwrap_or(self.ceiling)
.min(self.ceiling)
};
self.spread(base)
}
pub fn wait(&mut self, watching: &Watching) {
let wait = self.next_wait();
watching.sleep_for(wait);
}
#[must_use]
pub fn spread(&mut self, base: Duration) -> Duration {
self.entropy = self
.entropy
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let quarter = base / 4;
let offset = quarter
.checked_mul(u32::try_from(self.entropy >> 33 & 0xFF).unwrap_or(0))
.unwrap_or(quarter)
/ 255;
if self.entropy & 1 == 0 {
base.saturating_add(offset)
} else {
base.saturating_sub(offset)
}
}
}
fn seed() -> u64 {
let since = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |elapsed| elapsed.as_nanos() as u64);
since ^ u64::from(std::process::id()).wrapping_mul(0x9E37_79B9_7F4A_7C15)
}