use super::{CoreManager, RunningMode}; use crate::cmd::StringifyErr as _; use crate::config::{Config, IVerge}; use crate::core::handle::Handle; use crate::core::manager::CLASH_LOGGER; use crate::core::service::{SERVICE_MANAGER, ServiceStatus}; use anyhow::Result; use clash_verge_logging::{Type, logging}; use scopeguard::defer; use smartstring::alias::String; use tauri_plugin_clash_verge_sysinfo; /// sidecar→service 交接结果 #[cfg(target_os = "windows")] enum HandoffOutcome { /// 服务尚未就绪 NotReady, /// 已完成或无需交接 Done, /// 交接失败并已回退 Failed, } impl CoreManager { pub async fn start_core(&self) -> Result<()> { self.prepare_startup().await; defer! { self.after_core_process(); } 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, }; // 回退 sidecar 后,后台等待服务就绪再交接 #[cfg(target_os = "windows")] if result.is_ok() && matches!(*self.get_running_mode(), RunningMode::Sidecar) { self.spawn_service_handoff_watcher().await; } result } pub async fn stop_core(&self) -> Result<()> { CLASH_LOGGER.clear_logs().await; defer! { self.after_core_process(); } match *self.get_running_mode() { RunningMode::Service => self.stop_core_by_service().await, RunningMode::Sidecar => { self.stop_core_by_sidecar(); Ok(()) } RunningMode::NotRunning => Ok(()), } } pub async fn restart_core(&self) -> Result<()> { logging!(info, Type::Core, "Restarting core"); self.stop_core().await?; self.start_core().await } pub async fn change_core(&self, clash_core: &String) -> Result<(), String> { if !IVerge::VALID_CLASH_CORES.contains(&clash_core.as_str()) { return Err(format!("Invalid clash core: {}", clash_core).into()); } Config::verge().await.edit_draft(|d| { d.clash_core = Some(clash_core.to_owned()); }); Config::verge().await.apply(); let verge_data = Config::verge().await.latest_arc(); verge_data.save_file().await.map_err(|e| e.to_string())?; self.update_config_checked().await.stringify_err()?; Ok(()) } async fn prepare_startup(&self) { #[cfg(target_os = "windows")] self.wait_for_service_if_needed().await; self.set_running_mode(match SERVICE_MANAGER.current().await { ServiceStatus::Ready => RunningMode::Service, _ => RunningMode::Sidecar, }); } fn after_core_process(&self) { let app_handle = Handle::app_handle(); tauri_plugin_clash_verge_sysinfo::set_app_core_mode(app_handle, self.get_running_mode().to_string()); } #[cfg(target_os = "windows")] async fn wait_for_service_if_needed(&self) { use crate::{config::Config, constants::timing, core::service}; use backon::{ConstantBuilder, Retryable as _}; let needs_service = Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false); if !needs_service { return; } let max_times = timing::SERVICE_WAIT_MAX.as_millis() / timing::SERVICE_WAIT_INTERVAL.as_millis(); let backoff = ConstantBuilder::default() .with_delay(timing::SERVICE_WAIT_INTERVAL) .with_max_times(max_times as usize); let _ = (|| async { if matches!(SERVICE_MANAGER.current().await, ServiceStatus::Ready) { return Ok(()); } // If the service IPC path is not ready yet, treat it as transient and retry. // Running init/refresh too early can mark service state unavailable and break later config reloads. if !service::is_service_ipc_path_exists() { return Err(anyhow::anyhow!("Service IPC not ready")); } SERVICE_MANAGER.init().await?; let _ = SERVICE_MANAGER.refresh().await; if matches!(SERVICE_MANAGER.current().await, ServiceStatus::Ready) { Ok(()) } else { Err(anyhow::anyhow!("Service not ready")) } }) .retry(backoff) .await; } /// 在窗口内等待服务就绪,再从 sidecar 交接到 service #[cfg(target_os = "windows")] async fn spawn_service_handoff_watcher(&self) { use crate::constants::timing; use crate::process::AsyncHandler; use std::sync::atomic::Ordering; use std::time::Instant; // 仅 TUN 模式需要服务交接 let needs_service = Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false); if !needs_service { return; } // 单实例,避免并发交接 if self.handoff_watcher_running.swap(true, Ordering::AcqRel) { return; } logging!( info, Type::Core, "service not ready at startup; sidecar active, watching for handoff" ); AsyncHandler::spawn(|| async move { let manager = Self::global(); let started = Instant::now(); loop { if started.elapsed() >= timing::SERVICE_HANDOFF_WINDOW { logging!( info, Type::Core, "service handoff window elapsed; staying in sidecar mode" ); break; } tokio::time::sleep(timing::SERVICE_HANDOFF_INTERVAL).await; // 模式已变更时退出 if !matches!(*manager.get_running_mode(), RunningMode::Sidecar) { break; } match manager.try_handoff_sidecar_to_service().await { // 已交接或无需交接 HandoffOutcome::Done => break, // 已回退 sidecar,停止重试 HandoffOutcome::Failed => { logging!(warn, Type::Core, "handoff attempt failed; staying in sidecar mode"); break; } HandoffOutcome::NotReady => {} } } manager.handoff_watcher_running.store(false, Ordering::Release); }); } /// 服务就绪后停止 sidecar,再以 service 重启内核 #[cfg(target_os = "windows")] async fn try_handoff_sidecar_to_service(&self) -> HandoffOutcome { use crate::core::service; // 主动刷新服务状态,避免缓存状态阻止交接 if !service::is_service_ipc_path_exists() { return HandoffOutcome::NotReady; } if SERVICE_MANAGER.init().await.is_err() { return HandoffOutcome::NotReady; } let _ = SERVICE_MANAGER.refresh().await; if !matches!(SERVICE_MANAGER.current().await, ServiceStatus::Ready) { return HandoffOutcome::NotReady; } // 与配置更新/重启互斥 if !self.try_start_config_update() { return HandoffOutcome::NotReady; } defer! { self.finish_config_update(); } // 加锁后复检运行模式和 TUN 状态 if !matches!(*self.get_running_mode(), RunningMode::Sidecar) || !Config::verge().await.latest_arc().enable_tun_mode.unwrap_or(false) { return HandoffOutcome::Done; } logging!( info, Type::Core, "service became ready; handing off from sidecar to service" ); self.stop_core_by_sidecar(); match self.start_core_by_service().await { Ok(()) => { logging!(info, Type::Core, "handoff to service mode succeeded"); HandoffOutcome::Done } Err(e) => { logging!( error, Type::Core, "handoff to service failed: {}; restarting sidecar", e ); if let Err(e2) = self.start_core_by_sidecar().await { logging!( error, Type::Core, "failed to restart sidecar after handoff failure: {}", e2 ); } HandoffOutcome::Failed } } } }