mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-08 11:02:08 +08:00
fix(core): serialize core lifecycle transitions
Add a lifecycle lock around core start, stop, restart, and service handoff paths. Make start_core idempotent, skip startup while exiting, and roll back running mode after startup failures so later retries are not blocked.
This commit is contained in:
@@ -23,19 +23,52 @@ enum HandoffOutcome {
|
||||
|
||||
impl CoreManager {
|
||||
pub async fn start_core(&self) -> Result<()> {
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
self.start_core_inner().await
|
||||
}
|
||||
|
||||
/// 调用者须已持有 `lifecycle_lock`。
|
||||
async fn start_core_inner(&self) -> Result<()> {
|
||||
// 退出中不再启动新内核。
|
||||
if Handle::global().is_exiting() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 已有内核运行时保持幂等,重启请走 restart_core。
|
||||
if !matches!(*self.get_running_mode(), RunningMode::NotRunning) {
|
||||
logging!(
|
||||
info,
|
||||
Type::Core,
|
||||
"start_core called while a core is running; treated as no-op"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.prepare_startup().await;
|
||||
defer! {
|
||||
self.after_core_process();
|
||||
}
|
||||
|
||||
// 等待服务期间可能进入退出;未真正启动时回滚状态。
|
||||
if Handle::global().is_exiting() {
|
||||
self.set_running_mode(RunningMode::NotRunning);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let result = match *self.get_running_mode() {
|
||||
RunningMode::Service => self.start_core_by_service().await,
|
||||
RunningMode::NotRunning | RunningMode::Sidecar => self.start_core_by_sidecar().await,
|
||||
};
|
||||
|
||||
// 启动失败时回滚 mode,允许后续重试。
|
||||
if result.is_err() {
|
||||
self.set_running_mode(RunningMode::NotRunning);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 回退 sidecar 后,后台等待服务就绪再交接
|
||||
#[cfg(target_os = "windows")]
|
||||
if result.is_ok() && matches!(*self.get_running_mode(), RunningMode::Sidecar) {
|
||||
if matches!(*self.get_running_mode(), RunningMode::Sidecar) {
|
||||
self.spawn_service_handoff_watcher().await;
|
||||
}
|
||||
|
||||
@@ -43,6 +76,12 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
pub async fn stop_core(&self) -> Result<()> {
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
self.stop_core_inner().await
|
||||
}
|
||||
|
||||
/// 调用者须已持有 `lifecycle_lock`。
|
||||
async fn stop_core_inner(&self) -> Result<()> {
|
||||
CLASH_LOGGER.clear_logs().await;
|
||||
defer! {
|
||||
self.after_core_process();
|
||||
@@ -59,9 +98,11 @@ impl CoreManager {
|
||||
}
|
||||
|
||||
pub async fn restart_core(&self) -> Result<()> {
|
||||
// 持锁覆盖 stop+start,避免生命周期操作插入。
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
logging!(info, Type::Core, "Restarting core");
|
||||
self.stop_core().await?;
|
||||
self.start_core().await
|
||||
self.stop_core_inner().await?;
|
||||
self.start_core_inner().await
|
||||
}
|
||||
|
||||
pub async fn change_core(&self, clash_core: &String) -> Result<(), String> {
|
||||
@@ -210,7 +251,7 @@ impl CoreManager {
|
||||
return HandoffOutcome::NotReady;
|
||||
}
|
||||
|
||||
// 与配置更新/重启互斥
|
||||
// 先抢 config 锁;失败则让位给正在进行的更新。
|
||||
if !self.try_start_config_update() {
|
||||
return HandoffOutcome::NotReady;
|
||||
}
|
||||
@@ -218,7 +259,10 @@ impl CoreManager {
|
||||
self.finish_config_update();
|
||||
}
|
||||
|
||||
// 加锁后复检运行模式和 TUN 状态
|
||||
// 再取 lifecycle 锁;锁序固定为 config→lifecycle。
|
||||
let _life = self.lifecycle_lock.lock().await;
|
||||
|
||||
// 持锁后复检运行模式和 TUN 状态
|
||||
if !matches!(*self.get_running_mode(), RunningMode::Sidecar)
|
||||
|| !Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,9 @@ pub struct CoreManager {
|
||||
state: ArcSwap<State>,
|
||||
last_update: ArcSwapOption<Instant>,
|
||||
config_update_in_progress: AtomicBool,
|
||||
// 串行化 start/stop/restart 和 sidecar→service 交接。
|
||||
// 锁序固定为 config_update_in_progress → lifecycle_lock。
|
||||
lifecycle_lock: tokio::sync::Mutex<()>,
|
||||
// sidecar→service 交接 watcher 单实例标志。
|
||||
#[cfg(target_os = "windows")]
|
||||
handoff_watcher_running: AtomicBool,
|
||||
@@ -68,6 +71,7 @@ impl Default for CoreManager {
|
||||
state: ArcSwap::new(Arc::new(State::default())),
|
||||
last_update: ArcSwapOption::new(None),
|
||||
config_update_in_progress: AtomicBool::new(false),
|
||||
lifecycle_lock: tokio::sync::Mutex::new(()),
|
||||
#[cfg(target_os = "windows")]
|
||||
handoff_watcher_running: AtomicBool::new(false),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user