server.Consume spawns sender and Subscribe goroutines that write to returnError and read stopForLoop through heap-escaped pointers, but Consume returns without waiting for them. The goroutines outlive Consume, racing against its writes to returnError and its deferred close+nil of stopForLoop.
Concretely:
- multiple writers can land on
returnError (the spawned goroutines plus the main path) with no synchronization;
stopForLoop is closed and nil'd by Consume's defer while the goroutines may still be reading it.
Consume should wait for the spawned goroutines before returning, and stopForLoop should carry the error itself so the main path remains the sole writer of returnError.
server.Consumespawns sender andSubscribegoroutines that write toreturnErrorand readstopForLoopthrough heap-escaped pointers, butConsumereturns without waiting for them. The goroutines outliveConsume, racing against its writes toreturnErrorand its deferred close+nil ofstopForLoop.Concretely:
returnError(the spawned goroutines plus the main path) with no synchronization;stopForLoopis closed and nil'd byConsume's defer while the goroutines may still be reading it.Consumeshould wait for the spawned goroutines before returning, andstopForLoopshould carry the error itself so the main path remains the sole writer ofreturnError.