Leadaxe:sing-box-lx v1.14.4-lx.28 修复 masque 停止卡死与 cache.db 释放
在 sing-box-lx #61 的修复说明中,Leadaxe 表示 v1.14.4-lx.28 同时处理两类停止卡死:为 masque 经 detour 的 quic 连接补真实读超时并在关闭时取消连接、释放会话;cache.db 的 CacheFile.Release 改为幂等,补写 FakeIP metadata,并用按路径注册表在新 Box 打开前释放旧实例。原文称 release build running,设备尚未复核,LxBox 删除 cache.db 后重试的 fallback 保留。
作者原文@LeadaxeFixed in
v1.14.4-lx.28(release build running), in two layers.The actual hang turned out to be in masque, not in the WG endpoints. Two LxBox support packs from today carry the goroutine dump of the stop watchdog, and both show the same single blocked stack:
Box.Close→masque.(*Outbound).Closewaiting onrunMu, whichensureSessionholds across the whole dial. The dial sat inquic.DialEarlythrough a detour (the node isvhttp: h3withdetourto a selector, member vless + WebSocket). quic-go never closes aPacketConnit did not create: when its single-use transport shuts down it callsSetReadDeadline(now)and waits for its listener to return fromReadPacket. Through the detour that conn is a stream (XUDP over WebSocket) whoseSetReadDeadlineis a no-op, so the listener never woke,Transport.Closenever returned,DialEarlynever returned, and the mutex was never released. Trigger in the field: a ping of that node was in flight when Stop was pressed.
- 1858657b0 (SPEC 146): the detour conn gets a real read deadline (
deadline.NewConn, byNeedAdditionalReadDeadline) before quic-go sees it;Outbound.Closeno longer takes the lock held across the dial — it cancels the connect in flight and tears the session down; a session that comes up afterCloseis disposed of. Three red/green tests.This issue's layer, cache.db, is in as the second line of defence, with one change against the plan above:
- e8ea92cd8 (SPEC 147): idempotent
CacheFile.Release()(flushes pending FakeIP metadata, closes bbolt, from then on writes returnos.ErrClosedandresetDBnever reopens the file), a per-path registry so a new Box releases the previous instance beforebbolt.Openinstead of waiting 10 s, and the normal scope cleanup goes throughReleasetoo.FakeIPMetadata()now seeds the pending metadata it read-and-deleted, so a takeover does not reset the FakeIP pool on the next start.- Step 2 of the plan (
Release()as the first step ofBox.Close) is not implemented: the registry already covers the in-process case completely, and an early release would have broken the fakeip store's synchronous metadata write on every normal stop, on every platform.Not checked on a device yet. The LxBox fallback (§703, delete
cache.dband retry once) stays in place and should stop firing with lx.28.