refactor: optimize timer and network management with atomic operations

This commit is contained in:
Tunglies
2025-08-01 23:02:11 +08:00
Unverified
parent 569e2d5192
commit 3eb2a5b3ef
6 changed files with 58 additions and 62 deletions

View File

@@ -1,9 +1,14 @@
use crate::{config::Config, feat, logging, logging_error, utils::logging::Type};
use crate::{config::Config, feat, logging, logging_error, singleton, utils::logging::Type};
use anyhow::{Context, Result};
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
use once_cell::sync::OnceCell;
use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, sync::Arc};
use parking_lot::RwLock;
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
},
};
type TaskID = u64;
@@ -22,23 +27,24 @@ pub struct Timer {
/// save the current state - using RwLock for better read concurrency
pub timer_map: Arc<RwLock<HashMap<String, TimerTask>>>,
/// increment id - kept as mutex since it's just a counter
pub timer_count: Arc<Mutex<TaskID>>,
/// increment id - atomic counter for better performance
pub timer_count: AtomicU64,
/// Flag to mark if timer is initialized - atomic for better performance
pub initialized: Arc<std::sync::atomic::AtomicBool>,
pub initialized: AtomicBool,
}
impl Timer {
pub fn global() -> &'static Timer {
static TIMER: OnceCell<Timer> = OnceCell::new();
// Use singleton macro
singleton!(Timer, TIMER_INSTANCE);
TIMER.get_or_init(|| Timer {
impl Timer {
fn new() -> Self {
Timer {
delay_timer: Arc::new(RwLock::new(DelayTimerBuilder::default().build())),
timer_map: Arc::new(RwLock::new(HashMap::new())),
timer_count: Arc::new(Mutex::new(1)),
initialized: Arc::new(std::sync::atomic::AtomicBool::new(false)),
})
timer_count: AtomicU64::new(1),
initialized: AtomicBool::new(false),
}
}
/// Initialize timer with better error handling and atomic operations
@@ -46,12 +52,7 @@ impl Timer {
// Use compare_exchange for thread-safe initialization check
if self
.initialized
.compare_exchange(
false,
true,
std::sync::atomic::Ordering::SeqCst,
std::sync::atomic::Ordering::SeqCst,
)
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_err()
{
logging!(debug, Type::Timer, "Timer already initialized, skipping...");
@@ -63,8 +64,7 @@ impl Timer {
// Initialize timer tasks
if let Err(e) = self.refresh() {
// Reset initialization flag on error
self.initialized
.store(false, std::sync::atomic::Ordering::SeqCst);
self.initialized.store(false, Ordering::SeqCst);
logging_error!(Type::Timer, false, "Failed to initialize timer: {}", e);
return Err(e);
}
@@ -299,7 +299,8 @@ impl Timer {
}
// Find new tasks to add
let mut next_id = *self.timer_count.lock();
let mut next_id = self.timer_count.load(Ordering::Relaxed);
let original_id = next_id;
for (uid, &interval) in new_map.iter() {
if !timer_map.contains_key(uid) {
@@ -316,8 +317,8 @@ impl Timer {
}
// Update counter only if we added new tasks
if next_id > *self.timer_count.lock() {
*self.timer_count.lock() = next_id;
if next_id > original_id {
self.timer_count.store(next_id, Ordering::Relaxed);
}
logging!(debug, Type::Timer, "定时任务变更数量: {}", diff_map.len());

View File

@@ -8,11 +8,10 @@ pub fn use_script(
name: String,
) -> Result<(Mapping, Vec<(String, String)>)> {
use boa_engine::{native_function::NativeFunction, Context, JsValue, Source};
use parking_lot::Mutex;
use std::sync::Arc;
use std::{cell::RefCell, rc::Rc};
let mut context = Context::default();
let outputs = Arc::new(Mutex::new(vec![]));
let outputs = Rc::new(RefCell::new(vec![]));
let copy_outputs = outputs.clone();
unsafe {
@@ -25,7 +24,7 @@ pub fn use_script(
let level = level.to_std_string().unwrap();
let data = args.get(1).unwrap().to_string(context)?;
let data = data.to_std_string().unwrap();
let mut out = copy_outputs.lock();
let mut out = copy_outputs.borrow_mut();
out.push((level, data));
Ok(JsValue::undefined())
},
@@ -68,7 +67,7 @@ pub fn use_script(
// 直接解析JSON结果,不做其他解析
let res: Result<Mapping, Error> = parse_json_safely(&result);
let mut out = outputs.lock();
let mut out = outputs.borrow_mut();
match res {
Ok(config) => Ok((use_lowercase(config), out.to_vec())),
Err(err) => {

View File

@@ -162,6 +162,7 @@ mod app_init {
/// Setup plugins for the Tauri builder
pub fn setup_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
#[allow(unused_mut)]
let mut builder = builder
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_updater::Builder::new().build())

View File

@@ -254,10 +254,9 @@ fn setup_light_weight_timer() -> Result<()> {
// 获取task_id
let task_id = {
let mut timer_count = Timer::global().timer_count.lock();
let id = *timer_count;
*timer_count += 1;
id
Timer::global()
.timer_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
};
// 创建任务

View File

@@ -2,7 +2,10 @@ use anyhow::Result;
use parking_lot::Mutex;
use reqwest::{Client, ClientBuilder, Proxy, RequestBuilder, Response};
use std::{
sync::{Arc, Once},
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Once,
},
time::{Duration, Instant},
};
use tokio::runtime::{Builder, Runtime};
@@ -23,12 +26,12 @@ const POOL_IDLE_TIMEOUT: Duration = Duration::from_secs(15);
/// 网络管理器
pub struct NetworkManager {
runtime: Arc<Runtime>,
self_proxy_client: Arc<Mutex<Option<Client>>>,
system_proxy_client: Arc<Mutex<Option<Client>>>,
no_proxy_client: Arc<Mutex<Option<Client>>>,
self_proxy_client: Mutex<Option<Client>>,
system_proxy_client: Mutex<Option<Client>>,
no_proxy_client: Mutex<Option<Client>>,
init: Once,
last_connection_error: Arc<Mutex<Option<(Instant, String)>>>,
connection_error_count: Arc<Mutex<usize>>,
last_connection_error: Mutex<Option<(Instant, String)>>,
connection_error_count: AtomicUsize,
}
// Use singleton_lazy macro to replace lazy_static!
@@ -47,12 +50,12 @@ impl NetworkManager {
NetworkManager {
runtime: Arc::new(runtime),
self_proxy_client: Arc::new(Mutex::new(None)),
system_proxy_client: Arc::new(Mutex::new(None)),
no_proxy_client: Arc::new(Mutex::new(None)),
self_proxy_client: Mutex::new(None),
system_proxy_client: Mutex::new(None),
no_proxy_client: Mutex::new(None),
init: Once::new(),
last_connection_error: Arc::new(Mutex::new(None)),
connection_error_count: Arc::new(Mutex::new(0)),
last_connection_error: Mutex::new(None),
connection_error_count: AtomicUsize::new(0),
}
}
@@ -85,12 +88,11 @@ impl NetworkManager {
let mut last_error = self.last_connection_error.lock();
*last_error = Some((Instant::now(), error.to_string()));
let mut error_count = self.connection_error_count.lock();
*error_count += 1;
self.connection_error_count.fetch_add(1, Ordering::Relaxed);
}
fn should_reset_clients(&self) -> bool {
let error_count = *self.connection_error_count.lock();
let error_count = self.connection_error_count.load(Ordering::Relaxed);
let last_error = self.last_connection_error.lock();
if error_count > 5 {
@@ -120,10 +122,7 @@ impl NetworkManager {
let mut client = self.no_proxy_client.lock();
*client = None;
}
{
let mut error_count = self.connection_error_count.lock();
*error_count = 0;
}
self.connection_error_count.store(0, Ordering::Relaxed);
}
/// 创建带有自定义选项的HTTP请求

View File

@@ -15,10 +15,7 @@ use parking_lot::{Mutex, RwLock};
use percent_encoding::percent_decode_str;
use scopeguard;
use serde_yaml::Mapping;
use std::{
sync::Arc,
time::{Duration, Instant},
};
use std::time::{Duration, Instant};
use tauri::{AppHandle, Manager};
use tokio::net::TcpListener;
@@ -33,7 +30,7 @@ const DEFAULT_WIDTH: u32 = 940;
const DEFAULT_HEIGHT: u32 = 700;
// 添加全局UI准备就绪标志
static UI_READY: OnceCell<Arc<RwLock<bool>>> = OnceCell::new();
static UI_READY: OnceCell<RwLock<bool>> = OnceCell::new();
// 窗口创建锁,防止并发创建窗口
static WINDOW_CREATING: OnceCell<Mutex<(bool, Instant)>> = OnceCell::new();
@@ -63,18 +60,18 @@ impl Default for UiReadyState {
}
// 获取UI就绪状态细节
static UI_READY_STATE: OnceCell<Arc<UiReadyState>> = OnceCell::new();
static UI_READY_STATE: OnceCell<UiReadyState> = OnceCell::new();
fn get_window_creating_lock() -> &'static Mutex<(bool, Instant)> {
WINDOW_CREATING.get_or_init(|| Mutex::new((false, Instant::now())))
}
fn get_ui_ready() -> &'static Arc<RwLock<bool>> {
UI_READY.get_or_init(|| Arc::new(RwLock::new(false)))
fn get_ui_ready() -> &'static RwLock<bool> {
UI_READY.get_or_init(|| RwLock::new(false))
}
fn get_ui_ready_state() -> &'static Arc<UiReadyState> {
UI_READY_STATE.get_or_init(|| Arc::new(UiReadyState::default()))
fn get_ui_ready_state() -> &'static UiReadyState {
UI_READY_STATE.get_or_init(UiReadyState::default)
}
// 更新UI准备阶段