select: Compiler and selectgo
select: Compiler and selectgo
Overview
select looks like switch but is a compiler + runtime duet. Simple shapes are rewritten; hard cases call runtime.selectgo.
Depth companion: Internals for Interns — The select Statement.
Diagram: two paths
select { ... }
│
├── one case / simple ──► compiler rewrite
│
└── general ──► runtime.selectgo
│
└── park until one case wins
Rules that matter
| Rule | Meaning |
|---|---|
| Ready cases | If several ready, uniform random pick |
| All blocked | Wait until one ready (unless default) |
default |
Non-blocking poll |
| Nil channel case | Never selected (disable pattern) |
Pseudo-algorithm (teaching)
1. randomize case order
2. lock involved channels carefully (runtime does ordered locking)
3. probe for immediately ready op
4. if none and default → default
5. else enqueue this G on all wait queues; park
6. on wake: dequeue others; complete winner only
Synctest note
Inside a synctest bubble, select is durable only if every non-nil case is bubbled (223).
Experiment
go run .package main
import "fmt"
func main() {
a, b := make(chan int, 1), make(chan int, 1)
counts := map[string]int{}
for i := 0; i < 1000; i++ {
a <- 1
b <- 1
select {
case <-a:
counts["a"]++
case <-b:
counts["b"]++
}
// drain the other so next iter both ready
select {
case <-a:
default:
}
select {
case <-b:
default:
}
}
fmt.Println(counts)
}What to notice: Both cases win a substantial share—not left-to-right priority.
Try next: go build -gcflags=-S on a one-case select and a multi-case select; compare call sites.