diff --git a/src-tauri/src/core/manager/lifecycle.rs b/src-tauri/src/core/manager/lifecycle.rs index 788c6c043..3d6a82cf4 100644 --- a/src-tauri/src/core/manager/lifecycle.rs +++ b/src-tauri/src/core/manager/lifecycle.rs @@ -23,19 +23,52 @@ enum HandoffOutcome { impl CoreManager { pub async fn start_core(&self) -> Result<()> { + let _life = self.lifecycle_lock.lock().await; + self.start_core_inner().await + } + + /// 调用者须已持有 `lifecycle_lock`。 + async fn start_core_inner(&self) -> Result<()> { + // 退出中不再启动新内核。 + if Handle::global().is_exiting() { + return Ok(()); + } + + // 已有内核运行时保持幂等,重启请走 restart_core。 + if !matches!(*self.get_running_mode(), RunningMode::NotRunning) { + logging!( + info, + Type::Core, + "start_core called while a core is running; treated as no-op" + ); + return Ok(()); + } + self.prepare_startup().await; defer! { self.after_core_process(); } + // 等待服务期间可能进入退出;未真正启动时回滚状态。 + if Handle::global().is_exiting() { + self.set_running_mode(RunningMode::NotRunning); + return Ok(()); + } + let result = match *self.get_running_mode() { RunningMode::Service => self.start_core_by_service().await, RunningMode::NotRunning | RunningMode::Sidecar => self.start_core_by_sidecar().await, }; + // 启动失败时回滚 mode,允许后续重试。 + if result.is_err() { + self.set_running_mode(RunningMode::NotRunning); + return result; + } + // 回退 sidecar 后,后台等待服务就绪再交接 #[cfg(target_os = "windows")] - if result.is_ok() && matches!(*self.get_running_mode(), RunningMode::Sidecar) { + if matches!(*self.get_running_mode(), RunningMode::Sidecar) { self.spawn_service_handoff_watcher().await; } @@ -43,6 +76,12 @@ impl CoreManager { } pub async fn stop_core(&self) -> Result<()> { + let _life = self.lifecycle_lock.lock().await; + self.stop_core_inner().await + } + + /// 调用者须已持有 `lifecycle_lock`。 + async fn stop_core_inner(&self) -> Result<()> { CLASH_LOGGER.clear_logs().await; defer! { self.after_core_process(); @@ -59,9 +98,11 @@ impl CoreManager { } pub async fn restart_core(&self) -> Result<()> { + // 持锁覆盖 stop+start,避免生命周期操作插入。 + let _life = self.lifecycle_lock.lock().await; logging!(info, Type::Core, "Restarting core"); - self.stop_core().await?; - self.start_core().await + self.stop_core_inner().await?; + self.start_core_inner().await } pub async fn change_core(&self, clash_core: &String) -> Result<(), String> { @@ -210,7 +251,7 @@ impl CoreManager { return HandoffOutcome::NotReady; } - // 与配置更新/重启互斥 + // 先抢 config 锁;失败则让位给正在进行的更新。 if !self.try_start_config_update() { return HandoffOutcome::NotReady; } @@ -218,7 +259,10 @@ impl CoreManager { self.finish_config_update(); } - // 加锁后复检运行模式和 TUN 状态 + // 再取 lifecycle 锁;锁序固定为 config→lifecycle。 + let _life = self.lifecycle_lock.lock().await; + + // 持锁后复检运行模式和 TUN 状态 if !matches!(*self.get_running_mode(), RunningMode::Sidecar) || !Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false) { diff --git a/src-tauri/src/core/manager/mod.rs b/src-tauri/src/core/manager/mod.rs index 940b4c5d4..c95fa5158 100644 --- a/src-tauri/src/core/manager/mod.rs +++ b/src-tauri/src/core/manager/mod.rs @@ -42,6 +42,9 @@ pub struct CoreManager { state: ArcSwap, last_update: ArcSwapOption, config_update_in_progress: AtomicBool, + // 串行化 start/stop/restart 和 sidecar→service 交接。 + // 锁序固定为 config_update_in_progress → lifecycle_lock。 + lifecycle_lock: tokio::sync::Mutex<()>, // sidecar→service 交接 watcher 单实例标志。 #[cfg(target_os = "windows")] handoff_watcher_running: AtomicBool, @@ -68,6 +71,7 @@ impl Default for CoreManager { state: ArcSwap::new(Arc::new(State::default())), last_update: ArcSwapOption::new(None), config_update_in_progress: AtomicBool::new(false), + lifecycle_lock: tokio::sync::Mutex::new(()), #[cfg(target_os = "windows")] handoff_watcher_running: AtomicBool::new(false), }