use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Duration;
pub struct BusEventLimiter {
min_interval_ms: u64,
last_process_ms: AtomicU64,
in_progress: AtomicBool,
}
impl BusEventLimiter {
pub fn new(min_interval: Duration) -> Self {
Self {
min_interval_ms: min_interval.as_millis() as u64,
last_process_ms: AtomicU64::new(0),
in_progress: AtomicBool::new(false),
}
}
pub fn default_interval() -> Self {
Self::new(Duration::from_millis(500))
}
pub fn try_acquire(&self) -> bool {
let now = now_ms();
let last = self.last_process_ms.load(Ordering::Acquire);
if now.saturating_sub(last) < self.min_interval_ms {
return false;
}
if self
.in_progress
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
return false;
}
self.last_process_ms.store(now, Ordering::Release);
true
}
pub fn release(&self) {
self.in_progress.store(false, Ordering::Release);
}
pub fn reset(&self) {
self.last_process_ms.store(0, Ordering::Release);
self.in_progress.store(false, Ordering::Release);
}
}
fn now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_call_acquires() {
let limiter = BusEventLimiter::new(Duration::from_millis(100));
assert!(limiter.try_acquire());
limiter.release();
}
#[test]
fn test_rapid_second_call_rejected() {
let limiter = BusEventLimiter::new(Duration::from_millis(500));
assert!(limiter.try_acquire());
assert!(!limiter.try_acquire()); limiter.release();
}
#[test]
fn test_release_allows_next() {
let limiter = BusEventLimiter::new(Duration::from_millis(0));
assert!(limiter.try_acquire());
limiter.release();
std::thread::sleep(Duration::from_millis(1));
assert!(limiter.try_acquire());
limiter.release();
}
#[test]
fn test_reset_clears_state() {
let limiter = BusEventLimiter::new(Duration::from_millis(500));
assert!(limiter.try_acquire());
limiter.reset();
assert!(limiter.try_acquire());
limiter.release();
}
#[test]
fn test_default_interval() {
let limiter = BusEventLimiter::default_interval();
assert!(limiter.try_acquire());
limiter.release();
}
}