Stopping the daemon while the feed is flowing can panic with "send on closed channel" #34

Closed
opened 2026-09-28 20:20:43 +02:00 by clawbot · 2 comments
Collaborator

Found while checking #33. Stopping the daemon while the RIS Live feed is flowing sometimes ends in panic: send on closed channel at internal/streamer/streamer.go:680, after the handlers have flushed but before Final metrics is logged. The process exits 2 and the rest of the shutdown is skipped.

Cause: Streamer.Stop cancels the stream and closes every handler queue while holding the streamer's lock. The read loop checks for cancellation only once per line, before it parses the line and hands the message to the handler queues. A line that passed that check just before Stop waits for the lock, then sends to a queue that is already closed. It is a race, so it happens on some stops and not others; it does not depend on how the entrypoint starts the daemon.

Definition of done

  • Stopping the daemon while the feed is flowing never panics: the log reaches Final metrics and the process exits 0.
  • A test covers a stop that races with a message being handed to the handler queues.
  • make check stays green (in the Docker build).

Model: opus-5-5

Found while checking https://git.eeqj.de/sneak/routewatch/issues/33. Stopping the daemon while the RIS Live feed is flowing sometimes ends in `panic: send on closed channel` at `internal/streamer/streamer.go:680`, after the handlers have flushed but before `Final metrics` is logged. The process exits 2 and the rest of the shutdown is skipped. Cause: `Streamer.Stop` cancels the stream and closes every handler queue while holding the streamer's lock. The read loop checks for cancellation only once per line, before it parses the line and hands the message to the handler queues. A line that passed that check just before `Stop` waits for the lock, then sends to a queue that is already closed. It is a race, so it happens on some stops and not others; it does not depend on how the entrypoint starts the daemon. ## Definition of done - Stopping the daemon while the feed is flowing never panics: the log reaches `Final metrics` and the process exits 0. - A test covers a stop that races with a message being handed to the handler queues. - `make check` stays green (in the Docker build). Model: opus-5-5
Author
Collaborator

Implementer's notes, on top of the definition of done above:

  • Stop cancels the stream and closes the handler queues under the streamer's write lock (internal/streamer/streamer.go, Stop), and the read loop sends to the queues under the read lock. So a check made under that same read lock, just before the sends, sees either a streamer that is still running or one that has stopped, never half of each. Returning there is enough; no new lock, channel or flag is needed.
  • Stop's comment says it is safe to call more than once, but a second call closes the queues again, which also panics. Make that true in the same change, since the definition of done is that stopping never panics.
  • The test drives Stop while messages are being handed to the queues and passes under -race; it must not touch the network.

Model: opus-5-5

Implementer's notes, on top of the definition of done above: - `Stop` cancels the stream and closes the handler queues under the streamer's write lock (`internal/streamer/streamer.go`, `Stop`), and the read loop sends to the queues under the read lock. So a check made under that same read lock, just before the sends, sees either a streamer that is still running or one that has stopped, never half of each. Returning there is enough; no new lock, channel or flag is needed. - `Stop`'s comment says it is safe to call more than once, but a second call closes the queues again, which also panics. Make that true in the same change, since the definition of done is that stopping never panics. - The test drives `Stop` while messages are being handed to the queues and passes under `-race`; it must not touch the network. Model: opus-5-5
clawbot self-assigned this 2026-09-28 21:09:04 +02:00
Author
Collaborator

PR: #36

Model: opus-5-5

PR: https://git.eeqj.de/sneak/routewatch/pulls/36 Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/routewatch#34