Skip to content

Relay: a stream leg accepted as the connect wait ends is used, not left open - #166

Merged
undecuple merged 2 commits into
mainfrom
fork-relay-accept-race
Nov 13, 2026
Merged

undecuple merged 2 commits into
mainfrom
fork-relay-accept-race

Conversation

@undecuple

Copy link
Copy Markdown
Owner

Why

A relay stream has two legs. The connecting side waits up to 10 seconds for the target to accept. The accepting side takes the stream out of the server's table, answers its player "ready", and then hands over its connection. If the connecting side's wait ended between those steps, the accepting player had been told the stream was ready, the connecting player was told it was not found, and the accepted connection stayed in the stream's channel with nothing to close it.

Change

  • server/relay.go: the wait moves into awaitAccept(st, d). When the wait ends (timeout or server stop) it checks, under the server lock, whether the stream is still in the table. If it is, it is removed and there is no leg. If it is not, the accepting side has claimed it, and awaitAccept waits for its leg and returns it, so the stream is used as normal. The accepting side now always sends a value on the channel: its connection, or nil if the "ready" write failed. A stop while waiting still ends the connection silently, as before.
  • server/relay_accept_test.go: three tests of awaitAccept (a leg claimed as the wait ends is returned; a claimed stream whose accepting side failed gives nil; an unclaimed stream times out and is removed from the table).
  • CHANGELOG Server: entry; the generated site files are regenerated.

Tests

  • go vet and go test -race ./server pass.
  • Mutation: making awaitAccept return nil for a claimed stream fails TestAwaitAcceptUsesLegClaimedAtTimeout.
  • python3 tools/features.py, python3 tools/site.py, python3 tools/repo_check.py and scripts/check-deploy.sh pass.

Evidence

Before the change, a throwaway test (not in this PR) with the wait shortened to 4 ms and accept times spread around it: in 12,000 attempts, 3 had the accepting side told ready and the connecting side not, and the process's open descriptor count rose by 3. At the real 10 second wait the window is the same few microseconds, so it is rare. The new tests do not depend on timing.

Not in this change

The wait length is unchanged. No other relay behaviour changes.

Overlap

None open on server/relay.go. Touches CHANGELOG.md, site/index.html and site/data.json, which other open PRs also change; I will update this branch if one merges first.

Developed with AI agent assistance.

@undecuple
undecuple merged commit 92ef60f into main Nov 13, 2026
10 checks passed
@undecuple
undecuple deleted the fork-relay-accept-race branch November 13, 2026 04:10
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