fix: 根据 code review 修复三个问题

根据 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 <noreply@anthropic.com>
This commit is contained in:
cooperandClaude Sonnet 4.6 committed 2026-05-04 19:43:34 +08:00
1 parent e0d459bbd0
commit 2050524cbf
3 files changed
+21 -5

No files matched your search

+18 -3
View File
@@ -1388,6 +1388,8 @@ async fn end_session(inner: &Arc<Inner>) -> 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<Inner>) -> 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 → 静默退出。
+2 -1
View File
@@ -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),
}
}
}
@@ -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'