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'