Go Concepts
Advanced Go from the runtime scheduler and garbage collector to concurrency, generics and production, measured on Go 1.27 and sourced from the official docs, books and papers, one concept at a time.
Scheduler & GC
The Goroutine Scheduler: G, M and P
The Go scheduler from the header of runtime/proc.go and Vyukov's design doc, checked with Go 1.27 programs: G, M and P; the runnext slot running the newest goroutine first (and -race shuffling it); local and global run queues and work stealing in GODEBUG=schedtrace; asynchronous preemption by SIGURG against asyncpreemptoff=1 (a 1 ms sleep becoming 302 ms, a GC stop waiting for the loop); the P handed off 32 to 54 ยตs after a blocking read(2); 100 blocking reads costing 100 threads while the netpoller costs none; and the /sched/* metrics, including the name from the 1.26 release notes that does not exist.
GOMAXPROCS in Containers
Go 1.25's container-aware GOMAXPROCS, separating what was measured on macOS with Go 1.27 from what comes from the docs and the runtime source: the rule (minimum of CPUs, affinity and the rounded-up cgroup limit, floor of 2, request ignored), the cpu.max and cfs quota files read only from the leaf cgroup, periodic updates by sysmon and the GOMAXPROCS updater goroutine, overrides and SetDefaultGOMAXPROCS with their stop-the-world pauses, the go line in go.mod turning it all off (and a godebug block turning it back on), a godebug counter that never moves, and an open-loop CPU-bound load showing capped throughput and seconds of latency below the needed parallelism, with throttling left to the documentation.
The Go Garbage Collector: GOGC and GOMEMLIMIT
Go's concurrent, non-moving tricolor mark-sweep collector from the hybrid write barrier proposal and the ISMM 2018 keynote; a GODEBUG=gctrace=1 line read field by field; one fixed workload measured in Go 1.27 under GOGC 50, 100, 200 and off, where each doubling halves cycles and GC CPU and doubles memory headroom; GOMEMLIMIT as a soft ceiling with GOGC=off (11 cycles instead of 62 to 64) and the suffixes it rejects; the death spiral with the live heap near and above the limit (30 times the GC CPU, memory passing the limit, a 6x slowdown the 50% CPU limiter does not prevent); and the knobs in runtime/metrics and runtime/debug.
Green Tea GC
Green Tea, the default mark algorithm since Go 1.26, which queues spans instead of objects and scans the marked objects of each span in one pass; the two bit sets per span and the 16 to 512 byte window read from the runtime source; the GOEXPERIMENT=nogreenteagc opt-out, which still builds in Go 1.27.0 despite the 1.26 notes; GC CPU per cycle measured on arm64 with and without Green Tea on three heap shapes (50 to 58% less on a graph of small objects, a tie on one long chain, 26 to 38% more on 2 KiB objects) and on an allocation-driven loop (about 50% less); and the comparison with the 10 to 40% in the release notes.
Concurrency
The Go Memory Model and sync/atomic
The Go memory model as revised in 2022, with happens-before and the synchronized-before edges of channels, mutexes, sync.Once, WaitGroup and atomics; DRF-SC; store buffering and message passing litmus tests showing real reordering on arm64; the compiler deleting writes from a racy loop; what a racy program still guarantees (word-sized values, no out-of-thin-air) and what it does not (a torn interface up to SIGSEGV); sequentially consistent typed atomics (atomic.Int64, atomic.Pointer[T]); vet warnings; and broken double-checked init against sync.OnceValue and atomic.Pointer, with real -race reports.
Advanced Concurrency Patterns in Go
Pipelines with context cancellation (adapted from the Go blog's pipelines article), fan-out and fan-in and the order they lose, bounded parallelism with errgroup.SetLimit against a semaphore and a buffered channel measuring peak tasks and goroutines, weighted semaphores and their FIFO queue, fail-fast with errgroup.WithContext and context.Cause, singleflight with 100 callers and one execution, the first caller's context leaking to all of them and context.WithoutCancel, and sync.WaitGroup.Go (Go 1.25) with the vet waitgroup analyzer and go fix.
Context: Cancellation Trees, Deadlines and Causes
The context tree and synchronous cancellation measured up to 100,000 children, the goroutine per child under a custom Context, DeadlineExceeded versus Canceled, WithCancelCause, WithTimeoutCause and context.Cause, AfterFunc and its stop function unblocking a net.Conn read, WithoutCancel for work after the response, key collisions in WithValue and the linear cost of lookup, go vet lostcancel messages and the heap a forgotten cancel holds, what the server sees when an HTTP client deadline expires, and why a context should not live inside a struct.
Concurrency Bugs: Races and Deadlocks in Real Go Code
The blocking versus non-blocking and shared memory versus message passing taxonomy of 171 real bugs (ASPLOS 2019) and the causes of 1,011 races fixed at Uber (PLDI 2022), reproduced with real -race reports, runtime fatal errors and go vet messages: loop variable capture with go 1.21 versus go 1.22, concurrent map writes, append to a slice without a lock, WaitGroup.Add inside the goroutine and WaitGroup.Go, a send nobody receives and the leak the runtime never reports, a mutex copied by value, the measured cost of the race detector, and GCatch and GFix as static detection.
Goroutine Leak Detection: the goroutineleak Profile and goleak
The goroutineleak profile, generally available in Go 1.27 in runtime/pprof and at /debug/pprof/goroutineleak, how the GC proves by reachability that a blocked goroutine can never wake up (Golf, ASPLOS 2025), the blind spots measured with primitives reachable from globals, from sleeping goroutines and from a closure-captured variable, a comparison with the goroutine profile and with go.uber.org/goleak in tests, and the measured cost of one detection cycle, including the chained worst case.
Deterministic Tests with testing/synctest
testing/synctest bubbles (generally available in Go 1.25) with a fake clock that only advances when every goroutine is durably blocked, what counts as durably blocked and why mutexes and I/O freeze the clock, synctest.Wait as a synchronization point for the race detector, a retry, a deadline and 23 fake minutes of cache expiry measured in microseconds, the same-instant timer flake fixed by synctest.Sleep (Go 1.27), httptest.NewTestServer (Go 1.27) with in-memory networking, and the panics and fatal errors measured when each rule is broken.