Skip to content

fix(ostool-server): isolate serial and websocket I/O with bounded queues - #185

Merged
ZR233 merged 1 commit into
mainfrom
codex/serial-transport-isolation
Sep 11, 2026
Merged

fix(ostool-server): isolate serial and websocket I/O with bounded queues#185
ZR233 merged 1 commit into
mainfrom
codex/serial-transport-isolation

Conversation

@ZR233

@ZR233 ZR233 commented Sep 11, 2026

Copy link
Copy Markdown
Member

问题与证据

排查 rcore-os/tgoskits#2357 的 OrangePi native-hardware-smoke 超时时,发现板卡 shell 和 NPU 命令仍能正常完成,但串口日志出现 NUL、截断和成功标记缺失。同一问题也出现在 TGOSKits dev 基线。

服务主机上实测:TIOCINQ 接收队列达到 4095 字节,FTDI TIOCGICOUNT.overrun 从 288 增至 298;期间 frame/parity 没有新增。旧服务器在同一个循环中读取 64 字节后等待 WebSocket 发送、会话心跳,并在串口写入后调用 flush()tokio-serialpoll_flush 底层同步调用 tcdrain,还持有 tokio::io::split 的共享锁。上述等待都会使接收停止推进。

这证明了服务端接收链路存在丢失及阻塞边界;尚不能仅凭计数把每次现场暂停归因于某一个具体 await。

改动及原因

  • 将串口 RX/TX、WebSocket RX/TX 拆成四个独立推进的异步循环,用有界通道隔离两种传输。采用借用式结构化并发,由会话统一取消全部循环,再合并、清理和释放串口,避免遗留后台任务或串口引用。
  • 每个数据通道最多 64 个 4 KiB 数据块;队列满时明确失败,不静默丢字节。单条 binary 或解码后的 tx 命令限制为 256 KiB,整条检查后再入队,避免容量不足时只提交半条命令。
  • 运行期写入不调用阻塞式 tcdrain。关闭时以非阻塞查询观察发送队列,最多等 1 秒,超时也清除残留命令。WebSocket 关闭通知同样不能无限阻止租约回收。
  • 串口正常 EOF 先发送完已排队数据;会话取消立即停止转发和未完成命令。首次上电完成前仍不向串口提交客户端命令。
  • 更新中英文说明,记录缓冲上限、失败行为和关闭语义。未调整硬件测试超时、波特率、流控或成功匹配规则。

验证

  • 确定性红绿:旧 write_serial_payload 在禁止进入 UART drain 的同一回归中失败;移除运行期 drain 后通过。

  • 定向覆盖:阻塞 WebSocket 时继续接收串口、阻塞串口 TX 时继续输出及处理关闭、EOF 保留包括 NUL 在内的原始字节、超出缓冲容量的持续输出保持顺序、溢出显式报错、超大命令零字节提交、取消归还串口所有权、排空失败仍清理缓冲。

  • cargo test -p ostool-server:143 项单元测试、3 项真实 PTY/WebSocket 生命周期测试通过。

  • cargo clippy -p ostool-server --target x86_64-unknown-linux-gnu --all-featurescargo fmt --all -- --check、release 构建通过。

  • 等现有板卡会话全部释放后,已替换测试服务器二进制;保留原二进制备份,未改配置和 systemd unit。

  • 同一 OrangePi-5-Plus-3、原始内核及原始 native-hardware-smoke 连续三轮通过(68.37、95.43、95.29 秒):首轮接收队列采样峰值 992 字节,后两轮合并峰值 496 字节;overrun/frame/parity 增量均为 0,三份日志 NUL 数均为 0。一次通过不足以证明所有历史停滞;这里同时保留原失败的计数证据及修复后的连续观测。

  • 正式 cargo xtask starry test board --board orangepi-5-plus --test-case native-network-smoke 同板卡通过(51.14 秒),队列峰值 496 字节、overrun/frame/parity 增量及日志 NUL 均为 0。首次直接运行板卡入口因缺少预上传会话文件在启动前退出,未计为板卡失败或通过;随后使用测试入口准备依赖并完整执行。

精确提交 4a873173f922947919d0de39e73988cdf5b272dapush CIPR CI 均 completed/success,覆盖完整工作区 clippy、构建、测试、Web UI 单元与集成测试、publish dry run。

@ZR233
ZR233 merged commit e084661 into main Sep 11, 2026
2 checks passed
@ZR233
ZR233 deleted the codex/serial-transport-isolation branch September 11, 2026 07:31
@github-actions github-actions Bot mentioned this pull request Sep 11, 2026

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本 PR 将 ostool-server 的串口 RX/TX 与 WebSocket RX/TX 拆成独立异步 worker,加入有界数据队列、命令大小限制、关闭清理逻辑,并同步中英文文档,目标是避免串口和网络 I/O 相互阻塞。改动集中在串口 transport/session 生命周期与说明文档;串口发现、QEMU/physical serial 分支及既有 WebSocket 控制协议仍沿用现有契约。

固定 head 4a873173f922947919d0de39e73988cdf5b272da 的两个 CI check runs 均为 completed/success,未发现由本 PR 引起的 CI 失败。本地按计划尝试了 cargo fmt --checkcargo clippy --manifest-path ostool-server/Cargo.toml --all-features -- -D warningscargo test --manifest-path ostool-server/Cargo.toml --all-features,但审查环境没有 cargo,三项均因 /bin/sh: cargo: not found 未能执行;这属于环境限制而非代码结论。此前没有 GitHub review 或 PR comment 需要处理。相似的 #77/#78 涉及会话释放生命周期,但没有发现与本 PR 重复的串口 transport 实现。

仍有一个阻塞问题:ostool-server/src/serial/transport.rs:54 在串口 EOF 后永久等待,而 WebSocket 写 worker 若卡在发送 queued frame,transport 就不会返回,导致外层无法及时合并串口、清理缓冲并释放 session lease。请见 inline comment;建议补充 EOF 与 WebSocket writer 的协调及有界排空回归测试。

Powered by gpt-5.6-luna

if size == 0 {
// Let the WebSocket writer drain queued final bytes before ending.
drop(output_tx);
return pending::<anyhow::Result<()>>().await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

串口 EOF 后这里会永久 pending,而 run 只有在四个 worker 之一结束时才返回。如果此时 write_websocket 正卡在 ws_sender.send(...)(客户端停止读取但连接没有关闭),它也不会结束;外层只好等独立的会话过期路径介入,绕过文档承诺的一秒关闭上限;如果客户端仍发送 heartbeat,串口租约甚至可以一直保持。请让 EOF 进入协调器并给最后的 WebSocket 排空设置有限时限,超时也要退出 transport 触发外层清理。

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