Goroutine Visualizer
You cannot kill a goroutine. This page lets you try.
Every goroutine on this page is a real goroutine in this web server, and every channel is a real Go channel. Nothing here is a model of a scheduler — the blocking you see is the runtime parking a goroutine, and the states beside each one are worked out from a stream of events those operations emit as they happen. Build a pipeline, fill the buffer, and then try to stop something that is parked on a send nobody is listening to.
Nothing on this page touches a chain.
Where the work happens
The topology runs on the server, in Go, and its state reaches this page over a server-sent event stream ten times a second. Your commands go back the other way as a POST. A session starts real goroutines on a real box and holds them, so the server admits 4 topologies at a time, one per address, gives each a budget of 64 goroutines, and stops every session after 90 seconds.
Channel capacity runs from 0 to 32, and both ends are applied in the runtime package, not in the browser. The controls below carry the same numbers so they cannot ask for something the server would refuse, but the server's copy is the rule.
Leave this off and the goroutine performs a bare channel operation. That is the one that cannot be stopped.
Stalling stops the consumers receiving. The buffer then fills, and whatever is producing parks on its send.
Connecting to the server…
All goroutines in this session are asleep. Every one of them is parked on a channel operation and no timer is outstanding, so nothing here can make progress on its own. This is what Go reports as a deadlock when it happens to a whole program.
Goroutines
Nothing running yet. Spawn a producer and a consumer, or press Send one.
Event stream
Every state above is worked out from these events and from nothing else. No goroutine reports its own state, because a goroutine that is blocked is not running and cannot report anything — which is exactly why the state has to be inferred from what it was last seen attempting.
What to try
- Change nothing. A producer and a consumer start with the page. The producer sends every 400 ms and the consumer receives every 1200 ms, so the buffer fills within a couple of seconds and the producer parks. Its badge reads
blocked_send. - Press Stall the consumers, then cancel the parked producer. Nothing happens. The leak counter goes up. That goroutine is inside
ch <- v, and a canceled context is not something that expression can observe — there is no arm for it to take. - Now spawn a producer with the select arm ticked, and cancel that one. It is gone before you finish reading this sentence. Same topology, same parked operation, one extra
case <-ctx.Done(). - Leave the consumers stalled with the producer parked. Every goroutine is on a channel operation and no timer is outstanding, so the deadlock banner appears. Resume the consumers and it clears.
- Set the capacity to 0 and press Send one with the arm unticked. A capacity-zero channel is a rendezvous, so that goroutine parks on the spot with nobody to hand its value to. Then try to rebuild the channel: the server refuses, because a channel cannot be resized and a new one would leave that goroutine standing at the old one forever.
What happens to the leaked ones
In a program of your own, a goroutine parked on a bare channel operation stays there for the life of the process. That is the whole problem, and it is why the counter above is worth watching.
This server cannot afford that, so it is worth saying plainly what it does instead. It carries at most 192 leaked goroutines at a time. Once your session has ended and 2 minutes have passed, a reaper frees the ones you left behind — and the only way it can is by doing the thing that was missing: it receives. A receive arriving is what unblocks a parked send. Cancelation never becomes possible; the counterparty simply turns up at last, the send completes, and the goroutine's own loop reaches its next context check and returns.
Nothing is reclaimed while your session is running, or for those minutes afterwards, so the counter you are looking at is the literal truth for as long as you are looking at it.
Not built yet
This is the working vertical: a producer, one instrumented channel, and a consumer, with capacity control, the cancellation contrast, the leak counter and deadlock detection. Worker pools, select over several channels, tickers and timeouts are planned and are not here. A downloadable runtime trace is planned too, and is deliberately last — a trace file is only worth shipping once the topology is rich enough to make one interesting.