diff --git a/crates/clash-verge-draft/src/lib.rs b/crates/clash-verge-draft/src/lib.rs index 5d548d091..472dd430f 100644 --- a/crates/clash-verge-draft/src/lib.rs +++ b/crates/clash-verge-draft/src/lib.rs @@ -1,8 +1,8 @@ use parking_lot::RwLock; use std::sync::Arc; -pub type SharedBox = Arc>; -type DraftInner = (SharedBox, Option>); +pub type SharedDraft = Arc; +type DraftInner = (SharedDraft, Option>); /// Draft 管理:committed 与 optional draft 都以 Arc> 存储, // (committed_snapshot, optional_draft_snapshot) @@ -15,12 +15,12 @@ impl Draft { #[inline] pub fn new(data: T) -> Self { Self { - inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))), + inner: Arc::new(RwLock::new((Arc::new(data), None))), } } /// 以 Arc> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc) #[inline] - pub fn data_arc(&self) -> SharedBox { + pub fn data_arc(&self) -> SharedDraft { let guard = self.inner.read(); Arc::clone(&guard.0) } @@ -28,7 +28,7 @@ impl Draft { /// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照 /// 这也是零拷贝:只 clone Arc,不 clone T #[inline] - pub fn latest_arc(&self) -> SharedBox { + pub fn latest_arc(&self) -> SharedDraft { let guard = self.inner.read(); guard.1.clone().unwrap_or_else(|| Arc::clone(&guard.0)) } @@ -41,21 +41,11 @@ impl Draft { where F: FnOnce(&mut T) -> R, { - // 先获得写锁以创建或取出草稿 Arc 的可变引用位置 let mut guard = self.inner.write(); - let mut draft_arc = if guard.1.is_none() { - Arc::clone(&guard.0) - } else { - #[allow(clippy::unwrap_used)] - guard.1.take().unwrap() - }; - drop(guard); - // Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box(要求 T: Clone) - let boxed = Arc::make_mut(&mut draft_arc); // &mut Box - // 对 Box 解引用得到 &mut T - let result = f(&mut **boxed); - // 恢复修改后的草稿 Arc - self.inner.write().1 = Some(draft_arc); + let mut draft_arc = guard.1.take().unwrap_or_else(|| Arc::clone(&guard.0)); + let data_mut = Arc::make_mut(&mut draft_arc); + let result = f(data_mut); + guard.1 = Some(draft_arc); result } @@ -84,19 +74,19 @@ impl Draft { F: FnOnce(Box) -> Fut + Send, Fut: std::future::Future, R), anyhow::Error>> + Send, { - // 读取已提交快照(cheap Arc clone, 然后得到 Box 所有权 via clone) - // 注意:为了让闭包接收 Box 所有权,我们需要 clone 底层 T(不可避免) - let local: Box = { + let (local, original_arc) = { let guard = self.inner.read(); - // 将 Arc> 的 Box clone 出来(会调用 T: Clone) - (*guard.0).clone() + let arc = Arc::clone(&guard.0); + (Box::new((*arc).clone()), arc) }; - let (new_local, res) = f(local).await?; - - // 将新的 Box 放到已提交位置(包进 Arc) - self.inner.write().0 = Arc::new(new_local); - + let mut guard = self.inner.write(); + if !Arc::ptr_eq(&guard.0, &original_arc) { + return Err(anyhow::anyhow!( + "Optimistic lock failed: Committed data has changed during async operation" + )); + } + guard.0 = Arc::from(new_local); Ok(res) } } diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index 574023729..c11531d2d 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -16,7 +16,7 @@ use crate::{ ret_err, utils::{dirs, help}, }; -use clash_verge_draft::SharedBox; +use clash_verge_draft::SharedDraft; use clash_verge_logging::{Type, logging}; use scopeguard::defer; use smartstring::alias::String; @@ -26,7 +26,7 @@ use std::time::Duration; static CURRENT_SWITCHING_PROFILE: AtomicBool = AtomicBool::new(false); #[tauri::command] -pub async fn get_profiles() -> CmdResult> { +pub async fn get_profiles() -> CmdResult> { logging!(debug, Type::Cmd, "获取配置文件列表"); let draft = Config::profiles().await; let data = draft.data_arc(); diff --git a/src-tauri/src/cmd/verge.rs b/src-tauri/src/cmd/verge.rs index a20ad6d8a..fc83b098f 100644 --- a/src-tauri/src/cmd/verge.rs +++ b/src-tauri/src/cmd/verge.rs @@ -1,10 +1,10 @@ use super::CmdResult; use crate::{cmd::StringifyErr as _, config::IVerge, feat}; -use clash_verge_draft::SharedBox; +use clash_verge_draft::SharedDraft; /// 获取Verge配置 #[tauri::command] -pub async fn get_verge_config() -> CmdResult> { +pub async fn get_verge_config() -> CmdResult> { feat::fetch_verge_config().await.stringify_err() } diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 99bfaafc1..97916a4c5 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -106,7 +106,7 @@ async fn get_config_values() -> ConfigValues { ref verge_http_enabled, ref enable_dns_settings, .. - } = **verge_arc; + } = *verge_arc; let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = ( Some(verge_arc.get_valid_clash_core()), diff --git a/src-tauri/src/feat/config.rs b/src-tauri/src/feat/config.rs index eac968e9f..afb2ed165 100644 --- a/src-tauri/src/feat/config.rs +++ b/src-tauri/src/feat/config.rs @@ -4,7 +4,7 @@ use crate::{ module::{auto_backup::AutoBackupManager, lightweight}, }; use anyhow::Result; -use clash_verge_draft::SharedBox; +use clash_verge_draft::SharedDraft; use clash_verge_logging::{Type, logging, logging_error}; use serde_yaml_ng::Mapping; @@ -269,7 +269,7 @@ pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> { Ok(()) } -pub async fn fetch_verge_config() -> Result> { +pub async fn fetch_verge_config() -> Result> { let draft = Config::verge().await; let data = draft.data_arc(); Ok(data)