mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
af8f907467
Concurrent failover switches for the same app could cause is_current, local settings, and Live backup to point at different providers. - Add SwitchLockManager with per-app mutexes (different apps still parallel) - Unify scattered switch logic into ProxyService::hot_switch_provider - Fix TOCTOU in set_current_provider via mutate_settings - Add logical_target_changed to skip redundant UI refreshes - Add tests for serialization and restore-waits-for-switch scenarios
43 lines
1.3 KiB
Rust
43 lines
1.3 KiB
Rust
//! Per-app switch lock
|
|
//!
|
|
//! 确保同一应用同时只有一个供应商切换操作在执行,
|
|
//! 防止并发切换导致 is_current 与 Live 备份不一致。
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::{Mutex, OwnedMutexGuard, RwLock};
|
|
|
|
/// 每个应用类型一把互斥锁,保证同一应用的切换操作串行执行。
|
|
///
|
|
/// 不同应用之间(如 Claude 和 Codex)可以并行切换。
|
|
#[derive(Clone, Default)]
|
|
pub struct SwitchLockManager {
|
|
locks: Arc<RwLock<HashMap<String, Arc<Mutex<()>>>>>,
|
|
}
|
|
|
|
impl SwitchLockManager {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// 获取指定应用的切换锁。
|
|
///
|
|
/// 返回 `OwnedMutexGuard`,持有期间同一 `app_type` 的其他切换会排队等待。
|
|
pub async fn lock_for_app(&self, app_type: &str) -> OwnedMutexGuard<()> {
|
|
let lock = {
|
|
let locks = self.locks.read().await;
|
|
if let Some(lock) = locks.get(app_type) {
|
|
lock.clone()
|
|
} else {
|
|
drop(locks);
|
|
let mut locks = self.locks.write().await;
|
|
locks
|
|
.entry(app_type.to_string())
|
|
.or_insert_with(|| Arc::new(Mutex::new(())))
|
|
.clone()
|
|
}
|
|
};
|
|
lock.lock_owned().await
|
|
}
|
|
}
|