mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-28 07:14:40 +08:00
feat: integrate arc-swap for improved concurrency in CoreManager and Hotkey(onMac) management
This commit is contained in:
7
src-tauri/Cargo.lock
generated
7
src-tauri/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<Mutex<Option<WebDavConfig>>>,
|
||||
clients: Arc<Mutex<HashMap<Operation, reqwest_dav::Client>>>,
|
||||
config: Arc<ArcSwapOption<WebDavConfig>>,
|
||||
clients: Arc<ArcSwap<HashMap<Operation, reqwest_dav::Client>>>,
|
||||
}
|
||||
|
||||
impl WebDavClient {
|
||||
pub fn global() -> &'static WebDavClient {
|
||||
static WEBDAV_CLIENT: OnceCell<WebDavClient> = 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<reqwest_dav::Client, Error> {
|
||||
// 先尝试从缓存获取
|
||||
{
|
||||
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<Mutex<HashMap<...>>> 的写法)
|
||||
{
|
||||
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> {
|
||||
|
||||
@@ -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<Mutex<Vec<String>>>,
|
||||
current: ArcSwap<Vec<String>>,
|
||||
}
|
||||
|
||||
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<String>) -> 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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -44,15 +44,15 @@ impl CoreManager {
|
||||
|
||||
fn should_update_config(&self) -> Result<bool> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Mutex<State>>,
|
||||
last_update: Arc<Mutex<Option<Instant>>>,
|
||||
state: ArcSwap<State>,
|
||||
last_update: ArcSwapOption<Instant>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
running_mode: Arc<RunningMode>,
|
||||
child_sidecar: Option<CommandChildGuard>,
|
||||
running_mode: ArcSwap<RunningMode>,
|
||||
child_sidecar: ArcSwapOption<CommandChildGuard>,
|
||||
}
|
||||
|
||||
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<RunningMode> {
|
||||
Arc::clone(&self.state.lock().running_mode)
|
||||
Arc::clone(&self.state.load().running_mode.load())
|
||||
}
|
||||
|
||||
pub fn take_child_sidecar(&self) -> Option<CommandChildGuard> {
|
||||
self.state
|
||||
.load()
|
||||
.child_sidecar
|
||||
.swap(None)
|
||||
.and_then(|arc| Arc::try_unwrap(arc).ok())
|
||||
}
|
||||
|
||||
pub fn get_last_update(&self) -> Option<Arc<Instant>> {
|
||||
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<()> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user