fix(core): 老版本 Java 按版本距离选 + 启动看门狗有界等待 + 结束归属(M214 D4/D6/D10)
D4:`pick` 的排序键从「来源序」改成「版本距离 → 来源序」,全不满足时选最接近要求的那个 (托管 25 与 JAVA_HOME/扫盘 17 之间选 17,而不是按 rank 选托管)。新增 `select::decide`: 不符且**没有人显式指定 Java** 时 `ull launch` 直接报 `java_mismatch` 不 spawn(`--java` / 实例 `javaPath` 是豁免,只告警)——1.12.2 配 Java 17/25 必然起不来,警告没有信息量。 `JavaMismatch` + `check` 拆到 `mismatch.rs` 并加 `advice()`(mapping.rs 408→282 行)。 D6:`LaunchSession::finish` 的等进程改成**有界**。判据在 `startup_watch.rs`(纯逻辑): ①窗口从未出现且**连续 N 秒没有任何日志**(还在打日志就说明还在加载,慢加载不被判死); ②日志出现关闭标记后 N 秒仍未退出(实机「关窗后 java.exe 仍存活」的形状)。 到点只**停止等待、绝不杀进程**:写 `session_end(still_running)` + 索引 + `launcher.log`, 返回 `LaunchError::Watchdog`,CLI 翻成 `launch_timeout`。两个时长可配 (默认 300s/60s;`ULL_LAUNCH_STARTUP_TIMEOUT_SECS` / `ULL_LAUNCH_SHUTDOWN_GRACE_SECS`, 生效值每次启动写进 launcher.log 与人读输出)。 D10:把「被终止」与「应用错误」分开。①我们自己杀的:`tree_kill` **送达即登记** `kill_registry`(pid 登记簿,取走语义、上限 64),覆盖 GUI 从别的线程 `kill_tree` 的路径, `finish` 据此判 `killed`;②外部强杀:Windows 上「退出码 1 + 零错误输出 + 无崩溃/JVM 关键字」 判 `killed` 并标 `terminated_externally`(任务管理器与 `taskkill /F` 都是 `TerminateProcess(h, 1)`,应用真崩给的是 NTSTATUS 异常码)。 顺带:`SessionHandle` 拆 `session_handle.rs`(launch_session.rs 422→343 行)、 `NativesReport::to_json` 落 core(cli/launch.rs 腾出空间,仍 399 行)。 用例:select 距离/取舍、startup_watch 状态机、watchdog_cases(4 条真进程)、 exit_cases(外部终止与「我们杀的」区分)、pipeline 跨线程 kill。
This commit is contained in:
1 parent
5959a27add
commit
ff78fe2cae
23 files changed
+1708
-250
No files matched your search
@@ -4,32 +4,34 @@
|
||||
//! 也避免单文件超过 400 行的硬约束。
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::launch::pump::{LineSink, LogLine};
|
||||
use crate::launch::session::SessionWriter;
|
||||
use crate::launch::window_detect::WindowDetector;
|
||||
use crate::launch::window_detect::{shutdown_marker, WindowDetector};
|
||||
|
||||
/// 会话日志 sink。
|
||||
#[derive(Debug)]
|
||||
pub struct SessionSink {
|
||||
writer: Mutex<SessionWriter>,
|
||||
detector: Mutex<WindowDetector>,
|
||||
shutdown_at: Mutex<Option<Instant>>,
|
||||
last_line_at: Mutex<Option<Instant>>,
|
||||
}
|
||||
|
||||
impl SessionSink {
|
||||
/// 以进程启动时刻为窗口检测起点。
|
||||
pub fn new(writer: SessionWriter) -> Self {
|
||||
Self {
|
||||
writer: Mutex::new(writer),
|
||||
detector: Mutex::new(WindowDetector::new()),
|
||||
}
|
||||
Self::with_start(writer, Instant::now())
|
||||
}
|
||||
|
||||
/// 指定窗口检测计时起点(进程 spawn 时刻)。
|
||||
pub fn with_start(writer: SessionWriter, started_at: std::time::Instant) -> Self {
|
||||
pub fn with_start(writer: SessionWriter, started_at: Instant) -> Self {
|
||||
Self {
|
||||
writer: Mutex::new(writer),
|
||||
detector: Mutex::new(WindowDetector::with_start(started_at)),
|
||||
shutdown_at: Mutex::new(None),
|
||||
last_line_at: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +47,16 @@ impl SessionSink {
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 第一次见到关闭标记(游戏自己说要退出)的时刻(D6)。
|
||||
pub fn shutdown_seen_at(&self) -> Option<Instant> {
|
||||
self.shutdown_at.lock().ok().and_then(|guard| *guard)
|
||||
}
|
||||
|
||||
/// 最后一行的时刻(D6:还在打日志就说明还在加载,不算卡死)。
|
||||
pub fn last_line_at(&self) -> Option<Instant> {
|
||||
self.last_line_at.lock().ok().and_then(|guard| *guard)
|
||||
}
|
||||
}
|
||||
|
||||
impl LineSink for SessionSink {
|
||||
@@ -53,9 +65,22 @@ impl LineSink for SessionSink {
|
||||
Ok(mut detector) => detector.observe(&line.text),
|
||||
Err(_) => None,
|
||||
};
|
||||
if let Ok(mut guard) = self.last_line_at.lock() {
|
||||
*guard = Some(Instant::now());
|
||||
}
|
||||
if shutdown_marker(&line.text).is_some() {
|
||||
if let Ok(mut guard) = self.shutdown_at.lock() {
|
||||
guard.get_or_insert_with(Instant::now);
|
||||
}
|
||||
}
|
||||
let Ok(mut writer) = self.writer.lock() else {
|
||||
return;
|
||||
};
|
||||
// 会话已经收尾(看门狗放弃等待后进程还在打日志):不再往事件流里追加,
|
||||
// 否则 `session_end` 之后会出现孤儿行。
|
||||
if writer.is_finished() {
|
||||
return;
|
||||
}
|
||||
if let Some(detection) = detection {
|
||||
let _ = writer.window_detected(&detection);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user