fix(windows): 快捷键二次按键失效 - 串行化 Pressed/Released edge#489
Conversation
根因: hotkey_bridge_loop 和 combo_hotkey_bridge_loop 把每条 Pressed/Released 边沿独立 async_runtime::spawn 成两个 task,被 tokio work-stealing 调度器 并行执行。Windows WH_KEYBOARD_LL 边沿间隔微秒级,Released task 经常先 swap(true→false),导致下一次 Pressed 看到 was_held=true → toggle 被静默吞掉。 macOS 上 CGEventTap 边沿间隔几十毫秒,race window 极窄,肉眼不可见, 所以仅 Windows 必现。ESC 走 HotkeyEvent::Cancelled 不经过 latch, 是用户唯一能逃出 stuck-latch 的路径。 修复: 用 tauri::async_runtime::block_on 顺序 await 两个 handler, bridge 线程的 mpsc::Receiver::recv() FIFO 保证 swap 顺序正确。 bridge 在独立 OS 线程而非 tokio worker,block_on 安全。 代价: Hold 模式短按 stop 多 ≤500ms 延迟,被 request_stop_during_starting 覆盖,可接受。qa / translation bridge 不走 handle_*_edge 不受影响。 附带清理 coordinator.rs unreachable_code warning。 Refs #468
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
|
@claude 请审核此 PR。如有问题请逐条列出(CRITICAL / HIGH / MEDIUM);如全部通过请明确说 LGTM。 |
|
Claude finished @appergb's task in 2m 2s —— View job PR #489 代码审核
LGTM ✓三处改动全部通过审核,无 CRITICAL / HIGH / MEDIUM 级别问题。 审核详情1. 竞态根因分析(正确)
// handle_pressed_edge: swap(true) → was_held=false 时才处理
// handle_released_edge: swap(false) → was_held=true 时才处理原来两次独立 2.
|
| loop | 原因 |
|---|---|
qa_hotkey_bridge_loop |
只有 Pressed 单事件,无 latch,spawn 无害 |
translation_hotkey_bridge_loop |
只调同步 mark_translation_modifier_seen,不走 handle_*_edge |
action_hotkey_bridge_loop |
只调同步函数,不走 handle_*_edge |
hotkey_bridge_loop 里的 QaShortcutPressed 分支继续使用 spawn 也正确,handle_qa_hotkey_pressed 不读写 hotkey_trigger_held,无竞态。
4. Hold 模式短按延迟(可接受)
block_on(handle_pressed_edge) 会等 begin_session 握手完成(≤500ms),期间 Released 在 channel 里排队。begin_session 完成后 session 已进 Listening,bridge 立即 recv Released → end_session,行为语义正确,仅有轻微停止延迟。request_stop_during_starting 的兜底路径(Starting 阶段第二次 Pressed/Released)仍然有效,是额外安全网。
5. ensure_asr_credentials unreachable_code 清理(正确)
// 修前:非 Windows 执行 return Err(...),接着的 return Ok(()) 不可达 → warning
// 修后:两个分支各自 cfg-gate,编译器每个平台只看到一条 return,warning 消除
#[cfg(not(target_os = "windows"))]
{ return Err("Foundry Local Whisper 当前仅支持 Windows".to_string()); }
#[cfg(target_os = "windows")]
{ return Ok(()); }逻辑完全等价,清理干净。
附注(非阻塞)
- PR 描述里对 [windows] 启动时静默运行功能在 Win11 失效 #468 的说明清晰,reviewer bot 的"不合规"提示是误判(它把此 PR 关联到 [windows] 启动时静默运行功能在 Win11 失效 #468,但 PR 正文已明确说明本 PR 修的是另一个 bug)。
- 代码注释较长,但记录了非显而易见的并发不变量,对后续维护有价值,符合项目现有风格。
…v 安装
v1.3.4-4 / -5 的 Linux 构建在 "Build fcitx5 plugin" 步骤连挂两次,
错误一致:"E: Unable to locate package fcitx5-dev"。同一 job 前面的
"Install Linux bundle deps" 步骤明明已经 apt-get update + 装好了一堆
universe 包,到 fcitx5 这步突然找不到 —— 表象与 GitHub Actions
ubuntu-22.04 runner 镜像弃用过渡期里 apt 元数据漂移的现象一致。
release-tauri.yml "Build fcitx5 plugin" 步骤增加三层防御:
1) sudo add-apt-repository -y universe(已启用则 no-op)
2) 本步骤内重新 sudo apt-get update,独立于上游 step 缓存
3) 安装失败时打印 apt-cache policy / search / os-release / sources.list.d
便于以后排查
同 commit 同时 bump 五处版本到 1.3.4-6 重发,让 PR #487/#488/#489
三个 Windows P0 修复带上 Linux 包一起进 Beta 通道。
问题
Windows Beta 1.3.4-4 上快捷键二次按键失效:
macOS 正常。
根因
hotkey_bridge_loop和combo_hotkey_bridge_loop把每条HotkeyEvent::Pressed/Released边沿独立async_runtime::spawn成两个 task:两个 task 被 tokio work-stealing 调度器并行执行:
swap(false→true),race window 极窄WH_KEYBOARD_LL是 kernel 注入回调,KEYDOWN/KEYUP 间隔微秒级 → Released task 经常先swap(true→false)→hotkey_trigger_held卡在 true → 下一次 Pressed 看到was_held=true→ toggle 静默吞掉ESC 走
HotkeyEvent::Cancelled直接调cancel_session,不经过 latch,所以唯一逃脱路径。修复
把 bridge loop 内的两次独立 spawn 改为
tauri::async_runtime::block_on顺序 await。bridge 线程是独立 OS 线程(std::thread,非 tokio worker),block_on安全。mpsc::Receiver::recv()FIFO 保证 Pressed/Released 严格按到达顺序处理。combo_hotkey_bridge_loop同模式替换。qa_hotkey_bridge_loop只有 Pressed 单事件、translation_hotkey_bridge_loop不走handle_*_edge,本地审查已确认都无需修。附带清理
coordinator.rs:2094的unreachable_codewarning。取舍
Hold 模式下 bridge 线程会被
block_on短暂占用,相邻 Released 事件最多延迟一个handle_pressed_edge完成时间(≤500ms,主要是 ASR OpenSession 握手)。request_stop_during_starting已覆盖 Starting 阶段松手语义,本地审查已验证路径正确。三平台同源,无 cfg 分叉。本地审查结论
cargo check --all-targets:零 error,无新增 warningcargo fmt问题在coordinator.rs:1662,与本 PR 无关Test plan
🤖 Generated with Claude Code