use crate::config::Config;
use crate::error::TimerError;
use crate::ntp;
use crate::timer::GlobalTimer;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tracing::{debug, info, warn};
lazy_static! {
static ref GLOBAL_TIMER: Arc<Mutex<Option<GlobalTimer>>> = Arc::new(Mutex::new(None));
static ref CONFIG: Arc<Mutex<Option<Arc<Config>>>> = Arc::new(Mutex::new(None));
}
pub struct TimerManager;
impl TimerManager {
pub async fn init_global_timer(config: Arc<Config>) -> Result<(), TimerError> {
config.validate().map_err(TimerError::ConfigError)?;
let mut config_guard = CONFIG
.lock()
.map_err(|e| TimerError::LockError(e.to_string()))?;
if config_guard.is_none() {
*config_guard = Some(Arc::clone(&config));
}
drop(config_guard);
let mut timer_guard = GLOBAL_TIMER
.lock()
.map_err(|e| TimerError::LockError(e.to_string()))?;
if timer_guard.is_none() {
let base_timestamp = ntp::get_ntp_timestamp(&config)?;
let timer = GlobalTimer::new(base_timestamp);
*timer_guard = Some(timer);
drop(timer_guard);
TimerManager::spawn_background_sync();
}
Ok(())
}
pub fn get_timestamp() -> Result<i64, TimerError> {
let timer_guard = GLOBAL_TIMER
.lock()
.map_err(|e| TimerError::LockError(e.to_string()))?;
match timer_guard.as_ref() {
Some(timer) => Ok(timer.get_current_timestamp()),
None => Err(TimerError::NotInitialized),
}
}
pub async fn manual_sync() -> Result<i64, TimerError> {
let config_opt = {
let config_guard = CONFIG
.lock()
.map_err(|e| TimerError::LockError(e.to_string()))?;
config_guard.as_ref().map(Arc::clone)
};
let config = config_opt.ok_or(TimerError::NotInitialized)?;
let new_ntp_timestamp = ntp::get_ntp_timestamp(&config)?;
let mut timer_guard = GLOBAL_TIMER
.lock()
.map_err(|e| TimerError::LockError(e.to_string()))?;
match timer_guard.as_mut() {
Some(timer) => {
if timer.detect_clock_jump(new_ntp_timestamp, config.clock_jump_threshold_secs) {
timer.update_timestamp(new_ntp_timestamp, true);
} else {
timer.update_timestamp(new_ntp_timestamp, false);
}
let timestamp = timer.get_current_timestamp();
info!(timestamp, "NTP 同步完成");
Ok(timestamp)
}
None => Err(TimerError::NotInitialized),
}
}
fn spawn_background_sync() {
tokio::spawn(async {
loop {
let sync_interval = {
let config_guard = CONFIG.lock().ok();
config_guard.and_then(|g| g.as_ref().map(|c| c.sync_interval_secs))
};
if let Some(interval) = sync_interval {
tokio::time::sleep(Duration::from_secs(interval)).await;
match TimerManager::manual_sync().await {
Ok(_) => {
debug!("后台同步完成");
}
Err(e) => {
warn!("后台同步失败,继续使用本地计时器: {}", e);
}
}
} else {
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
});
}
#[cfg(test)]
pub fn reset() {
if let Ok(mut timer_guard) = GLOBAL_TIMER.lock() {
*timer_guard = None;
}
if let Ok(mut config_guard) = CONFIG.lock() {
*config_guard = None;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_manual_sync_without_init() {
TimerManager::reset();
let result = TimerManager::manual_sync().await;
assert!(result.is_err());
}
}