select: Compiler and selectgo

Updated

September 8, 2026

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.