mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
fix(shutdown): mark shutdown as exiting to stop background tasks (#5024)
* fix(shutdown): mark shutdown as exiting to stop background tasks - lib.rs:570 → Flag app as exiting on ExitRequested, notify proxy guard, start cleanup immediately, with fallback in Exit event - tray/mod.rs:190 → Add unified exit checks around tray init/updates to prevent UI recreation during shutdown - event_driven_proxy.rs:252 → Ensure proxy guard skips all restore/re-enable work (including sysproxy.exe calls) once exit flag is set * fix(shutdown): refine exit handling and proxy guard notifications * fix(shutdown): add guard to run shutdown routine only once per lifecycle
This commit is contained in:
@@ -5,7 +5,7 @@ use tokio::time::{Duration, sleep, timeout};
|
|||||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||||
|
|
||||||
use crate::config::{Config, IVerge};
|
use crate::config::{Config, IVerge};
|
||||||
use crate::core::async_proxy_query::AsyncProxyQuery;
|
use crate::core::{async_proxy_query::AsyncProxyQuery, handle};
|
||||||
use crate::logging_error;
|
use crate::logging_error;
|
||||||
use crate::process::AsyncHandler;
|
use crate::process::AsyncHandler;
|
||||||
use crate::utils::logging::Type;
|
use crate::utils::logging::Type;
|
||||||
@@ -250,6 +250,12 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
ProxyEvent::AppStopping => {
|
ProxyEvent::AppStopping => {
|
||||||
log::info!(target: "app", "清理代理状态");
|
log::info!(target: "app", "清理代理状态");
|
||||||
|
Self::update_state_timestamp(state, |s| {
|
||||||
|
s.sys_enabled = false;
|
||||||
|
s.pac_enabled = false;
|
||||||
|
s.is_healthy = false;
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,6 +307,10 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn check_and_restore_proxy(state: &Arc<RwLock<ProxyState>>) {
|
async fn check_and_restore_proxy(state: &Arc<RwLock<ProxyState>>) {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过系统代理守卫检查");
|
||||||
|
return;
|
||||||
|
}
|
||||||
let (sys_enabled, pac_enabled) = {
|
let (sys_enabled, pac_enabled) = {
|
||||||
let s = state.read().await;
|
let s = state.read().await;
|
||||||
(s.sys_enabled, s.pac_enabled)
|
(s.sys_enabled, s.pac_enabled)
|
||||||
@@ -320,6 +330,11 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn check_and_restore_pac_proxy(state: &Arc<RwLock<ProxyState>>) {
|
async fn check_and_restore_pac_proxy(state: &Arc<RwLock<ProxyState>>) {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过PAC代理恢复检查");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let current = Self::get_auto_proxy_with_timeout().await;
|
let current = Self::get_auto_proxy_with_timeout().await;
|
||||||
let expected = Self::get_expected_pac_config().await;
|
let expected = Self::get_expected_pac_config().await;
|
||||||
|
|
||||||
@@ -346,6 +361,11 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn check_and_restore_sys_proxy(state: &Arc<RwLock<ProxyState>>) {
|
async fn check_and_restore_sys_proxy(state: &Arc<RwLock<ProxyState>>) {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过系统代理恢复检查");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let current = Self::get_sys_proxy_with_timeout().await;
|
let current = Self::get_sys_proxy_with_timeout().await;
|
||||||
let expected = Self::get_expected_sys_proxy().await;
|
let expected = Self::get_expected_sys_proxy().await;
|
||||||
|
|
||||||
@@ -374,6 +394,11 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn enable_system_proxy(state: &Arc<RwLock<ProxyState>>) {
|
async fn enable_system_proxy(state: &Arc<RwLock<ProxyState>>) {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过启用系统代理");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "启用系统代理");
|
log::info!(target: "app", "启用系统代理");
|
||||||
|
|
||||||
let pac_enabled = state.read().await.pac_enabled;
|
let pac_enabled = state.read().await.pac_enabled;
|
||||||
@@ -407,6 +432,11 @@ impl EventDrivenProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn switch_proxy_mode(state: &Arc<RwLock<ProxyState>>, to_pac: bool) {
|
async fn switch_proxy_mode(state: &Arc<RwLock<ProxyState>>, to_pac: bool) {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过代理模式切换");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "切换到{}模式", if to_pac { "PAC" } else { "HTTP代理" });
|
log::info!(target: "app", "切换到{}模式", if to_pac { "PAC" } else { "HTTP代理" });
|
||||||
|
|
||||||
if to_pac {
|
if to_pac {
|
||||||
@@ -545,6 +575,10 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
|
async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过PAC代理恢复");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
Self::execute_sysproxy_command(&["pac", expected_url]).await
|
Self::execute_sysproxy_command(&["pac", expected_url]).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +599,10 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
|
async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过系统代理恢复");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let address = format!("{}:{}", expected.host, expected.port);
|
let address = format!("{}:{}", expected.host, expected.port);
|
||||||
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await
|
Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await
|
||||||
}
|
}
|
||||||
@@ -582,6 +620,15 @@ impl EventDrivenProxyManager {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
async fn execute_sysproxy_command(args: &[&str]) -> Result<(), anyhow::Error> {
|
async fn execute_sysproxy_command(args: &[&str]) -> Result<(), anyhow::Error> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(
|
||||||
|
target: "app",
|
||||||
|
"应用正在退出,取消调用 sysproxy.exe,参数: {:?}",
|
||||||
|
args
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
use crate::utils::dirs;
|
use crate::utils::dirs;
|
||||||
#[allow(unused_imports)] // creation_flags必须
|
#[allow(unused_imports)] // creation_flags必须
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
|
|||||||
@@ -188,6 +188,11 @@ singleton_lazy!(Tray, TRAY, Tray::default);
|
|||||||
|
|
||||||
impl Tray {
|
impl Tray {
|
||||||
pub async fn init(&self) -> Result<()> {
|
pub async fn init(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘初始化");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
|
|
||||||
match self.create_tray_from_handle(app_handle).await {
|
match self.create_tray_from_handle(app_handle).await {
|
||||||
@@ -206,6 +211,11 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘点击行为
|
/// 更新托盘点击行为
|
||||||
pub async fn update_click_behavior(&self) -> Result<()> {
|
pub async fn update_click_behavior(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘点击行为更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||||
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
||||||
@@ -221,6 +231,10 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘菜单
|
/// 更新托盘菜单
|
||||||
pub async fn update_menu(&self) -> Result<()> {
|
pub async fn update_menu(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘菜单更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// 调整最小更新间隔,确保状态及时刷新
|
// 调整最小更新间隔,确保状态及时刷新
|
||||||
const MIN_UPDATE_INTERVAL: Duration = Duration::from_millis(100);
|
const MIN_UPDATE_INTERVAL: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
@@ -309,6 +323,11 @@ impl Tray {
|
|||||||
/// 更新托盘图标
|
/// 更新托盘图标
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub async fn update_icon(&self) -> Result<()> {
|
pub async fn update_icon(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘图标更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
|
|
||||||
let tray = match app_handle.tray_by_id("main") {
|
let tray = match app_handle.tray_by_id("main") {
|
||||||
@@ -340,6 +359,11 @@ impl Tray {
|
|||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub async fn update_icon(&self) -> Result<()> {
|
pub async fn update_icon(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘图标更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
|
|
||||||
let tray = match app_handle.tray_by_id("main") {
|
let tray = match app_handle.tray_by_id("main") {
|
||||||
@@ -367,6 +391,11 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘显示状态的函数
|
/// 更新托盘显示状态的函数
|
||||||
pub async fn update_tray_display(&self) -> Result<()> {
|
pub async fn update_tray_display(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘显示状态更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
let _tray = app_handle
|
let _tray = app_handle
|
||||||
.tray_by_id("main")
|
.tray_by_id("main")
|
||||||
@@ -380,6 +409,11 @@ impl Tray {
|
|||||||
|
|
||||||
/// 更新托盘提示
|
/// 更新托盘提示
|
||||||
pub async fn update_tooltip(&self) -> Result<()> {
|
pub async fn update_tooltip(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘提示更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
|
|
||||||
let verge = Config::verge().await.latest_ref().clone();
|
let verge = Config::verge().await.latest_ref().clone();
|
||||||
@@ -438,6 +472,10 @@ impl Tray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_part(&self) -> Result<()> {
|
pub async fn update_part(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘局部更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// self.update_menu().await?;
|
// self.update_menu().await?;
|
||||||
// 更新轻量模式显示状态
|
// 更新轻量模式显示状态
|
||||||
self.update_tray_display().await?;
|
self.update_tray_display().await?;
|
||||||
@@ -447,6 +485,11 @@ impl Tray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_tray_from_handle(&self, app_handle: &AppHandle) -> Result<()> {
|
pub async fn create_tray_from_handle(&self, app_handle: &AppHandle) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘创建");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "正在从AppHandle创建系统托盘");
|
log::info!(target: "app", "正在从AppHandle创建系统托盘");
|
||||||
|
|
||||||
// 获取图标
|
// 获取图标
|
||||||
@@ -524,6 +567,11 @@ impl Tray {
|
|||||||
|
|
||||||
// 托盘统一的状态更新函数
|
// 托盘统一的状态更新函数
|
||||||
pub async fn update_all_states(&self) -> Result<()> {
|
pub async fn update_all_states(&self) -> Result<()> {
|
||||||
|
if handle::Handle::global().is_exiting() {
|
||||||
|
log::debug!(target: "app", "应用正在退出,跳过托盘状态更新");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// 确保所有状态更新完成
|
// 确保所有状态更新完成
|
||||||
self.update_tray_display().await?;
|
self.update_tray_display().await?;
|
||||||
// self.update_menu().await?;
|
// self.update_menu().await?;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
|
use crate::config::Config;
|
||||||
|
use crate::core::event_driven_proxy::EventDrivenProxyManager;
|
||||||
|
use crate::core::{CoreManager, handle, sysopt};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use crate::utils::window_manager::WindowManager;
|
use crate::utils::window_manager::WindowManager;
|
||||||
use crate::{
|
use crate::{logging, module::lightweight, utils::logging::Type};
|
||||||
config::Config,
|
|
||||||
core::{CoreManager, handle, sysopt},
|
|
||||||
logging,
|
|
||||||
module::lightweight,
|
|
||||||
utils::logging::Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Public API: open or close the dashboard
|
/// Public API: open or close the dashboard
|
||||||
pub async fn open_or_close_dashboard() {
|
pub async fn open_or_close_dashboard() {
|
||||||
@@ -27,6 +24,7 @@ pub async fn quit() {
|
|||||||
// 获取应用句柄并设置退出标志
|
// 获取应用句柄并设置退出标志
|
||||||
let app_handle = handle::Handle::app_handle();
|
let app_handle = handle::Handle::app_handle();
|
||||||
handle::Handle::global().set_is_exiting();
|
handle::Handle::global().set_is_exiting();
|
||||||
|
EventDrivenProxyManager::global().notify_app_stopping();
|
||||||
|
|
||||||
// 优先关闭窗口,提供立即反馈
|
// 优先关闭窗口,提供立即反馈
|
||||||
if let Some(window) = handle::Handle::get_window() {
|
if let Some(window) = handle::Handle::get_window() {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ mod utils;
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::utils::window_manager::WindowManager;
|
use crate::utils::window_manager::WindowManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{handle, hotkey},
|
core::{EventDrivenProxyManager, handle, hotkey},
|
||||||
process::AsyncHandler,
|
process::AsyncHandler,
|
||||||
utils::{resolve, server},
|
utils::{resolve, server},
|
||||||
};
|
};
|
||||||
@@ -584,11 +584,20 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tauri::RunEvent::Exit => {
|
tauri::RunEvent::Exit => {
|
||||||
// Avoid duplicate cleanup
|
let handle = core::handle::Handle::global();
|
||||||
if core::handle::Handle::global().is_exiting() {
|
|
||||||
return;
|
if handle.is_exiting() {
|
||||||
|
logging!(
|
||||||
|
debug,
|
||||||
|
Type::System,
|
||||||
|
"Exit事件触发,但退出流程已执行,跳过重复清理"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logging!(debug, Type::System, "Exit事件触发,执行清理流程");
|
||||||
|
handle.set_is_exiting();
|
||||||
|
EventDrivenProxyManager::global().notify_app_stopping();
|
||||||
|
feat::clean();
|
||||||
}
|
}
|
||||||
feat::clean();
|
|
||||||
}
|
}
|
||||||
tauri::RunEvent::WindowEvent { label, event, .. } => {
|
tauri::RunEvent::WindowEvent { label, event, .. } => {
|
||||||
if label == "main" {
|
if label == "main" {
|
||||||
|
|||||||
Reference in New Issue
Block a user