From 08fd2ce68b54239e3233a0bcd6919423f6ca3e10 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Thu, 28 May 2026 17:41:39 +0800 Subject: [PATCH] fix(core): serialize runtime config updates --- src-tauri/src/cmd/runtime.rs | 8 +++----- src-tauri/src/core/manager/config.rs | 29 ++++++++++++++++++++++++++-- src-tauri/src/core/manager/mod.rs | 19 +++++++++++++++++- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/cmd/runtime.rs b/src-tauri/src/cmd/runtime.rs index 24906759e..d048a776c 100644 --- a/src-tauri/src/cmd/runtime.rs +++ b/src-tauri/src/cmd/runtime.rs @@ -94,12 +94,10 @@ pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> Cm /// 更新运行时链式代理配置 #[tauri::command] pub async fn update_proxy_chain_config_in_runtime(proxy_chain_config: Option) -> CmdResult<()> { + match CoreManager::global() + .update_runtime_config(|d| d.update_proxy_chain_config(proxy_chain_config)) + .await { - let runtime = Config::runtime().await; - runtime.edit_draft(|d| d.update_proxy_chain_config(proxy_chain_config)); - // 我们需要在 CoreManager 中验证并应用配置,这里不应该直接调用 runtime.apply() - } - match CoreManager::global().apply_generate_config().await { Ok(outcome) if outcome.is_valid() => {} Ok(outcome) => logging!( warn, diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index cf2267ef6..ddf40ee8c 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -10,6 +10,7 @@ use crate::{ }; use anyhow::{Result, anyhow}; use clash_verge_logging::{Type, logging}; +use scopeguard::defer; use smartstring::alias::String; use std::{collections::HashSet, path::PathBuf, time::Instant}; use tauri_plugin_mihomo::Error as MihomoError; @@ -45,6 +46,14 @@ impl CoreManager { }); } + if !self.try_start_config_update() { + logging!(info, Type::Core, "Configuration update is already running"); + return Ok(ValidationOutcome::Busy); + } + defer! { + self.finish_config_update(); + } + if !force && !self.should_update_config() { logging!(debug, Type::Core, "Skipping config update due to debounce"); return Ok(ValidationOutcome::Skipped { @@ -89,10 +98,26 @@ impl CoreManager { return Ok(ValidationOutcome::invalid_from_message(message)); } - self.apply_generate_config().await + self.apply_generate_config_inner().await } - pub async fn apply_generate_config(&self) -> Result { + pub(crate) async fn update_runtime_config(&self, f: F) -> Result + where + F: FnOnce(&mut IRuntime), + { + if !self.try_start_config_update() { + logging!(info, Type::Core, "Configuration update is already running"); + return Ok(ValidationOutcome::Busy); + } + defer! { + self.finish_config_update(); + } + + Config::runtime().await.edit_draft(f); + self.apply_generate_config_inner().await + } + + async fn apply_generate_config_inner(&self) -> Result { match CoreConfigValidator::global().validate_config_outcome().await { Ok(outcome) if outcome.is_valid() => { let run_path = Config::generate_file(ConfigType::Run).await?; diff --git a/src-tauri/src/core/manager/mod.rs b/src-tauri/src/core/manager/mod.rs index 93dc0bbd8..18d63725c 100644 --- a/src-tauri/src/core/manager/mod.rs +++ b/src-tauri/src/core/manager/mod.rs @@ -6,7 +6,14 @@ use anyhow::Result; use arc_swap::{ArcSwap, ArcSwapOption}; use clash_verge_logger::AsyncLogger; use once_cell::sync::Lazy; -use std::{fmt, sync::Arc, time::Instant}; +use std::{ + fmt, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, + time::Instant, +}; use tauri_plugin_shell::process::CommandChild; use crate::singleton; @@ -34,6 +41,7 @@ impl fmt::Display for RunningMode { pub struct CoreManager { state: ArcSwap, last_update: ArcSwapOption, + config_update_in_progress: AtomicBool, } #[derive(Debug)] @@ -56,6 +64,7 @@ impl Default for CoreManager { Self { state: ArcSwap::new(Arc::new(State::default())), last_update: ArcSwapOption::new(None), + config_update_in_progress: AtomicBool::new(false), } } } @@ -95,6 +104,14 @@ impl CoreManager { self.last_update.store(Some(Arc::new(time))); } + fn try_start_config_update(&self) -> bool { + !self.config_update_in_progress.swap(true, Ordering::AcqRel) + } + + fn finish_config_update(&self) { + self.config_update_in_progress.store(false, Ordering::Release); + } + pub async fn init(&self) -> Result<()> { self.start_core().await?; Ok(())