Data race (-race only): concurrent send/close on p.send — sendCritical vs readPump close(p.send) #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
go test -raceflags a data race on a player'ssendchannel:sendCritical's async send (ch <- msg,server/lobby.go:304) racesreadPump'sclose(p.send)(server/main.go:92).Severity — NOT deploy-blocking
-race-ONLY.sendCriticalwraps the send inrecover(), so a send-on-closed-channel is defined behavior (panic → recovered) under plaingo test. The race detector flags the concurrent send/close regardless, but it does not fail the suite without-race.cd server && go test ./...(.forgejo/workflows/test.yml) — no-race— so this does not red CI or block deploy.-count … -race(de-flake @05d9c7fdropped the suite 25s → 0.11s).Root cause
p.sendhas no single owner:readPumpcloses it on disconnect, whilesendCritical(and the run-loop broadcast) may still be sending.recover()masks the panic but the concurrent send/close is a genuine data race.Fix direction (deliberate, post-sprint)
Single-closer lifecycle. Options:
close(p.send)fromreadPump. Signal teardown via a separatedonechannel; letwritePump(the sole sender's drain point) own the channel close after sends stop.closedflag;sendCriticalchecks it under the lock.(a) is the cleaner refactor.
Repro
Filed per Herald's call during the 2026-06-20 CELLBLOCK polish sprint: real latent concurrency issue worth not losing, but recover-handled +
-race-only → correctly out of scope for the sprint (a single-closer refactor, not sprint-pressure work).