mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
The route table answers a destination with a name, and the socket server turns that name into a UDP address. A p2p hub needs the other answer: the name is a peer key, and the thing it resolves to is a stream, not an address. That is the whole of the second delivery — resolveUDPAddr is unreachable here by construction, and putting it back on this path would be a design error rather than a bug. The shape is forced by the device, not chosen. tunDevice shares one read buffer and one write buffer across every call and takes no lock: two readers would have the second overwrite the first's target mid-call, and two writers would copy through the same write buffer at once. It is safe today only because the socket server happens to run one goroutine per side. A p2p hub runs one per peer, so it has to reach the same guarantee a different way: run() is the only goroutine that reads, and wmu is the only way to write. This is the socket server's shape arrived at from the other side — not a bridge per stream. Which goroutine read a packet carries no meaning, because the route is what says where it goes. Two locks, for two different hazards. The device's wmu guards the shared device buffer. peerStream's own mu guards one peer's stream, whose conn frames every write onto a byte stream and serializes only its read buffer: two writers would put one frame's length prefix against the other's payload, and every frame length would then be read from the wrong offset. The -race run is what proves both are doing their job. withdraw removes a stream only while it still holds its key. A peer that reconnected already installed a replacement under that name, so reclaiming routes for it on the old stream's teardown would cut off the live connection — which is what makes a flapping peer unreachable rather than merely briefly absent. The keepalive is answered on the stream it arrived on, because the table registers but never writes. Drop the echo and every keepalive:true spoke misses the reply its read deadline is refreshed from and redials on its own timeout, with nothing in any log: client.go's registration gate tests for "udp" and a p2p link is "ip", so a spoke that never registers is already indistinguishable from silence. Hence the warning naming the destination. The concurrency tests are mutation-checked: with the device lock removed the first fails on overlapping writes in round 0, and with the stream lock removed the second fails on a spliced frame. Both were caught by the assertions, not only by the race detector — the first harness was a datagram pipe, which is atomic per message and so passed with the lock gone until it was replaced by a byte stream, which is what the p2p conn really is.