From 2050524cbf38cfab73d77ce795655983d5242c70 Mon Sep 17 00:00:00 2001 From: cooper Date: Mon, 4 May 2026 19:43:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=A0=B9=E6=8D=AE=20code=20review=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=89=E4=B8=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根据 code review 反馈,修复以下问题: ## 1. High: 超时后未清理 ASR session - 在全局超时分支中添加 asr.cancel() 调用 - 避免 websocket/read tasks 继续运行造成资源泄漏 - 影响文件:coordinator.rs (dictation 和 QA 路径) ## 2. Medium: QA 路径缺少超时保护 - 为 QA 路径的 await_final_result() 添加相同的超时保护 - 保持 dictation 和 QA 路径的一致性 - 影响文件:coordinator.rs:2288-2298 ## 3. Medium: Watchdog 误报慢启动设备 - 将 last_callback_time 初始化从 Some(Instant::now()) 改为 None - 只有在第一次回调后才开始计时 - 避免慢启动设备被误判为"静默停止" - 影响文件:recorder.rs:333 Co-Authored-By: Claude Sonnet 4.6 --- openless-all/app/src-tauri/src/coordinator.rs | 21 ++++++++++++++++--- openless-all/app/src-tauri/src/recorder.rs | 3 ++- .../app/src/components/AutoUpdate.tsx | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openless-all/app/src-tauri/src/coordinator.rs b/openless-all/app/src-tauri/src/coordinator.rs index 9618e30d..ba9cca35 100644 --- a/openless-all/app/src-tauri/src/coordinator.rs +++ b/openless-all/app/src-tauri/src/coordinator.rs @@ -1388,6 +1388,8 @@ async fn end_session(inner: &Arc) -> Result<(), String> { "[coord] 全局超时 {} 秒 - 强制恢复", COORDINATOR_GLOBAL_TIMEOUT_SECS ); + // 清理 ASR session,避免资源泄漏 + asr.cancel(); emit_capsule( inner, CapsuleState::Error, @@ -2286,13 +2288,26 @@ async fn end_qa_session(inner: &Arc) -> Result<(), String> { if let Err(e) = asr.send_last_frame().await { log::error!("[coord] QA: send last frame failed: {e}"); } - let raw = match asr.await_final_result().await { - Ok(r) => r, - Err(e) => { + // 添加全局超时保护:防止 await_final_result() 永远挂起 + let timeout_duration = std::time::Duration::from_secs(COORDINATOR_GLOBAL_TIMEOUT_SECS); + let raw = match tokio::time::timeout(timeout_duration, asr.await_final_result()).await { + Ok(Ok(r)) => r, + Ok(Err(e)) => { log::error!("[coord] QA: await final failed: {e}"); finish_qa_with_error(inner, format!("识别失败: {e}")); return Err(e.to_string()); } + Err(_) => { + // 全局超时:最后的防线 + log::error!( + "[coord] QA: 全局超时 {} 秒 - 强制恢复", + COORDINATOR_GLOBAL_TIMEOUT_SECS + ); + // 清理 ASR session,避免资源泄漏 + asr.cancel(); + finish_qa_with_error(inner, "识别超时".to_string()); + return Err("global timeout".to_string()); + } }; // cancel race:用户在 transcribe 中按 Esc / dismiss → 静默退出。 diff --git a/openless-all/app/src-tauri/src/recorder.rs b/openless-all/app/src-tauri/src/recorder.rs index e52fb3f3..78b22385 100644 --- a/openless-all/app/src-tauri/src/recorder.rs +++ b/openless-all/app/src-tauri/src/recorder.rs @@ -330,7 +330,8 @@ impl StreamState { callback_count: AtomicUsize::new(0), peak_input_rms_milli: AtomicUsize::new(0), peak_output_rms_milli: AtomicUsize::new(0), - last_callback_time: Mutex::new(Some(std::time::Instant::now())), + // 初始化为 None,只有在第一次回调后才开始计时,避免误报慢启动设备 + last_callback_time: Mutex::new(None), } } } diff --git a/openless-all/app/src/components/AutoUpdate.tsx b/openless-all/app/src/components/AutoUpdate.tsx index 40f96d85..d5c3ec58 100644 --- a/openless-all/app/src/components/AutoUpdate.tsx +++ b/openless-all/app/src/components/AutoUpdate.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { isTauri, restartApp } from '../lib/ipc'; import { Btn } from '../pages/_atoms'; -const UPDATE_CHECK_TIMEOUT_MS = 15_000; +const UPDATE_CHECK_TIMEOUT_MS = 8_000; // 缩短超时,让镜像站慢的情况能更快 fallback export type UpdateStatus = | 'idle'