From 4babc727549ae6f69d686826d02075942356c034 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:32:36 +0800 Subject: [PATCH] feat: integrate arc-swap for improved concurrency in CoreManager and Hotkey(onMac) management --- src-tauri/Cargo.lock | 7 +++++ src-tauri/Cargo.toml | 1 + src-tauri/src/core/backup.rs | 35 ++++++++++++----------- src-tauri/src/core/hotkey.rs | 22 ++++++--------- src-tauri/src/core/manager/config.rs | 8 +++--- src-tauri/src/core/manager/mod.rs | 42 ++++++++++++++++++++-------- src-tauri/src/core/manager/state.rs | 3 +- src-tauri/src/lib.rs | 19 +++++++++---- src-tauri/src/utils/resolve/mod.rs | 2 +- 9 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4c01e031..d433be36 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -152,6 +152,12 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "arraydeque" version = "0.5.1" @@ -1093,6 +1099,7 @@ version = "2.4.3" dependencies = [ "aes-gcm", "anyhow", + "arc-swap", "async-trait", "backoff", "base64 0.22.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b5e3a424..db31304e 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -86,6 +86,7 @@ smartstring = { version = "1.0.1", features = ["serde"] } clash_verge_service_ipc = { version = "2.0.21", features = [ "client", ], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" } +arc-swap = "1.7.1" [target.'cfg(windows)'.dependencies] runas = "=1.2.0" diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index cf90bdd1..6e48a137 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -6,8 +6,8 @@ use crate::{ utils::{dirs, logging::Type}, }; use anyhow::Error; +use arc_swap::{ArcSwap, ArcSwapOption}; use once_cell::sync::OnceCell; -use parking_lot::Mutex; use reqwest_dav::list_cmd::{ListEntity, ListFile}; use smartstring::alias::String; use std::{ @@ -56,24 +56,24 @@ impl Operation { } pub struct WebDavClient { - config: Arc>>, - clients: Arc>>, + config: Arc>, + clients: Arc>>, } impl WebDavClient { pub fn global() -> &'static WebDavClient { static WEBDAV_CLIENT: OnceCell = OnceCell::new(); WEBDAV_CLIENT.get_or_init(|| WebDavClient { - config: Arc::new(Mutex::new(None)), - clients: Arc::new(Mutex::new(HashMap::new())), + config: Arc::new(ArcSwapOption::new(None)), + clients: Arc::new(ArcSwap::new(Arc::new(HashMap::new()))), }) } async fn get_client(&self, op: Operation) -> Result { // 先尝试从缓存获取 { - let clients = self.clients.lock(); - if let Some(client) = clients.get(&op) { + let clients_map = self.clients.load(); + if let Some(client) = clients_map.get(&op) { return Ok(client.clone()); } } @@ -81,10 +81,10 @@ impl WebDavClient { // 获取或创建配置 let config = { // 首先检查是否已有配置 - let existing_config = self.config.lock().as_ref().cloned(); + let existing_config = self.config.load(); - if let Some(cfg) = existing_config { - cfg + if let Some(cfg_arc) = existing_config.clone() { + (*cfg_arc).clone() } else { // 释放锁后获取异步配置 let verge = Config::verge().await.latest_ref().clone(); @@ -106,8 +106,8 @@ impl WebDavClient { password: verge.webdav_password.unwrap_or_default(), }; - // 重新获取锁并存储配置 - *self.config.lock() = Some(config.clone()); + // 存储配置到 ArcSwapOption + self.config.store(Some(Arc::new(config.clone()))); config } }; @@ -161,18 +161,19 @@ impl WebDavClient { } } - // 缓存客户端 + // 缓存客户端(替换 Arc>> 的写法) { - let mut clients = self.clients.lock(); - clients.insert(op, client.clone()); + let mut map = (**self.clients.load()).clone(); + map.insert(op, client.clone()); + self.clients.store(map.into()); } Ok(client) } pub fn reset(&self) { - *self.config.lock() = None; - self.clients.lock().clear(); + self.config.store(None); + self.clients.store(Arc::new(HashMap::new())); } pub async fn upload(&self, file_path: PathBuf, file_name: String) -> Result<(), Error> { diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index d9429c76..f33a2cbc 100755 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -5,7 +5,7 @@ use crate::{ singleton_with_logging, utils::logging::Type, }; use anyhow::{Result, bail}; -use parking_lot::Mutex; +use arc_swap::ArcSwap; use smartstring::alias::String; use std::{collections::HashMap, fmt, str::FromStr, sync::Arc}; use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState}; @@ -93,13 +93,13 @@ impl SystemHotkey { } pub struct Hotkey { - current: Arc>>, + current: ArcSwap>, } impl Hotkey { fn new() -> Self { Self { - current: Arc::new(Mutex::new(Vec::new())), + current: ArcSwap::new(Arc::new(Vec::new())), } } @@ -272,9 +272,9 @@ impl Hotkey { singleton_with_logging!(Hotkey, INSTANCE, "Hotkey"); impl Hotkey { - pub async fn init(&self) -> Result<()> { + pub async fn init(&self, skip: bool) -> Result<()> { let verge = Config::verge().await; - let enable_global_hotkey = verge.latest_ref().enable_global_hotkey.unwrap_or(true); + let enable_global_hotkey = !skip && verge.latest_ref().enable_global_hotkey.unwrap_or(true); logging!( debug, @@ -283,10 +283,6 @@ impl Hotkey { enable_global_hotkey ); - if !enable_global_hotkey { - return Ok(()); - } - // Extract hotkeys data before async operations let hotkeys = verge.latest_ref().hotkeys.as_ref().cloned(); @@ -344,7 +340,7 @@ impl Hotkey { } } } - self.current.lock().clone_from(&hotkeys); + self.current.store(Arc::new(hotkeys)); } else { logging!(debug, Type::Hotkey, "No hotkeys configured"); } @@ -375,8 +371,8 @@ impl Hotkey { pub async fn update(&self, new_hotkeys: Vec) -> Result<()> { // Extract current hotkeys before async operations - let current_hotkeys = self.current.lock().clone(); - let old_map = Self::get_map_from_vec(¤t_hotkeys); + let current_hotkeys = &*self.current.load(); + let old_map = Self::get_map_from_vec(current_hotkeys); let new_map = Self::get_map_from_vec(&new_hotkeys); let (del, add) = Self::get_diff(old_map, new_map); @@ -390,7 +386,7 @@ impl Hotkey { } // Update the current hotkeys after all async operations - *self.current.lock() = new_hotkeys; + self.current.store(Arc::new(new_hotkeys)); Ok(()) } diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index 63455db8..b0e79bca 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -44,15 +44,15 @@ impl CoreManager { fn should_update_config(&self) -> Result { let now = Instant::now(); - let mut last = self.last_update.lock(); + let last = self.get_last_update(); - if let Some(last_time) = *last - && now.duration_since(last_time) < timing::CONFIG_UPDATE_DEBOUNCE + if let Some(last_time) = last + && now.duration_since(*last_time) < timing::CONFIG_UPDATE_DEBOUNCE { return Ok(false); } - *last = Some(now); + self.set_last_update(now); Ok(true) } diff --git a/src-tauri/src/core/manager/mod.rs b/src-tauri/src/core/manager/mod.rs index fbd49d05..8f41af64 100644 --- a/src-tauri/src/core/manager/mod.rs +++ b/src-tauri/src/core/manager/mod.rs @@ -3,7 +3,7 @@ mod lifecycle; mod state; use anyhow::Result; -use parking_lot::Mutex; +use arc_swap::{ArcSwap, ArcSwapOption}; use std::{fmt, sync::Arc, time::Instant}; use crate::process::CommandChildGuard; @@ -28,21 +28,21 @@ impl fmt::Display for RunningMode { #[derive(Debug)] pub struct CoreManager { - state: Arc>, - last_update: Arc>>, + state: ArcSwap, + last_update: ArcSwapOption, } #[derive(Debug)] struct State { - running_mode: Arc, - child_sidecar: Option, + running_mode: ArcSwap, + child_sidecar: ArcSwapOption, } impl Default for State { fn default() -> Self { Self { - running_mode: Arc::new(RunningMode::NotRunning), - child_sidecar: None, + running_mode: ArcSwap::new(Arc::new(RunningMode::NotRunning)), + child_sidecar: ArcSwapOption::new(None), } } } @@ -50,23 +50,41 @@ impl Default for State { impl Default for CoreManager { fn default() -> Self { Self { - state: Arc::new(Mutex::new(State::default())), - last_update: Arc::new(Mutex::new(None)), + state: ArcSwap::new(Arc::new(State::default())), + last_update: ArcSwapOption::new(None), } } } impl CoreManager { pub fn get_running_mode(&self) -> Arc { - Arc::clone(&self.state.lock().running_mode) + Arc::clone(&self.state.load().running_mode.load()) + } + + pub fn take_child_sidecar(&self) -> Option { + self.state + .load() + .child_sidecar + .swap(None) + .and_then(|arc| Arc::try_unwrap(arc).ok()) + } + + pub fn get_last_update(&self) -> Option> { + self.last_update.load_full() } pub fn set_running_mode(&self, mode: RunningMode) { - self.state.lock().running_mode = Arc::new(mode); + let state = self.state.load(); + state.running_mode.store(Arc::new(mode)); } pub fn set_running_child_sidecar(&self, child: CommandChildGuard) { - self.state.lock().child_sidecar = Some(child); + let state = self.state.load(); + state.child_sidecar.store(Some(Arc::new(child))); + } + + pub fn set_last_update(&self, time: Instant) { + self.last_update.store(Some(Arc::new(time))); } pub async fn init(&self) -> Result<()> { diff --git a/src-tauri/src/core/manager/state.rs b/src-tauri/src/core/manager/state.rs index d38a148c..af92ce18 100644 --- a/src-tauri/src/core/manager/state.rs +++ b/src-tauri/src/core/manager/state.rs @@ -93,8 +93,7 @@ impl CoreManager { defer! { self.set_running_mode(RunningMode::NotRunning); } - let mut state = self.state.lock(); - if let Some(child) = state.child_sidecar.take() { + if let Some(child) = self.take_child_sidecar() { let pid = child.pid(); drop(child); logging!(trace, Type::Core, "Sidecar stopped (PID: {:?})", pid); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c6db41c0..da6c5710 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -334,10 +334,7 @@ pub fn run() { .register_system_hotkey(SystemHotkey::CmdW) .await; } - - if !is_enable_global_hotkey { - let _ = hotkey::Hotkey::global().init().await; - } + let _ = hotkey::Hotkey::global().init(true).await; return; } @@ -358,8 +355,18 @@ pub fn run() { #[cfg(target_os = "macos")] { use crate::core::hotkey::SystemHotkey; - let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ); - let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW); + AsyncHandler::spawn(move || async move { + let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ); + let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW); + let is_enable_global_hotkey = Config::verge() + .await + .latest_ref() + .enable_global_hotkey + .unwrap_or(true); + if !is_enable_global_hotkey { + let _ = hotkey::Hotkey::global().reset(); + } + }); } } } diff --git a/src-tauri/src/utils/resolve/mod.rs b/src-tauri/src/utils/resolve/mod.rs index cb0989b1..4055ecd0 100644 --- a/src-tauri/src/utils/resolve/mod.rs +++ b/src-tauri/src/utils/resolve/mod.rs @@ -120,7 +120,7 @@ pub(super) async fn init_timer() { } pub(super) async fn init_hotkey() { - logging_error!(Type::Setup, Hotkey::global().init().await); + logging_error!(Type::Setup, Hotkey::global().init(false).await); } pub(super) async fn init_auto_lightweight_boot() {