diff --git a/Changelog.md b/Changelog.md index a5ebdc7c9..a066f3a6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -51,6 +51,7 @@ - 优化重启应用的资源清理逻辑 - 优化前端数据刷新 - 优化流量采样和数据处理 +- 优化应用重启/退出时的资源清理性能, 大幅缩短执行时间 diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index d3f01b6b0..4c391de62 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -9,6 +9,7 @@ use crate::{ validate::CoreConfigValidator, }, enhance, + process::AsyncHandler, utils::{dirs, help}, }; use anyhow::{Result, anyhow}; @@ -213,17 +214,27 @@ impl Config { // 升级草稿为正式数据,并写入文件。避免用户行为丢失。 // 仅在应用退出、重启、关机监听事件启用 pub async fn apply_all_and_save_file() { - let clash = Self::clash().await; - clash.apply(); - logging_error!(Type::Config, clash.data_arc().save_config().await); + logging!(info, Type::Config, "save all draft data"); + let save_clash_task = AsyncHandler::spawn(|| async { + let clash = Self::clash().await; + clash.apply(); + logging_error!(Type::Config, clash.data_arc().save_config().await); + }); - let verge = Self::verge().await; - verge.apply(); - logging_error!(Type::Config, verge.data_arc().save_file().await); + let save_verge_task = AsyncHandler::spawn(|| async { + let verge = Self::verge().await; + verge.apply(); + logging_error!(Type::Config, verge.data_arc().save_file().await); + }); - let profiles = Self::profiles().await; - profiles.apply(); - logging_error!(Type::Config, profiles.data_arc().save_file().await); + let save_profiles_task = AsyncHandler::spawn(|| async { + let profiles = Self::profiles().await; + profiles.apply(); + logging_error!(Type::Config, profiles.data_arc().save_file().await); + }); + + let _ = tokio::join!(save_clash_task, save_verge_task, save_profiles_task); + logging!(info, Type::Config, "save all draft data finished"); } } diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 3f5aa669e..39918369b 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -7,6 +7,8 @@ use crate::{ }; use anyhow::Result; use clash_verge_logging::{Type, logging, logging_error}; +#[cfg(not(target_os = "windows"))] +use parking_lot::Mutex; use parking_lot::RwLock; use scopeguard::defer; use smartstring::alias::String; @@ -23,6 +25,10 @@ use tauri_plugin_autostart::ManagerExt as _; pub struct Sysopt { update_sysproxy: AtomicBool, reset_sysproxy: AtomicBool, + #[cfg(not(target_os = "windows"))] + sysproxy: Arc>, + #[cfg(not(target_os = "windows"))] + autoproxy: Arc>, guard: Arc>, } @@ -31,6 +37,10 @@ impl Default for Sysopt { Self { update_sysproxy: AtomicBool::new(false), reset_sysproxy: AtomicBool::new(false), + #[cfg(not(target_os = "windows"))] + sysproxy: Arc::new(Mutex::new(Sysproxy::default())), + #[cfg(not(target_os = "windows"))] + autoproxy: Arc::new(Mutex::new(Autoproxy::default())), guard: Arc::new(RwLock::new(GuardMonitor::new( GuardType::None, Duration::from_secs(30), @@ -185,23 +195,25 @@ impl Sysopt { #[cfg(not(target_os = "windows"))] { - let mut sys = Sysproxy { - enable: false, - host: proxy_host.clone().into(), - port, - bypass: get_bypass().await.into(), - }; - let mut auto = Autoproxy { - enable: false, - url: format!("http://{proxy_host}:{pac_port}/commands/pac"), - }; + // 先 await, 避免持有锁导致的 Send 问题 + let bypass = get_bypass().await; + + let mut sys = self.sysproxy.lock(); + sys.enable = false; + sys.host = proxy_host.clone().into(); + sys.port = port; + sys.bypass = bypass.into(); + + let mut auto = self.autoproxy.lock(); + auto.enable = false; + auto.url = format!("http://{proxy_host}:{pac_port}/commands/pac"); if !sys_enable { sys.set_system_proxy()?; auto.set_auto_proxy()?; self.access_guard() .write() - .set_guard_type(GuardType::Sysproxy(sys)); + .set_guard_type(GuardType::Sysproxy(sys.clone())); return Ok(()); } @@ -212,7 +224,7 @@ impl Sysopt { auto.set_auto_proxy()?; self.access_guard() .write() - .set_guard_type(GuardType::Autoproxy(auto)); + .set_guard_type(GuardType::Autoproxy(auto.clone())); return Ok(()); } @@ -221,9 +233,11 @@ impl Sysopt { sys.enable = true; auto.set_auto_proxy()?; sys.set_system_proxy()?; + drop(auto); self.access_guard() .write() - .set_guard_type(GuardType::Sysproxy(sys)); + .set_guard_type(GuardType::Sysproxy(sys.clone())); + drop(sys); return Ok(()); } } @@ -282,32 +296,13 @@ impl Sysopt { //直接关闭所有代理 #[cfg(not(target_os = "windows"))] { - let mut sysproxy: Sysproxy = match Sysproxy::get_system_proxy() { - Ok(sp) => sp, - Err(e) => { - logging!( - warn, - Type::Core, - "Warning: 重置代理时获取系统代理配置失败: {e}, 使用默认配置" - ); - Sysproxy::default() - } - }; - let mut autoproxy = match Autoproxy::get_auto_proxy() { - Ok(ap) => ap, - Err(e) => { - logging!( - warn, - Type::Core, - "Warning: 重置代理时获取自动代理配置失败: {e}, 使用默认配置" - ); - Autoproxy::default() - } - }; + let mut sysproxy = self.sysproxy.lock(); sysproxy.enable = false; + sysproxy.set_system_proxy()?; + drop(sysproxy); + let mut autoproxy = self.autoproxy.lock(); autoproxy.enable = false; autoproxy.set_auto_proxy()?; - sysproxy.set_system_proxy()?; } #[cfg(target_os = "windows")] diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index 98fa4ad60..bfe88d2f1 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -26,12 +26,12 @@ pub async fn restart_clash_core() { /// Restart the application pub async fn restart_app() { logging!(debug, Type::System, "启动重启应用流程"); - utils::server::shutdown_embedded_server(); - Config::apply_all_and_save_file().await; - // 设置退出标志 handle::Handle::global().set_is_exiting(); + utils::server::shutdown_embedded_server(); + Config::apply_all_and_save_file().await; + logging!(info, Type::System, "开始异步清理资源"); let cleanup_result = clean_async().await; diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index 5d04f317a..fde7df86e 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -20,12 +20,12 @@ async fn open_or_close_dashboard_internal() { pub async fn quit() { logging!(debug, Type::System, "启动退出流程"); - utils::server::shutdown_embedded_server(); - Config::apply_all_and_save_file().await; - // 设置退出标志 handle::Handle::global().set_is_exiting(); + utils::server::shutdown_embedded_server(); + Config::apply_all_and_save_file().await; + logging!(info, Type::System, "开始异步清理资源"); let cleanup_result = clean_async().await; @@ -43,51 +43,8 @@ pub async fn quit() { pub async fn clean_async() -> bool { logging!(info, Type::System, "开始执行异步清理操作..."); - // 1. 处理TUN模式 - let tun_task = async { - let tun_enabled = Config::verge() - .await - .data_arc() - .enable_tun_mode - .unwrap_or(false); - - if !tun_enabled { - return true; - } - - let disable_tun = serde_json::json!({ "tun": { "enable": false } }); - - logging!(info, Type::System, "send disable tun request to mihomo"); - match timeout( - Duration::from_millis(1000), - handle::Handle::mihomo() - .await - .patch_base_config(&disable_tun), - ) - .await - { - Ok(Ok(_)) => { - logging!(info, Type::Window, "TUN模式已禁用"); - true - } - Ok(Err(e)) => { - logging!(warn, Type::Window, "Warning: 禁用TUN模式失败: {e}"); - // 超时不阻塞退出 - true - } - Err(_) => { - logging!( - warn, - Type::Window, - "Warning: 禁用TUN模式超时(可能系统正在关机),继续退出流程" - ); - true - } - } - }; - - // 2. 系统代理重置 - let proxy_task = async { + // 重置系统代理 + let proxy_task = tokio::task::spawn(async { #[cfg(target_os = "windows")] { use sysproxy::{Autoproxy, Sysproxy}; @@ -203,10 +160,44 @@ pub async fn clean_async() -> bool { } } } - }; + }); + + // 关闭 Tun 模式 + 停止核心服务 + let core_task = tokio::task::spawn(async { + logging!(info, Type::System, "disable tun"); + let tun_enabled = Config::verge() + .await + .data_arc() + .enable_tun_mode + .unwrap_or(false); + if tun_enabled { + let disable_tun = serde_json::json!({ "tun": { "enable": false } }); + + logging!(info, Type::System, "send disable tun request to mihomo"); + match timeout( + Duration::from_millis(1000), + handle::Handle::mihomo() + .await + .patch_base_config(&disable_tun), + ) + .await + { + Ok(Ok(_)) => { + logging!(info, Type::Window, "TUN模式已禁用"); + } + Ok(Err(e)) => { + logging!(warn, Type::Window, "Warning: 禁用TUN模式失败: {e}"); + } + Err(_) => { + logging!( + warn, + Type::Window, + "Warning: 禁用TUN模式超时(可能系统正在关机),继续退出流程" + ); + } + } + } - // 3. 核心服务停止 - let core_task = async { #[cfg(target_os = "windows")] let stop_timeout = Duration::from_secs(2); #[cfg(not(target_os = "windows"))] @@ -227,11 +218,11 @@ pub async fn clean_async() -> bool { true } } - }; + }); - // 4. DNS恢复(仅macOS) - #[cfg(target_os = "macos")] - let dns_task = async { + // DNS恢复(仅macOS) + let dns_task = tokio::task::spawn(async { + #[cfg(target_os = "macos")] match timeout( Duration::from_millis(1000), crate::utils::resolve::dns::restore_public_dns(), @@ -247,22 +238,23 @@ pub async fn clean_async() -> bool { false } } - }; + #[cfg(not(target_os = "macos"))] + true + }); - #[cfg(not(target_os = "macos"))] - let dns_task = async { true }; - - let tun_success = tun_task.await; // 并行执行清理任务 - let (proxy_success, core_success, dns_success) = tokio::join!(proxy_task, core_task, dns_task); + let (proxy_result, core_result, dns_result) = tokio::join!(proxy_task, core_task, dns_task); - let all_success = tun_success && proxy_success && core_success && dns_success; + let proxy_success = proxy_result.unwrap_or_default(); + let core_success = core_result.unwrap_or_default(); + let dns_success = dns_result.unwrap_or_default(); + + let all_success = proxy_success && core_success && dns_success; logging!( info, Type::System, - "异步关闭操作完成 - TUN: {}, 代理: {}, 核心: {}, DNS: {}, 总体: {}", - tun_success, + "异步关闭操作完成 - 代理: {}, 核心: {}, DNS: {}, 总体: {}", proxy_success, core_success, dns_success, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0e3e7c102..08e34c4a9 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -429,7 +429,9 @@ pub fn run() { } #[cfg(target_os = "macos")] tauri::RunEvent::Exit => AsyncHandler::block_on(async { - feat::quit().await; + if !handle::Handle::global().is_exiting() { + feat::quit().await; + } }), tauri::RunEvent::ExitRequested { api, code, .. } => { AsyncHandler::block_on(async {