From 720dc13e96060fd8f74faab8a4815845522cab0a Mon Sep 17 00:00:00 2001 From: Tunglies Date: Wed, 7 Oct 2026 07:38:56 +0000 Subject: [PATCH] refactor(backend): downgrade lock (#8135) * refactor(updater): drop write-only pending_* slots * refactor(notify): fold sequence counter into entries lock * refactor(port): fold startup-core-blocked flag into reason slot * refactor(service): drop redundant provider sync serial lock * refactor(port): store startup block reason in a OnceLock * refactor(core): replace macOS network monitor slot with armed latch --- src-tauri/src/config/port.rs | 17 ++-------- src-tauri/src/core/notify/notification.rs | 41 +++++++++++++---------- src-tauri/src/core/service.rs | 3 +- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src-tauri/src/config/port.rs b/src-tauri/src/config/port.rs index 82d3ff29c..76fb3eaa1 100644 --- a/src-tauri/src/config/port.rs +++ b/src-tauri/src/config/port.rs @@ -18,12 +18,7 @@ use clash_verge_logging::{Type, logging}; use once_cell::sync::Lazy; use parking_lot::Mutex; use serde_yaml_ng::Value; -use std::{ - collections::HashSet, - net::SocketAddr, - str::FromStr as _, - sync::atomic::{AtomicBool, Ordering}, -}; +use std::{collections::HashSet, net::SocketAddr, str::FromStr as _}; #[derive(Clone, Copy)] struct MixedPortFallback { @@ -32,8 +27,7 @@ struct MixedPortFallback { } static PENDING_FALLBACK_NOTICE: Lazy>> = Lazy::new(|| Mutex::new(None)); -static STARTUP_CORE_BLOCKED: AtomicBool = AtomicBool::new(false); -static STARTUP_CORE_BLOCK_REASON: Lazy>> = Lazy::new(|| Mutex::new(None)); +static STARTUP_CORE_BLOCK_REASON: Mutex> = Mutex::new(None); impl Config { pub(crate) async fn resolve_startup_mixed_port() -> Result { @@ -148,17 +142,12 @@ impl Config { } pub(crate) fn block_startup_core(error: &anyhow::Error) { - STARTUP_CORE_BLOCKED.store(true, Ordering::Release); *STARTUP_CORE_BLOCK_REASON.lock() = Some(error.to_string()); report_fallback_error(error.to_string()); } pub(crate) fn startup_core_block_reason() -> Option { - if STARTUP_CORE_BLOCKED.load(Ordering::Acquire) { - STARTUP_CORE_BLOCK_REASON.lock().clone() - } else { - None - } + STARTUP_CORE_BLOCK_REASON.lock().clone() } pub(crate) fn notify_startup_mixed_port_fallback() { diff --git a/src-tauri/src/core/notify/notification.rs b/src-tauri/src/core/notify/notification.rs index a1ba571dd..cc3c5947c 100644 --- a/src-tauri/src/core/notify/notification.rs +++ b/src-tauri/src/core/notify/notification.rs @@ -5,11 +5,7 @@ use parking_lot::Mutex; use serde_json::json; use smartstring::alias::String; use std::sync::Arc; -use std::{ - collections::HashMap, - future::Future, - sync::atomic::{AtomicU64, Ordering}, -}; +use std::{collections::HashMap, future::Future}; use tauri::{AppHandle, Emitter as _, Manager as _, WebviewWindow}; #[derive(Debug, Clone)] @@ -91,16 +87,23 @@ pub struct PendingFailure { /// Non-destructive pending state, indexed by stable code. #[derive(Debug, Default)] struct FailureTable { - entries: Mutex>, - sequence: AtomicU64, + entries: Mutex, +} + +#[derive(Debug, Default)] +struct FailureEntries { + map: HashMap, + sequence: u64, } impl FailureTable { fn record(&self, operation: FailedOperation, code: &str, detail: String) { // Sequence assignment and replacement must share one ordering lock. let mut entries = self.entries.lock(); + entries.sequence = entries.sequence.wrapping_add(1); // Preserve an unanswered request over a later restore. let operation = entries + .map .get(code) .filter(|existing| existing.operation.outranks(operation)) .map_or(operation, |existing| existing.operation); @@ -108,13 +111,13 @@ impl FailureTable { code: code.into(), detail, operation, - sequence: self.sequence.fetch_add(1, Ordering::AcqRel).wrapping_add(1), + sequence: entries.sequence, }; - entries.insert(code.into(), failure); + entries.map.insert(code.into(), failure); } fn snapshot(&self) -> Vec { - let mut failures: Vec = self.entries.lock().values().cloned().collect(); + let mut failures: Vec = self.entries.lock().map.values().cloned().collect(); failures.sort_by_key(|failure| failure.sequence); failures } @@ -122,17 +125,21 @@ impl FailureTable { /// Return whether a guard failure was retired. fn retire_guard(&self) -> bool { let mut entries = self.entries.lock(); - let before = entries.len(); - entries.retain(|_, failure| failure.operation != FailedOperation::SystemProxyGuard); - before != entries.len() + let before = entries.map.len(); + entries + .map + .retain(|_, failure| failure.operation != FailedOperation::SystemProxyGuard); + before != entries.map.len() } /// Return whether any proxy failure was retired. fn retire_system_proxy(&self, asked: FailedOperation) -> bool { let mut entries = self.entries.lock(); - let before = entries.len(); - entries.retain(|_, failure| !failure.operation.retired_by_success_of(asked)); - before != entries.len() + let before = entries.map.len(); + entries + .map + .retain(|_, failure| !failure.operation.retired_by_success_of(asked)); + before != entries.map.len() } } @@ -279,7 +286,7 @@ pub fn record_failure(operation: FailedOperation, code: &str, detail: impl Into< } pub fn has_pending_failure(code: &str) -> bool { - PENDING_FAILURES.entries.lock().contains_key(code) + PENDING_FAILURES.entries.lock().map.contains_key(code) } /// Return unresolved failures oldest first without clearing them. diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index a65fe3c21..1350407ef 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -1045,7 +1045,8 @@ fn decode_hex(encoded: &str) -> Result> { } static PROVIDER_SYNC_QUEUED: AtomicBool = AtomicBool::new(false); -static PROVIDER_SYNC_SERIAL: Lazy> = Lazy::new(|| tokio::sync::Mutex::new(())); +// Serializing the whole read-and-publish pass prevents an older cache from replacing a newer one. +static PROVIDER_SYNC_SERIAL: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); static SYNC_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(0); const RUNTIME_PROVIDER_SYNC_ATTEMPTS: u32 = 4; const CONTENT_COMPARE_CHUNK: usize = 64 * 1024;