From 93526a6fa1cb57dc0615e4e9d7cb641afa645b1a Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:17:54 +0800 Subject: [PATCH] fix(clash): take config write lock when changing mode change_clash_mode committed the clash draft and saved config.yaml without claiming CONFIG_WRITE_LOCK, so a concurrent patch_clash transaction could have its staged draft committed here or its file-snapshot rollback swallow the mode change. Take lock_config_write (waiting) rather than try_lock_config_write: two of the three callers (hotkey, tray) discard the result, so fail-fast would silently drop the switch, and the lock is only attempted after the mihomo patch already applied, where an Err would roll the frontend back while the core runs the new mode. --- Changelog.md | 1 + src-tauri/src/feat/clash.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index b6611ae59..efb9a999a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,7 @@ 🐞 修复问题 - 修复恢复备份的同时修改配置,可能导致配置丢失或错乱的问题 +- 修复切换规则模式与配置变更并发时可能丢失或错乱的问题 diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index 9b1d84b3c..853b35481 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -67,11 +67,14 @@ pub async fn change_clash_mode(mode: String) -> Result<(), String> { } let clash = Config::clash().await; - clash.edit_draft(|d| d.patch_config(&mapping)); - clash.apply(); - - let clash_data = clash.data_arc(); - if clash_data.save_config().await.is_ok() { + // Serialize with patch transactions: an unlocked commit can flush a staged draft mid-rollback. + let save_result = { + let _config_write = Config::lock_config_write().await; + clash.edit_draft(|d| d.patch_config(&mapping)); + clash.apply(); + clash.data_arc().save_config().await + }; + if save_result.is_ok() { announce(Refresh::Clash); tray::Tray::global().update_menu_and_icon().await; }