fix: failed to receive shutdown signal on windows (#5533)

* fix: receive shutdown signal failed on windows

* docs: update Changelog.md

* chore: update

* fix: use tokio runtime to handle shutdown signal

* docs: update Changelog.md

* fix: move tauri dependency to the correct section in Cargo.toml

* fix: remove unused exit handling code in run function

* fix(clash-verge-signal): use global runtime to avoid tokio runtime panic on unix

* chore: update tauri-plugin-mihomo deps

* fix: handle macOS exit event to ensure proper application termination

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
This commit is contained in:
oomeow
2025-11-22 22:36:00 +08:00
committed by GitHub
parent 687530a9cd
commit cbab199f80
11 changed files with 130 additions and 106 deletions

View File

@@ -1,8 +1,9 @@
use crate::{
config::Config,
core::{CoreManager, handle, tray},
feat::clean_async,
process::AsyncHandler,
utils::{self, resolve},
utils,
};
use clash_verge_logging::{Type, logging, logging_error};
use serde_yaml_ng::{Mapping, Value};
@@ -24,16 +25,22 @@ 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;
if let Err(err) = resolve::resolve_reset_async().await {
handle::Handle::notice_message(
"restart_app::error",
format!("Failed to cleanup resources: {err}"),
);
logging!(error, Type::Core, "Restart failed during cleanup: {err}");
return;
}
// 设置退出标志
handle::Handle::global().set_is_exiting();
logging!(info, Type::System, "开始异步清理资源");
let cleanup_result = clean_async().await;
logging!(
info,
Type::System,
"资源清理完成,退出代码: {}",
if cleanup_result { 0 } else { 1 }
);
let app_handle = handle::Handle::app_handle();
app_handle.restart();

View File

@@ -23,8 +23,7 @@ pub async fn quit() {
utils::server::shutdown_embedded_server();
Config::apply_all_and_save_file().await;
// 获取应用句柄并设置退出标志
let app_handle = handle::Handle::app_handle();
// 设置退出标志
handle::Handle::global().set_is_exiting();
logging!(info, Type::System, "开始异步清理资源");
@@ -36,6 +35,8 @@ pub async fn quit() {
"资源清理完成,退出代码: {}",
if cleanup_result { 0 } else { 1 }
);
let app_handle = handle::Handle::app_handle();
app_handle.exit(if cleanup_result { 0 } else { 1 });
}
@@ -56,6 +57,7 @@ pub async fn clean_async() -> bool {
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()
@@ -210,6 +212,7 @@ pub async fn clean_async() -> bool {
#[cfg(not(target_os = "windows"))]
let stop_timeout = Duration::from_secs(3);
logging!(info, Type::System, "stop core");
match timeout(stop_timeout, CoreManager::global().stop_core()).await {
Ok(_) => {
logging!(info, Type::Window, "core已停止");
@@ -269,41 +272,6 @@ pub async fn clean_async() -> bool {
all_success
}
pub fn clean() -> bool {
use crate::process::AsyncHandler;
let (tx, rx) = std::sync::mpsc::channel();
AsyncHandler::spawn(move || async move {
logging!(info, Type::System, "开始执行关闭操作...");
// 使用已有的异步清理函数
let cleanup_result = clean_async().await;
let _ = tx.send(cleanup_result);
});
#[cfg(target_os = "windows")]
let total_timeout = std::time::Duration::from_secs(5);
#[cfg(not(target_os = "windows"))]
let total_timeout = std::time::Duration::from_secs(8);
match rx.recv_timeout(total_timeout) {
Ok(result) => {
logging!(info, Type::System, "关闭操作完成,结果: {}", result);
result
}
Err(_) => {
logging!(
warn,
Type::System,
"清理操作超时(可能正在关机),返回成功避免阻塞"
);
true
}
}
}
#[cfg(target_os = "macos")]
pub async fn hide() {
use crate::module::lightweight::add_light_weight_timer;

View File

@@ -10,9 +10,10 @@ mod feat;
mod module;
mod process;
pub mod utils;
use crate::constants::files;
#[cfg(target_os = "linux")]
use crate::utils::linux;
use crate::utils::resolve::init_signal;
use crate::{constants::files, utils::resolve::prioritize_initialization};
use crate::{
core::handle,
process::AsyncHandler,
@@ -237,13 +238,14 @@ pub fn run() {
let builder = app_init::setup_plugins(tauri::Builder::default())
.setup(|app| {
logging!(info, Type::Setup, "开始应用初始化...");
#[allow(clippy::expect_used)]
APP_HANDLE
.set(app.app_handle().clone())
.expect("failed to set global app handle");
let _handle = AsyncHandler::block_on(async { prioritize_initialization().await });
logging!(info, Type::Setup, "开始应用初始化...");
if let Err(e) = app_init::setup_autostart(app) {
logging!(error, Type::Setup, "Failed to setup autostart: {}", e);
}
@@ -257,6 +259,7 @@ pub fn run() {
resolve::resolve_setup_handle();
resolve::resolve_setup_async();
resolve::resolve_setup_sync();
init_signal();
logging!(info, Type::Setup, "初始化已启动");
Ok(())
@@ -423,6 +426,10 @@ pub fn run() {
event_handlers::handle_reopen(has_visible_windows).await;
});
}
#[cfg(target_os = "macos")]
tauri::RunEvent::Exit => AsyncHandler::block_on(async {
feat::quit().await;
}),
tauri::RunEvent::ExitRequested { api, code, .. } => {
AsyncHandler::block_on(async {
let _ = handle::Handle::mihomo()
@@ -439,13 +446,6 @@ pub fn run() {
api.prevent_exit();
}
}
tauri::RunEvent::Exit => {
let handle = core::handle::Handle::global();
if !handle.is_exiting() {
handle.set_is_exiting();
feat::clean();
}
}
tauri::RunEvent::WindowEvent { label, event, .. } if label == "main" => match event {
tauri::WindowEvent::CloseRequested { .. } => {
event_handlers::handle_window_close(&event);

View File

@@ -19,7 +19,7 @@ use clash_verge_service_ipc::WriterConfig;
use flexi_logger::writers::FileLogWriter;
use flexi_logger::{Cleanup, Criterion, FileSpec};
#[cfg(not(feature = "tauri-dev"))]
use flexi_logger::{Duplicate, LogSpecBuilder, Logger};
use flexi_logger::{Duplicate, LogSpecBuilder, Logger, LoggerHandle};
use std::{path::PathBuf, str::FromStr as _};
use tauri_plugin_shell::ShellExt as _;
use tokio::fs;
@@ -27,7 +27,7 @@ use tokio::fs::DirEntry;
/// initialize this instance's log file
#[cfg(not(feature = "tauri-dev"))]
pub async fn init_logger() -> Result<()> {
pub async fn init_logger() -> Result<LoggerHandle> {
// TODO 提供 runtime 级别实时修改
let (log_level, log_max_size, log_max_count) = {
let verge_guard = Config::verge().await;
@@ -47,11 +47,9 @@ pub async fn init_logger() -> Result<()> {
.unwrap_or(log_level);
spec.default(level);
#[cfg(feature = "tracing")]
spec.module("tauri", log::LevelFilter::Debug);
#[cfg(feature = "tracing")]
spec.module("wry", log::LevelFilter::Off);
#[cfg(feature = "tracing")]
spec.module("tauri_plugin_mihomo", log::LevelFilter::Off);
spec.module("tauri", log::LevelFilter::Debug)
.module("wry", log::LevelFilter::Off)
.module("tauri_plugin_mihomo", log::LevelFilter::Off);
let spec = spec.build();
let logger = Logger::with(spec)
@@ -83,14 +81,14 @@ pub async fn init_logger() -> Result<()> {
"kode_bridge",
])));
let _handle = logger.start()?;
let handle = logger.start()?;
// TODO 全局 logger handle 控制
// GlobalLoggerProxy::global().set_inner(handle);
// TODO 提供前端设置等级,热更新等级
// logger.parse_new_spec(spec)
Ok(())
Ok(handle)
}
pub async fn sidecar_writer() -> Result<FileLogWriter> {

View File

@@ -1,4 +1,5 @@
use anyhow::Result;
use flexi_logger::LoggerHandle;
use crate::{
config::Config,
@@ -23,6 +24,21 @@ pub mod ui;
pub mod window;
pub mod window_script;
pub async fn prioritize_initialization() -> Option<LoggerHandle> {
init_work_config().await;
init_resources().await;
#[cfg(not(feature = "tauri-dev"))]
{
logging!(info, Type::Setup, "Initializing logger");
init::init_logger().await.ok()
}
#[cfg(feature = "tauri-dev")]
{
None
}
}
pub fn resolve_setup_handle() {
init_handle();
}
@@ -31,14 +47,11 @@ pub fn resolve_setup_sync() {
AsyncHandler::spawn(|| async {
AsyncHandler::spawn_blocking(init_scheme);
AsyncHandler::spawn_blocking(init_embed_server);
AsyncHandler::spawn_blocking(init_signal);
});
}
pub fn resolve_setup_async() {
AsyncHandler::spawn(|| async {
#[cfg(not(feature = "tauri-dev"))]
resolve_setup_logger().await;
logging!(
info,
Type::ClashVergeRev,
@@ -96,11 +109,6 @@ pub(super) fn init_scheme() {
logging_error!(Type::Setup, init::init_scheme());
}
#[cfg(not(feature = "tauri-dev"))]
pub(super) async fn resolve_setup_logger() {
logging_error!(Type::Setup, init::init_logger().await);
}
pub async fn resolve_scheme(param: &str) -> Result<()> {
logging_error!(Type::Setup, scheme::resolve_scheme(param).await);
Ok(())
@@ -134,7 +142,7 @@ pub(super) async fn init_auto_backup() {
logging_error!(Type::Setup, AutoBackupManager::global().init().await);
}
pub(super) fn init_signal() {
pub fn init_signal() {
logging!(info, Type::Setup, "Initializing signal handlers...");
clash_verge_signal::register(
#[cfg(windows)]