Day 062 — Generators & done channels¶
Month 3 · Week 1 · ⬅ Day 061 · Day 063 ➡ · Journal index
🎯 Learning Objective¶
Build generators that return receive-only channels, compose them into pipelines, and use a done channel so producers shut down cleanly and never leak.
📚 Topics¶
- The generator pattern: a function returning
<-chan Tbacked by a goroutine - Pipeline stages (Gen → Map → Filter → Collect) and fan-in (
merge) - The
done-channel cancellation idiom (the precursor tocontext)
📖 Reading / Sources¶
- Go blog — Go Concurrency Patterns: Pipelines and cancellation
- Rob Pike — Go Concurrency Patterns (talk)
- Effective Go — Channels
📝 Notes¶
- A generator is a function that creates a channel, launches a goroutine to fill it, and returns the channel receive-only (
<-chan T). The caller justranges over it — the goroutine is an implementation detail → [[generator]]. - A pipeline chains stages, each taking an input channel and returning an output channel it owns and closes. Because a single channel is FIFO, a linear pipeline preserves order → [[pipeline]].
- Fan-in /
mergecombines N channels into one: one forwarding goroutine per input, plus async.WaitGroupcloser goroutine that closes the output exactly once after all inputs drain. Order across inputs is not guaranteed → [[fan-in]]. - The leak problem: if the consumer stops reading early, a generator blocked on
out <- vstays parked forever → [[goroutine-leak]]. Fix: pass adone <-chan struct{}andselectbetweenout <- vand<-done; closingdoneunblocks every parked send → [[done-channel]]. - Closing
doneis a broadcast: every goroutine selecting on it observes the close at once — the clean way to cancel a whole fan-out.defer close(done)in the consumer guarantees cleanup on every exit path. - This
done-channel idiom is exactly whatcontext.Contextgeneralises (cancellation + deadlines + values), which is the next big topic this month.
💻 Code Examples¶
func gen(done <-chan struct{}, nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
select {
case out <- n:
case <-done: // consumer quit — abandon and clean up, no leak
return
}
}
}()
return out
}
Full code:
examples/month-03/generator/main.go· Run:go run ./examples/month-03/generator
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| Channel pipeline: Gen → Map → Filter → Collect | ✅ | exercises/month-03/week-1/pipeline/ |
Fan-in Merge that closes the output exactly once |
✅ | exercises/month-03/week-1/fanin/ |
🐛 Mistakes Made¶
- Wrote a generator with no cancellation; an early
breakin the consumer leaked its goroutine. Added adonechannel and aselect. - Closed the
mergeoutput from inside each forwarder → double-close panic. Moved the close to a single closer goroutine afterwg.Wait().
❓ Open Questions¶
- When does
contextfully replace the rawdonechannel? (Almost always in real code —doneis the teaching version;contextadds deadlines, values, and a standard API.)
🧠 Active Recall (answer without looking)¶
-
Q: Why return
<-chan T(receive-only) from a generator instead ofchan T?
A
It enforces that callers only receive — they can't accidentally send to or close a channel the generator owns. The compiler guards the contract. -
Q: How does a
donechannel stop a generator that's blocked on a send?
A
The generator `select`s between `out <- v` and `<-done`. Closing `done` makes its receive ready immediately, so the parked send is abandoned and the goroutine returns instead of leaking.
🪶 Feynman Reflection¶
A generator is a vending machine that quietly restocks itself: you keep pressing "next" and items appear, never seeing the worker in back. A done channel is the master "closing time" switch — flip it (close it) and every worker, no matter what they're mid-doing, hears it at once and goes home, so nobody is left waiting at a locked counter.
🕳️ Knowledge Gaps¶
- Bounded-parallelism pipelines (fan-out then fan-in with N workers per stage) — practise with the worker pool plus merge.
✅ Summary¶
I can write order-preserving pipeline stages, fan several channels into one with a close-once merge, and prevent goroutine leaks with a broadcast done channel — the mental model context builds on.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 063: week review + active recall across goroutines, channels, select, and patterns.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦🟦⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): generators, done channels and fan-in (day 062)