mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-17 16:00:35 +08:00
* fix(proxy): resolve system proxy toggle stuck and state desync (#6614) Backend: replace hand-rolled AtomicBool lock in update_sysproxy() with tokio::sync::Mutex so concurrent calls wait instead of being silently dropped, ensuring the latest config is always applied. Move blocking OS calls (networksetup on macOS) to spawn_blocking so they no longer stall the tokio worker thread pool. Frontend: release SwitchRow pendingRef in .finally() so the UI always re-syncs with the actual OS proxy state, and rollback checked on error. Closes #6614 * fix(changelog): add note for macOS proxy toggle freeze issue
This commit is contained in:
parent
607ef5a8a9
commit
ca8e350694
@ -6,6 +6,7 @@
|
|||||||
### 🐞 修复问题
|
### 🐞 修复问题
|
||||||
|
|
||||||
- 修复系统代理关闭后在 PAC 模式下未完全关闭
|
- 修复系统代理关闭后在 PAC 模式下未完全关闭
|
||||||
|
- 修复 macOS 开关代理时可能的卡死
|
||||||
|
|
||||||
### ✨ 新增功能
|
### ✨ 新增功能
|
||||||
|
|
||||||
|
|||||||
@ -15,9 +15,10 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use sysproxy::{Autoproxy, GuardMonitor, GuardType, Sysproxy};
|
use sysproxy::{Autoproxy, GuardMonitor, GuardType, Sysproxy};
|
||||||
|
use tokio::sync::Mutex as TokioMutex;
|
||||||
|
|
||||||
pub struct Sysopt {
|
pub struct Sysopt {
|
||||||
update_sysproxy: AtomicBool,
|
update_lock: TokioMutex<()>,
|
||||||
reset_sysproxy: AtomicBool,
|
reset_sysproxy: AtomicBool,
|
||||||
inner_proxy: Arc<RwLock<(Sysproxy, Autoproxy)>>,
|
inner_proxy: Arc<RwLock<(Sysproxy, Autoproxy)>>,
|
||||||
guard: Arc<RwLock<GuardMonitor>>,
|
guard: Arc<RwLock<GuardMonitor>>,
|
||||||
@ -26,7 +27,7 @@ pub struct Sysopt {
|
|||||||
impl Default for Sysopt {
|
impl Default for Sysopt {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
update_sysproxy: AtomicBool::new(false),
|
update_lock: TokioMutex::new(()),
|
||||||
reset_sysproxy: AtomicBool::new(false),
|
reset_sysproxy: AtomicBool::new(false),
|
||||||
inner_proxy: Arc::new(RwLock::new((Sysproxy::default(), Autoproxy::default()))),
|
inner_proxy: Arc::new(RwLock::new((Sysproxy::default(), Autoproxy::default()))),
|
||||||
guard: Arc::new(RwLock::new(GuardMonitor::new(GuardType::None, Duration::from_secs(30)))),
|
guard: Arc::new(RwLock::new(GuardMonitor::new(GuardType::None, Duration::from_secs(30)))),
|
||||||
@ -107,95 +108,70 @@ impl Sysopt {
|
|||||||
|
|
||||||
/// init the sysproxy
|
/// init the sysproxy
|
||||||
pub async fn update_sysproxy(&self) -> Result<()> {
|
pub async fn update_sysproxy(&self) -> Result<()> {
|
||||||
if self.update_sysproxy.load(Ordering::Acquire) {
|
let _lock = self.update_lock.lock().await;
|
||||||
logging!(info, Type::Core, "Sysproxy update is already in progress.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if self
|
|
||||||
.update_sysproxy
|
|
||||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
logging!(info, Type::Core, "Sysproxy update is already in progress.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
defer! {
|
|
||||||
logging!(info, Type::Core, "Sysproxy update completed.");
|
|
||||||
self.update_sysproxy.store(false, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
let verge = Config::verge().await.latest_arc();
|
let verge = Config::verge().await.latest_arc();
|
||||||
let port = {
|
let port = match verge.verge_mixed_port {
|
||||||
let verge_port = verge.verge_mixed_port;
|
|
||||||
match verge_port {
|
|
||||||
Some(port) => port,
|
Some(port) => port,
|
||||||
None => Config::clash().await.latest_arc().get_mixed_port(),
|
None => Config::clash().await.latest_arc().get_mixed_port(),
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let pac_port = IVerge::get_singleton_port();
|
let pac_port = IVerge::get_singleton_port();
|
||||||
|
let (sys_enable, pac_enable, proxy_host, proxy_guard) = (
|
||||||
let (sys_enable, pac_enable, proxy_host, proxy_guard) = {
|
|
||||||
(
|
|
||||||
verge.enable_system_proxy.unwrap_or_default(),
|
verge.enable_system_proxy.unwrap_or_default(),
|
||||||
verge.proxy_auto_config.unwrap_or_default(),
|
verge.proxy_auto_config.unwrap_or_default(),
|
||||||
verge.proxy_host.clone().unwrap_or_else(|| String::from("127.0.0.1")),
|
verge.proxy_host.clone().unwrap_or_else(|| String::from("127.0.0.1")),
|
||||||
verge.enable_proxy_guard.unwrap_or_default(),
|
verge.enable_proxy_guard.unwrap_or_default(),
|
||||||
)
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// 先 await, 避免持有锁导致的 Send 问题
|
// 先 await, 避免持有锁导致的 Send 问题
|
||||||
let bypass = get_bypass().await;
|
let bypass = get_bypass().await;
|
||||||
|
|
||||||
|
let (sys, auto, guard_type) = {
|
||||||
let (sys, auto) = &mut *self.inner_proxy.write();
|
let (sys, auto) = &mut *self.inner_proxy.write();
|
||||||
sys.enable = false;
|
|
||||||
sys.host = proxy_host.clone().into();
|
sys.host = proxy_host.clone().into();
|
||||||
sys.port = port;
|
sys.port = port;
|
||||||
sys.bypass = bypass.into();
|
sys.bypass = bypass.into();
|
||||||
|
|
||||||
auto.enable = false;
|
|
||||||
auto.url = format!("http://{proxy_host}:{pac_port}/commands/pac");
|
auto.url = format!("http://{proxy_host}:{pac_port}/commands/pac");
|
||||||
|
|
||||||
self.access_guard().write().set_guard_type(GuardType::None);
|
|
||||||
|
|
||||||
// `enable_system_proxy` is the master switch.
|
// `enable_system_proxy` is the master switch.
|
||||||
// When disabled, force clear both global proxy and PAC at OS level.
|
// When disabled, force clear both global proxy and PAC at OS level.
|
||||||
if !sys_enable {
|
let guard_type = if !sys_enable {
|
||||||
sys.set_system_proxy()?;
|
sys.enable = false;
|
||||||
auto.set_auto_proxy()?;
|
auto.enable = false;
|
||||||
return Ok(());
|
GuardType::None
|
||||||
}
|
} else if pac_enable {
|
||||||
|
|
||||||
if pac_enable {
|
|
||||||
sys.enable = false;
|
sys.enable = false;
|
||||||
auto.enable = true;
|
auto.enable = true;
|
||||||
sys.set_system_proxy()?;
|
|
||||||
auto.set_auto_proxy()?;
|
|
||||||
if proxy_guard {
|
if proxy_guard {
|
||||||
self.access_guard()
|
GuardType::Autoproxy(auto.clone())
|
||||||
.write()
|
} else {
|
||||||
.set_guard_type(GuardType::Autoproxy(auto.clone()));
|
GuardType::None
|
||||||
}
|
}
|
||||||
return Ok(());
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
if sys_enable {
|
|
||||||
auto.enable = false;
|
|
||||||
sys.enable = true;
|
sys.enable = true;
|
||||||
auto.set_auto_proxy()?;
|
auto.enable = false;
|
||||||
sys.set_system_proxy()?;
|
|
||||||
if proxy_guard {
|
if proxy_guard {
|
||||||
self.access_guard()
|
GuardType::Sysproxy(sys.clone())
|
||||||
.write()
|
} else {
|
||||||
.set_guard_type(GuardType::Sysproxy(sys.clone()));
|
GuardType::None
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(sys.clone(), auto.clone(), guard_type)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.access_guard().write().set_guard_type(guard_type);
|
||||||
|
|
||||||
|
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||||
|
sys.set_system_proxy()?;
|
||||||
|
auto.set_auto_proxy()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reset the sysproxy
|
/// reset the sysproxy
|
||||||
#[allow(clippy::unused_async)]
|
|
||||||
pub async fn reset_sysproxy(&self) -> Result<()> {
|
pub async fn reset_sysproxy(&self) -> Result<()> {
|
||||||
if self
|
if self
|
||||||
.reset_sysproxy
|
.reset_sysproxy
|
||||||
@ -212,11 +188,19 @@ impl Sysopt {
|
|||||||
self.access_guard().write().set_guard_type(GuardType::None);
|
self.access_guard().write().set_guard_type(GuardType::None);
|
||||||
|
|
||||||
// 直接关闭所有代理
|
// 直接关闭所有代理
|
||||||
|
let (sys, auto) = {
|
||||||
let (sys, auto) = &mut *self.inner_proxy.write();
|
let (sys, auto) = &mut *self.inner_proxy.write();
|
||||||
sys.enable = false;
|
sys.enable = false;
|
||||||
sys.set_system_proxy()?;
|
|
||||||
auto.enable = false;
|
auto.enable = false;
|
||||||
|
(sys.clone(), auto.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||||
|
sys.set_system_proxy()?;
|
||||||
auto.set_auto_proxy()?;
|
auto.set_auto_proxy()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,10 +67,14 @@ const SwitchRow = ({
|
|||||||
const handleChange = (_: React.ChangeEvent, value: boolean) => {
|
const handleChange = (_: React.ChangeEvent, value: boolean) => {
|
||||||
pendingRef.current = true
|
pendingRef.current = true
|
||||||
setChecked(value)
|
setChecked(value)
|
||||||
onToggle(value).catch((err: any) => {
|
onToggle(value)
|
||||||
pendingRef.current = false
|
.catch((err: any) => {
|
||||||
|
setChecked(active)
|
||||||
onError?.(err)
|
onError?.(err)
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pendingRef.current = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user