diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index 11c6e12cc..70f7f5afb 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -3,7 +3,7 @@ use crate::{ core::{CoreManager, handle, tray}, logging_error, process::AsyncHandler, - utils::{logging::Type, resolve}, + utils::{self, logging::Type, resolve}, }; use serde_yaml_ng::{Mapping, Value}; @@ -23,6 +23,7 @@ pub async fn restart_clash_core() { /// Restart the application pub async fn restart_app() { + utils::server::shutdown_embedded_server(); if let Err(err) = resolve::resolve_reset_async().await { handle::Handle::notice_message( "restart_app::error", @@ -34,45 +35,6 @@ pub async fn restart_app() { let app_handle = handle::Handle::app_handle(); app_handle.restart(); - // TODO: PR Ref: https://github.com/clash-verge-rev/clash-verge-rev/pull/4960 - // handle::Handle::notice_message("restart_app::info", "Restarting application..."); - - // // Use the manual restart method consistently to ensure reliability across platforms - // // This addresses the issue where app_handle.restart() doesn't work properly on Windows - // let current_exe = match env::current_exe() { - // Ok(path) => path, - // Err(_) => { - // // If we can't get the current executable path, try to use the fallback method - // if let Some(app_handle) = handle::Handle::global().app_handle() { - // app_handle.restart(); - // } - // exit(1); // If we reach here, either app_handle was None or restart() failed to restart - // } - // }; - - // let mut cmd = Command::new(current_exe); - // cmd.args(env::args().skip(1)); - - // match cmd.spawn() { - // Ok(child) => { - // log::info!(target: "app", "New application instance started with PID: {}", child.id()); - // // Successfully started new process, now exit current process - // if let Some(app_handle) = handle::Handle::global().app_handle() { - // app_handle.exit(0); - // } else { - // exit(0); - // } - // } - // Err(e) => { - // log::error!(target: "app", "Failed to start new application instance: {}", e); - // // If manual spawn fails, try the original restart method as a last resort - // if let Some(app_handle) = handle::Handle::global().app_handle() { - // app_handle.restart(); - // } else { - // exit(1); - // } - // } - // } } fn after_change_clash_mode() { diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index 81261aecb..755c3f822 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -1,3 +1,4 @@ +use crate::utils; use crate::utils::window_manager::WindowManager; use crate::{ config::Config, @@ -21,6 +22,7 @@ async fn open_or_close_dashboard_internal() { pub async fn quit() { logging!(debug, Type::System, true, "启动退出流程"); + utils::server::shutdown_embedded_server(); // 获取应用句柄并设置退出标志 let app_handle = handle::Handle::app_handle(); diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index e16986e41..e75a444f3 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -6,7 +6,10 @@ use crate::{ utils::logging::Type, }; use anyhow::{Result, bail}; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; use port_scanner::local_port_available; +use tokio::sync::oneshot; use warp::Filter; #[derive(serde::Deserialize, Debug)] @@ -14,6 +17,9 @@ struct QueryParam { param: String, } +// 关闭 embedded server 的信号发送端 +static SHUTDOWN_SENDER: OnceCell>>> = OnceCell::new(); + /// check whether there is already exists pub async fn check_singleton() -> Result<()> { let port = IVerge::get_singleton_port(); @@ -42,6 +48,11 @@ pub async fn check_singleton() -> Result<()> { /// The embed server only be used to implement singleton process /// maybe it can be used as pac server later pub fn embed_server() { + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + #[allow(clippy::expect_used)] + SHUTDOWN_SENDER + .set(Mutex::new(Some(shutdown_tx))) + .expect("failed to set shutdown signal for embedded server"); let port = IVerge::get_singleton_port(); AsyncHandler::spawn(move || async move { @@ -90,6 +101,22 @@ pub fn embed_server() { }); let commands = visible.or(scheme).or(pac); - warp::serve(commands).run(([127, 0, 0, 1], port)).await; + warp::serve(commands) + .bind(([127, 0, 0, 1], port)) + .await + .graceful(async { + shutdown_rx.await.ok(); + }) + .run() + .await; }); } + +pub fn shutdown_embedded_server() { + log::info!("shutting down embedded server"); + if let Some(sender) = SHUTDOWN_SENDER.get() + && let Some(sender) = sender.lock().take() + { + sender.send(()).ok(); + } +}