Files
HomeAgent/internal
JianFeeeee 6190a5a587 fix(plugins): 修复隔离全量测试暴露的两处数据竞争
-go test ./... -race 全仓复验暴露的 7 处 race、5 个失败测试,全部定位。
两处独立缺陷,互不相关。

## 缺陷一(remotedevice,8 处 race):连接写无串行化

WARNING: DATA RACE
  Read at 0x... by goroutine 28:
    bufio.(*Writer).Available() / writeFrameHeader / PushData
  Previous write at 0x... by goroutine 27:
    bufio.(*Writer).Flush() / writeFrame / handleWS

同一连接的 bufio.Writer 被两条并发路径写:
  - handleWS 主循环:读到设备帧后回写 hello_ack/bind_ack/pong
  - PushJSON/PushData:agent→设备的下发路径,可来自任意 goroutine

bufio.Writer 不是线程安全的。不加锁就在 WriteByte/Flush 上撞——这
不是理论风险,TestWSPushDataAudio 的异步 PushData 与 handleWS 的
hello_ack 回写并发时被 -race 稳定抓到。

修法:wconn 增加 wmu(sync.Mutex),PushJSON/PushData 拿锁后整条
下发(start + N 个 chunk + end)持锁——设备侧按协议串行聚合,中途
被插帧会破坏协议顺序。handleWS 的 hello_ack/bind_ack/pong 也改走
同一把锁(wsWriteLocked 封装,避免调用方绕过)。设备已离线时不回写。

关键点:不能只锁 Push* 不锁 handleWS——那只是把竞争挪了个位置。

## 缺陷二(agentcli,1 处 race):共享读缓冲被并发读写

WARNING: DATA RACE
  Write at 0x... by goroutine 26:
    os.File.Read / (*linuxPty).Read / reader
  Previous read at 0x... by goroutine 25:
    runtime.slicecopy / readLoop

readLoop 创建 buf := make([]byte, ReadBufSize) 传给 reader goroutine
(t.session.Read(buf) 持续覆写),自己又在读到结果后
copy(data, buf[:r.n])——同一缓冲被读写并发。Go 的 pty 读走 OS 层
fd,专门在 reader 写下一段时读,跑 -race 稳定复现。

修法:readResult 携带 data field,reader 每次读完后把数据复制进自己
分配的切片再随结果传递,读取与拷贝之间不再共享任何可变状态。原 buf
保留(仍由 reader 独享用于 OS 读),readLoop 不再从其中 copy。

## 验证

  - 两个插件包 -race -count=2 全过
  - 全仓 go build / go vet / go test 通过
  - 全仓 go test ./... -race:32 包全过,0 DATA RACE,0 FAIL
  - SDK 冻结 diff = 0

其中 remotedevice 的 TestScreenseeEndToEnd / TestComputeruseEndToEnd
/ TestClipboardEndToEnd 原本因 race 挂,修后恢复全绿。
2026-09-05 05:53:17 +08:00
..