use std::sync::atomic::{AtomicU32, Ordering};
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct BusyHandler {
timeout_ms: u32,
retry_count: AtomicU32,
start_time: Instant,
}
impl BusyHandler {
pub fn new(timeout_ms: u32) -> Self {
Self {
timeout_ms,
retry_count: AtomicU32::new(0),
start_time: Instant::now(),
}
}
pub fn handle_busy(&self) -> bool {
if self.timeout_ms == 0 {
return false;
}
let count = self.retry_count.fetch_add(1, Ordering::SeqCst);
let elapsed = self.start_time.elapsed().as_millis() as u64;
if elapsed >= self.timeout_ms as u64 {
return false;
}
let wait_ms = Self::calculate_wait_time(count);
let remaining = self.timeout_ms as u64 - elapsed;
let actual_wait = std::cmp::min(wait_ms, remaining);
std::thread::sleep(Duration::from_millis(actual_wait));
true
}
pub fn is_timeout(&self) -> bool {
if self.timeout_ms == 0 {
return true;
}
self.start_time.elapsed().as_millis() as u64 >= self.timeout_ms as u64
}
pub fn retry_count(&self) -> u32 {
self.retry_count.load(Ordering::SeqCst)
}
pub fn elapsed_ms(&self) -> u64 {
self.start_time.elapsed().as_millis() as u64
}
pub fn reset(&self) {
self.retry_count.store(0, Ordering::SeqCst);
}
fn calculate_wait_time(retry_count: u32) -> u64 {
let base = 1u64;
let max_wait = 100u64;
if retry_count >= 63 {
return max_wait;
}
std::cmp::min(base << retry_count, max_wait)
}
}
impl Clone for BusyHandler {
fn clone(&self) -> Self {
Self {
timeout_ms: self.timeout_ms,
retry_count: AtomicU32::new(0),
start_time: Instant::now(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusyResult {
Success,
Timeout,
Abort,
}
#[derive(Debug, Clone, Copy)]
pub struct BusyConfig {
pub timeout_ms: u32,
pub max_retries: u32,
}
impl BusyConfig {
pub fn new(timeout_ms: u32) -> Self {
Self {
timeout_ms,
max_retries: 0, }
}
pub fn with_max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}
pub fn no_wait() -> Self {
Self {
timeout_ms: 0,
max_retries: 0,
}
}
}
impl Default for BusyConfig {
fn default() -> Self {
Self {
timeout_ms: 5000, max_retries: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_busy_handler_no_wait() {
let handler = BusyHandler::new(0);
assert!(!handler.handle_busy());
assert!(handler.is_timeout());
}
#[test]
fn test_busy_handler_wait() {
let handler = BusyHandler::new(100); assert!(handler.handle_busy()); assert!(!handler.is_timeout());
assert_eq!(handler.retry_count(), 1);
}
#[test]
fn test_busy_handler_timeout() {
let handler = BusyHandler::new(1); std::thread::sleep(Duration::from_millis(2));
assert!(!handler.handle_busy()); assert!(handler.is_timeout());
}
#[test]
fn test_calculate_wait_time() {
assert_eq!(BusyHandler::calculate_wait_time(0), 1);
assert_eq!(BusyHandler::calculate_wait_time(1), 2);
assert_eq!(BusyHandler::calculate_wait_time(2), 4);
assert_eq!(BusyHandler::calculate_wait_time(6), 64);
assert_eq!(BusyHandler::calculate_wait_time(7), 100); assert_eq!(BusyHandler::calculate_wait_time(10), 100); }
#[test]
fn test_busy_config() {
let config = BusyConfig::default();
assert_eq!(config.timeout_ms, 5000);
assert_eq!(config.max_retries, 0);
let config = BusyConfig::new(1000).with_max_retries(10);
assert_eq!(config.timeout_ms, 1000);
assert_eq!(config.max_retries, 10);
let config = BusyConfig::no_wait();
assert_eq!(config.timeout_ms, 0);
}
}