Skip to content

perf(shell): native restart path + UI-thread hygiene (black-frame freezes) - #2

Merged
masknull merged 2 commits into
mainfrom
perf/native-restart-ui-hygiene
Sep 25, 2026
Merged

masknull merged 2 commits into
mainfrom
perf/native-restart-ui-hygiene

Conversation

@masknull

Copy link
Copy Markdown
Owner

背景(证据)

  • 重启慢: 壳日志 dsh.log 实测"重启清理→启动尝试"间隔 6~20s; 关键路径上 netstat -ano -p tcp spawn 本机实测 311ms 冷/44ms 热, wait_for_backend_stop 每 300ms 轮询一次(最坏 8s), HTTP 就绪探测每 400ms 一次且单次最坏 3s 超时, 另有固定 1s settle
  • 黑屏/卡死: 本机事件日志近 7 天无 dsh-local-shell 挂起/崩溃记录、EBWebView Crashpad 无报告、无 GPU TDR; 用户反馈"内容可点、标题栏透明、任务栏块黑、系统不卡"是窗口表面呈现失败特征。三个同步命令(仍在 UI 线程同步跑)恰好在重启/退出路径上: dsh_custom_path(最坏 ~19s)、app_full_restart、log_tail(整读日志且每 2s 轮询)

改动

  • 新增 src-tauri/src/wproc.rs(裸 Win32 FFI, 零新依赖, 沿用 memtok.rs 风格并显式 #[link]): GetExtendedTcpTable 取代 netstat; Toolhelp32 快照 + TerminateProcess 树杀取代 taskkill /T; OpenProcess(SYNCHRONIZE) + WaitForSingleObject 精确等待被杀 PID; 非 Windows 保留旧 spawn 实现作 cfg 回落
  • src-tauri/src/dsh.rs: port_listener_pid / kill_tree / kill_port_listeners / wait_for_backend_stop 重接 wproc; teardown 与清端口把 PID 记入 PENDING_EXIT_PIDS 供精确等待; 固定 settle 1s→250ms, 兜底轮询 300ms→50ms; log_tail 只读文件尾 256KB
  • src-tauri/src/lib.rs: dsh_custom_path/app_full_restart/dsh_exit 移出 UI 线程(spawn_blocking); log_tail 改 async; 新增 [hangwatch] 独立线程每 2s 探主窗口消息泵(SendMessageTimeout WM_NULL + GetLastError()==ERROR_TIMEOUT 判定), 停转即落日志; 窗口获得焦点/从托盘恢复时强制重组窗口表面(RedrawWindow + SWP_FRAMECHANGED + 同值 bounds 回写, 1s 冷却)
  • CHANGELOG.md: v2.0.7 双语条目

验证

  • 本地无 Rust 工具链, 编译验证走本 PR 的 CI 构建(产物 exe 见 artifacts)
  • 已静态核对: 括号配平(状态机扫描, dsh/wproc 通过, lib.rs 差异项为检查器在原文件同样误报)、符号引用、FFI 签名/dw_size 预置/非对齐读

deepseek added 2 commits September 25, 2026 20:34
…ezes)

重启/黑屏 两个问题的 P0 修复(详细证据见 CHANGELOG v2.0.7):

- src-tauri/src/wproc.rs(新增, 裸 Win32 FFI, 零新依赖):
  GetExtendedTcpTable 取代 netstat spawn(实测单次 311ms 冷/44ms 热),
  Toolhelp32 快照 + TerminateProcess 树杀取代 taskkill /T /F,
  OpenProcess(SYNCHRONIZE)+WaitForSingleObject 精确等待被杀 PID 退出,
  取代 HTTP 探测(3s 超时)与 netstat 轮询; 固定 settle 1s→250ms,
  轮询 300ms→50ms。非 Windows 保留旧 spawn 实现(cfg 回落)。
- src-tauri/src/dsh.rs: port_listener_pid / kill_tree /
  kill_port_listeners / wait_for_backend_stop 全部重接 wproc;
  teardown 与清端口把 PID 记入 PENDING_EXIT_PIDS 供精确等待;
  log_tail 只读文件尾 256KB。
- src-tauri/src/lib.rs: dsh_custom_path / app_full_restart / dsh_exit
  移出 UI 线程(同步命令体跑在主线程, 阻塞即窗口黑屏/标题栏透明/
  任务栏预览黑, 与用户反馈吻合); log_tail 改 async + spawn_blocking。
- src-tauri/src/lib.rs: [hangwatch] 独立线程每 2s 探主窗口消息泵
  (SendMessageTimeout WM_NULL + GetLastError==ERROR_TIMEOUT 判定),
  停转即落日志(含 listeners/可见性), 供偶发问题定位; 窗口获得焦点
  与从托盘恢复时强制重组窗口表面(RedrawWindow + SWP_FRAMECHANGED),
  1s 冷却。
…ed import (CI 实证)

第一轮 PR 构建(CI)暴露两个问题, 本 commit 修正:
1. error E0599: `WebviewWindow` 没有 `set_outer_size`(仅 `Window` 有;
   WebviewWindow 未 Deref 到 Window)。tauri 2.11.5 源码核实
   `WebviewWindow::set_size` 委托 `Window::set_size` → tao set_size,
   其 Windows 语义为外框尺寸, 传 inner_size 会让窗口每次缩一截标题栏,
   风险大于收益 —— 黑屏恢复改为纯原生帧重组(RedrawWindow +
   SetWindowPos(SWP_FRAMECHANGED)), 本就是主机制且零语义歧义。
2. 未用的 import(`Emitter`@lib.rs, `uuid::Uuid`@dsh.rs, `crate::dsh`@update.rs,
   均为历史遗留/全限定路径替代), 清理让构建零警告。
@masknull
masknull merged commit f441df2 into main Sep 25, 2026
1 check passed
@masknull
masknull deleted the perf/native-restart-ui-hygiene branch September 25, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant