perf: spawn clean task (#5595)

* perf: spawn task

* perf: spawn task to save draft date

* perf: store proxy client

* chore: update

* perf: reduce handle quit on macOS

* chore: clippy

* docs: update Changelog.md

* chore: update
This commit is contained in:
oomeow
2025-11-28 21:12:34 +08:00
committed by GitHub
parent 683667f8ae
commit 6456c597ed
6 changed files with 114 additions and 113 deletions

View File

@@ -51,6 +51,7 @@
- 优化重启应用的资源清理逻辑 - 优化重启应用的资源清理逻辑
- 优化前端数据刷新 - 优化前端数据刷新
- 优化流量采样和数据处理 - 优化流量采样和数据处理
- 优化应用重启/退出时的资源清理性能, 大幅缩短执行时间
</details> </details>

View File

@@ -9,6 +9,7 @@ use crate::{
validate::CoreConfigValidator, validate::CoreConfigValidator,
}, },
enhance, enhance,
process::AsyncHandler,
utils::{dirs, help}, utils::{dirs, help},
}; };
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
@@ -213,17 +214,27 @@ impl Config {
// 升级草稿为正式数据,并写入文件。避免用户行为丢失。 // 升级草稿为正式数据,并写入文件。避免用户行为丢失。
// 仅在应用退出、重启、关机监听事件启用 // 仅在应用退出、重启、关机监听事件启用
pub async fn apply_all_and_save_file() { pub async fn apply_all_and_save_file() {
logging!(info, Type::Config, "save all draft data");
let save_clash_task = AsyncHandler::spawn(|| async {
let clash = Self::clash().await; let clash = Self::clash().await;
clash.apply(); clash.apply();
logging_error!(Type::Config, clash.data_arc().save_config().await); logging_error!(Type::Config, clash.data_arc().save_config().await);
});
let save_verge_task = AsyncHandler::spawn(|| async {
let verge = Self::verge().await; let verge = Self::verge().await;
verge.apply(); verge.apply();
logging_error!(Type::Config, verge.data_arc().save_file().await); logging_error!(Type::Config, verge.data_arc().save_file().await);
});
let save_profiles_task = AsyncHandler::spawn(|| async {
let profiles = Self::profiles().await; let profiles = Self::profiles().await;
profiles.apply(); profiles.apply();
logging_error!(Type::Config, profiles.data_arc().save_file().await); 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");
} }
} }

View File

@@ -7,6 +7,8 @@ use crate::{
}; };
use anyhow::Result; use anyhow::Result;
use clash_verge_logging::{Type, logging, logging_error}; use clash_verge_logging::{Type, logging, logging_error};
#[cfg(not(target_os = "windows"))]
use parking_lot::Mutex;
use parking_lot::RwLock; use parking_lot::RwLock;
use scopeguard::defer; use scopeguard::defer;
use smartstring::alias::String; use smartstring::alias::String;
@@ -23,6 +25,10 @@ use tauri_plugin_autostart::ManagerExt as _;
pub struct Sysopt { pub struct Sysopt {
update_sysproxy: AtomicBool, update_sysproxy: AtomicBool,
reset_sysproxy: AtomicBool, reset_sysproxy: AtomicBool,
#[cfg(not(target_os = "windows"))]
sysproxy: Arc<Mutex<Sysproxy>>,
#[cfg(not(target_os = "windows"))]
autoproxy: Arc<Mutex<Autoproxy>>,
guard: Arc<RwLock<GuardMonitor>>, guard: Arc<RwLock<GuardMonitor>>,
} }
@@ -31,6 +37,10 @@ impl Default for Sysopt {
Self { Self {
update_sysproxy: AtomicBool::new(false), update_sysproxy: AtomicBool::new(false),
reset_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( guard: Arc::new(RwLock::new(GuardMonitor::new(
GuardType::None, GuardType::None,
Duration::from_secs(30), Duration::from_secs(30),
@@ -185,23 +195,25 @@ impl Sysopt {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
{ {
let mut sys = Sysproxy { // 先 await, 避免持有锁导致的 Send 问题
enable: false, let bypass = get_bypass().await;
host: proxy_host.clone().into(),
port, let mut sys = self.sysproxy.lock();
bypass: get_bypass().await.into(), sys.enable = false;
}; sys.host = proxy_host.clone().into();
let mut auto = Autoproxy { sys.port = port;
enable: false, sys.bypass = bypass.into();
url: format!("http://{proxy_host}:{pac_port}/commands/pac"),
}; let mut auto = self.autoproxy.lock();
auto.enable = false;
auto.url = format!("http://{proxy_host}:{pac_port}/commands/pac");
if !sys_enable { if !sys_enable {
sys.set_system_proxy()?; sys.set_system_proxy()?;
auto.set_auto_proxy()?; auto.set_auto_proxy()?;
self.access_guard() self.access_guard()
.write() .write()
.set_guard_type(GuardType::Sysproxy(sys)); .set_guard_type(GuardType::Sysproxy(sys.clone()));
return Ok(()); return Ok(());
} }
@@ -212,7 +224,7 @@ impl Sysopt {
auto.set_auto_proxy()?; auto.set_auto_proxy()?;
self.access_guard() self.access_guard()
.write() .write()
.set_guard_type(GuardType::Autoproxy(auto)); .set_guard_type(GuardType::Autoproxy(auto.clone()));
return Ok(()); return Ok(());
} }
@@ -221,9 +233,11 @@ impl Sysopt {
sys.enable = true; sys.enable = true;
auto.set_auto_proxy()?; auto.set_auto_proxy()?;
sys.set_system_proxy()?; sys.set_system_proxy()?;
drop(auto);
self.access_guard() self.access_guard()
.write() .write()
.set_guard_type(GuardType::Sysproxy(sys)); .set_guard_type(GuardType::Sysproxy(sys.clone()));
drop(sys);
return Ok(()); return Ok(());
} }
} }
@@ -282,32 +296,13 @@ impl Sysopt {
//直接关闭所有代理 //直接关闭所有代理
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
{ {
let mut sysproxy: Sysproxy = match Sysproxy::get_system_proxy() { let mut sysproxy = self.sysproxy.lock();
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()
}
};
sysproxy.enable = false; sysproxy.enable = false;
sysproxy.set_system_proxy()?;
drop(sysproxy);
let mut autoproxy = self.autoproxy.lock();
autoproxy.enable = false; autoproxy.enable = false;
autoproxy.set_auto_proxy()?; autoproxy.set_auto_proxy()?;
sysproxy.set_system_proxy()?;
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

View File

@@ -26,12 +26,12 @@ pub async fn restart_clash_core() {
/// Restart the application /// Restart the application
pub async fn restart_app() { pub async fn restart_app() {
logging!(debug, Type::System, "启动重启应用流程"); logging!(debug, Type::System, "启动重启应用流程");
utils::server::shutdown_embedded_server();
Config::apply_all_and_save_file().await;
// 设置退出标志 // 设置退出标志
handle::Handle::global().set_is_exiting(); handle::Handle::global().set_is_exiting();
utils::server::shutdown_embedded_server();
Config::apply_all_and_save_file().await;
logging!(info, Type::System, "开始异步清理资源"); logging!(info, Type::System, "开始异步清理资源");
let cleanup_result = clean_async().await; let cleanup_result = clean_async().await;

View File

@@ -20,12 +20,12 @@ async fn open_or_close_dashboard_internal() {
pub async fn quit() { pub async fn quit() {
logging!(debug, Type::System, "启动退出流程"); logging!(debug, Type::System, "启动退出流程");
utils::server::shutdown_embedded_server();
Config::apply_all_and_save_file().await;
// 设置退出标志 // 设置退出标志
handle::Handle::global().set_is_exiting(); handle::Handle::global().set_is_exiting();
utils::server::shutdown_embedded_server();
Config::apply_all_and_save_file().await;
logging!(info, Type::System, "开始异步清理资源"); logging!(info, Type::System, "开始异步清理资源");
let cleanup_result = clean_async().await; let cleanup_result = clean_async().await;
@@ -43,51 +43,8 @@ pub async fn quit() {
pub async fn clean_async() -> bool { pub async fn clean_async() -> bool {
logging!(info, Type::System, "开始执行异步清理操作..."); logging!(info, Type::System, "开始执行异步清理操作...");
// 1. 处理TUN模式 // 重置系统代理
let tun_task = async { let proxy_task = tokio::task::spawn(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 {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
use sysproxy::{Autoproxy, Sysproxy}; 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")] #[cfg(target_os = "windows")]
let stop_timeout = Duration::from_secs(2); let stop_timeout = Duration::from_secs(2);
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
@@ -227,11 +218,11 @@ pub async fn clean_async() -> bool {
true true
} }
} }
}; });
// 4. DNS恢复仅macOS // DNS恢复仅macOS
let dns_task = tokio::task::spawn(async {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let dns_task = async {
match timeout( match timeout(
Duration::from_millis(1000), Duration::from_millis(1000),
crate::utils::resolve::dns::restore_public_dns(), crate::utils::resolve::dns::restore_public_dns(),
@@ -247,22 +238,23 @@ pub async fn clean_async() -> bool {
false false
} }
} }
};
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
let dns_task = async { true }; 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!( logging!(
info, info,
Type::System, Type::System,
"异步关闭操作完成 - TUN: {}, 代理: {}, 核心: {}, DNS: {}, 总体: {}", "异步关闭操作完成 - 代理: {}, 核心: {}, DNS: {}, 总体: {}",
tun_success,
proxy_success, proxy_success,
core_success, core_success,
dns_success, dns_success,

View File

@@ -429,7 +429,9 @@ pub fn run() {
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
tauri::RunEvent::Exit => AsyncHandler::block_on(async { tauri::RunEvent::Exit => AsyncHandler::block_on(async {
if !handle::Handle::global().is_exiting() {
feat::quit().await; feat::quit().await;
}
}), }),
tauri::RunEvent::ExitRequested { api, code, .. } => { tauri::RunEvent::ExitRequested { api, code, .. } => {
AsyncHandler::block_on(async { AsyncHandler::block_on(async {