mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-29 01:52:08 +08:00
* fix(windows): ensure sidecar process dies with the main app * chore: fix clippy lints * chore: fix TS2307 error on commit. chore: Undo the changes about package.json and pnpm-lock.yaml * Fix formatting errors caused by merging. 刚刚的合并让原本的函数少了个结尾括号 * fix(windows): close process handle after assigning sidecar to job object * fix(windows): sidecar Job Object 错误改为向上传播并补充测试 将 job 创建逻辑抽为返回 Result<OwnedHandle> 的独立函数,任一 Win32 调用失败时 终止已 spawn 的子进程并返回 Err,不再静默降级。句柄由裸 isize 改为 OwnedHandle, 借助 RAII 自动关闭。新增 Windows 单元测试,覆盖 KILL_ON_JOB_CLOSE 生命周期 及无效 PID 的失败路径。 --------- Co-authored-by: wonfen <96291150+wonfen@users.noreply.github.com>
148 lines
4.0 KiB
Rust
148 lines
4.0 KiB
Rust
mod config;
|
|
mod lifecycle;
|
|
mod state;
|
|
|
|
use anyhow::Result;
|
|
use arc_swap::{ArcSwap, ArcSwapOption};
|
|
use clash_verge_logger::AsyncLogger;
|
|
use once_cell::sync::Lazy;
|
|
use std::{
|
|
fmt,
|
|
sync::{
|
|
Arc,
|
|
atomic::{AtomicBool, Ordering},
|
|
},
|
|
time::Instant,
|
|
};
|
|
use tauri_plugin_shell::process::CommandChild;
|
|
|
|
use crate::singleton;
|
|
#[cfg(target_os = "windows")]
|
|
use std::os::windows::io::OwnedHandle;
|
|
|
|
pub(crate) static CLASH_LOGGER: Lazy<Arc<AsyncLogger>> = Lazy::new(|| Arc::new(AsyncLogger::new()));
|
|
|
|
#[derive(Debug, serde::Serialize, PartialEq, Eq)]
|
|
pub enum RunningMode {
|
|
Service,
|
|
Sidecar,
|
|
NotRunning,
|
|
}
|
|
|
|
impl fmt::Display for RunningMode {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Service => write!(f, "Service"),
|
|
Self::Sidecar => write!(f, "Sidecar"),
|
|
Self::NotRunning => write!(f, "NotRunning"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CoreManager {
|
|
state: ArcSwap<State>,
|
|
last_update: ArcSwapOption<Instant>,
|
|
#[cfg(target_os = "windows")]
|
|
job_handle: ArcSwapOption<OwnedHandle>,
|
|
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,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct State {
|
|
running_mode: ArcSwap<RunningMode>,
|
|
child_sidecar: ArcSwapOption<CommandChild>,
|
|
}
|
|
|
|
impl Default for State {
|
|
fn default() -> Self {
|
|
Self {
|
|
running_mode: ArcSwap::new(Arc::new(RunningMode::NotRunning)),
|
|
child_sidecar: ArcSwapOption::new(None),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for CoreManager {
|
|
fn default() -> Self {
|
|
Self {
|
|
state: ArcSwap::new(Arc::new(State::default())),
|
|
last_update: ArcSwapOption::new(None),
|
|
#[cfg(target_os = "windows")]
|
|
job_handle: 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),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CoreManager {
|
|
fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn get_running_mode(&self) -> Arc<RunningMode> {
|
|
Arc::clone(&self.state.load().running_mode.load())
|
|
}
|
|
|
|
pub fn take_child_sidecar(&self) -> Option<CommandChild> {
|
|
self.state
|
|
.load()
|
|
.child_sidecar
|
|
.swap(None)
|
|
.and_then(|arc| Arc::try_unwrap(arc).ok())
|
|
}
|
|
|
|
pub fn get_last_update(&self) -> Option<Arc<Instant>> {
|
|
self.last_update.load_full()
|
|
}
|
|
|
|
pub fn set_running_mode(&self, mode: RunningMode) {
|
|
let state = self.state.load();
|
|
state.running_mode.store(Arc::new(mode));
|
|
}
|
|
|
|
pub fn set_running_child_sidecar(&self, child: CommandChild) {
|
|
let state = self.state.load();
|
|
state.child_sidecar.store(Some(Arc::new(child)));
|
|
}
|
|
|
|
pub fn set_last_update(&self, time: Instant) {
|
|
self.last_update.store(Some(Arc::new(time)));
|
|
}
|
|
|
|
/// Replaces the Windows Job Object handle owned by the core manager
|
|
///
|
|
/// Passing `None` drops the current handle, which closes the Job Object
|
|
/// and terminates its assigned processes due to `KILL_ON_JOB_CLOSE`.
|
|
///
|
|
/// This method is currently only used on Windows.
|
|
#[cfg(target_os = "windows")]
|
|
fn set_job_handle(&self, handle: Option<OwnedHandle>) {
|
|
self.job_handle.store(handle.map(Arc::new));
|
|
}
|
|
|
|
fn try_start_config_update(&self) -> bool {
|
|
!self.config_update_in_progress.swap(true, Ordering::AcqRel)
|
|
}
|
|
|
|
fn finish_config_update(&self) {
|
|
self.config_update_in_progress.store(false, Ordering::Release);
|
|
}
|
|
|
|
pub async fn init(&self) -> Result<()> {
|
|
self.start_core().await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
singleton!(CoreManager, CORE_MANAGER);
|