use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
pub const IDLE_TIMEOUT: Duration = Duration::from_secs(60);
pub const PING_INTERVAL: Duration = Duration::from_secs(30);
pub const PING_TIMEOUT: Duration = Duration::from_secs(5);
pub const STREAM_OPEN_TIMEOUT: Duration = Duration::from_secs(5);
pub const SHUTDOWN_DRAIN_TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Clone)]
pub struct ActivityTracker {
last_activity_ms: Arc<AtomicU64>,
created: Instant,
}
impl ActivityTracker {
pub fn new() -> Self {
Self {
last_activity_ms: Arc::new(AtomicU64::new(0)),
created: Instant::now(),
}
}
#[inline]
pub fn record_activity(&self) {
let now_ms = self.created.elapsed().as_millis() as u64;
self.last_activity_ms.store(now_ms, Ordering::Relaxed);
}
#[inline]
pub fn idle_duration(&self) -> Duration {
let last_ms = self.last_activity_ms.load(Ordering::Relaxed);
let now_ms = self.created.elapsed().as_millis() as u64;
Duration::from_millis(now_ms.saturating_sub(last_ms))
}
#[inline]
pub fn is_idle(&self, threshold: Duration) -> bool {
self.idle_duration() >= threshold
}
}
impl Default for ActivityTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_activity_tracker_initial_state() {
let tracker = ActivityTracker::new();
assert!(!tracker.is_idle(Duration::from_millis(100)));
}
#[tokio::test]
async fn test_activity_tracker_becomes_idle() {
let tracker = ActivityTracker::new();
tokio::time::sleep(Duration::from_millis(50)).await;
assert!(tracker.is_idle(Duration::from_millis(40)));
assert!(!tracker.is_idle(Duration::from_millis(100)));
}
#[tokio::test]
async fn test_activity_tracker_reset() {
let tracker = ActivityTracker::new();
tokio::time::sleep(Duration::from_millis(50)).await;
assert!(tracker.is_idle(Duration::from_millis(40)));
tracker.record_activity();
assert!(!tracker.is_idle(Duration::from_millis(40)));
}
#[tokio::test]
async fn test_activity_tracker_clone_shares_state() {
let tracker1 = ActivityTracker::new();
let tracker2 = tracker1.clone();
tokio::time::sleep(Duration::from_millis(50)).await;
assert!(tracker1.is_idle(Duration::from_millis(40)));
assert!(tracker2.is_idle(Duration::from_millis(40)));
tracker1.record_activity();
assert!(!tracker2.is_idle(Duration::from_millis(40)));
}
}